blob: 4c76f2a56beaa32e30e709b934eac9cd151f4206 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <utility>
#include "base/run_loop.h"
#include "mojo/core/embedder/embedder.h"
#include "mojo/public/cpp/bindings/associated_binding_set.h"
#include "mojo/public/cpp/bindings/binding_set.h"
#include "mojo/public/cpp/bindings/strong_binding_set.h"
#include "mojo/public/cpp/bindings/tests/bindings_test_base.h"
#include "mojo/public/interfaces/bindings/tests/ping_service.mojom.h"
#include "mojo/public/interfaces/bindings/tests/test_associated_interfaces.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace mojo {
namespace test {
namespace {
using BindingSetTest = BindingsTestBase;
template <typename BindingSetType, typename ContextType>
void ExpectContextHelper(BindingSetType* binding_set,
ContextType expected_context) {
EXPECT_EQ(expected_context, binding_set->dispatch_context());
}
template <typename BindingSetType, typename ContextType>
base::Closure ExpectContext(BindingSetType* binding_set,
ContextType expected_context) {
return base::Bind(
&ExpectContextHelper<BindingSetType, ContextType>, binding_set,
expected_context);
}
template <typename BindingSetType>
void ExpectBindingIdHelper(BindingSetType* binding_set,
BindingId expected_binding_id) {
EXPECT_EQ(expected_binding_id, binding_set->dispatch_binding());
}
template <typename BindingSetType>
base::Closure ExpectBindingId(BindingSetType* binding_set,
BindingId expected_binding_id) {
return base::Bind(&ExpectBindingIdHelper<BindingSetType>, binding_set,
expected_binding_id);
}
template <typename BindingSetType>
void ReportBadMessageHelper(BindingSetType* binding_set,
const std::string& error) {
binding_set->ReportBadMessage(error);
}
template <typename BindingSetType>
base::Closure ReportBadMessage(BindingSetType* binding_set,
const std::string& error) {
return base::Bind(&ReportBadMessageHelper<BindingSetType>, binding_set,
error);
}
template <typename BindingSetType>
void SaveBadMessageCallbackHelper(BindingSetType* binding_set,
ReportBadMessageCallback* callback) {
*callback = binding_set->GetBadMessageCallback();
}
template <typename BindingSetType>
base::Closure SaveBadMessageCallback(BindingSetType* binding_set,
ReportBadMessageCallback* callback) {
return base::Bind(&SaveBadMessageCallbackHelper<BindingSetType>, binding_set,
callback);
}
base::Closure Sequence(const base::Closure& first,
const base::Closure& second) {
return base::Bind(
[] (const base::Closure& first, const base::Closure& second) {
first.Run();
second.Run();
}, first, second);
}
class PingImpl : public PingService {
public:
PingImpl() {}
~PingImpl() override {}
void set_ping_handler(const base::RepeatingClosure& handler) {
ping_handler_ = handler;
}
private:
// PingService:
void Ping(PingCallback callback) override {
if (!ping_handler_.is_null())
ping_handler_.Run();
std::move(callback).Run();
}
base::RepeatingClosure ping_handler_;
};
TEST_P(BindingSetTest, BindingSetContext) {
PingImpl impl;
BindingSet<PingService, int> bindings;
PingServicePtr ping_a, ping_b;
bindings.AddBinding(&impl, MakeRequest(&ping_a), 1);
bindings.AddBinding(&impl, MakeRequest(&ping_b), 2);
{
impl.set_ping_handler(ExpectContext(&bindings, 1));
base::RunLoop loop;
ping_a->Ping(loop.QuitClosure());
loop.Run();
}
{
impl.set_ping_handler(ExpectContext(&bindings, 2));
base::RunLoop loop;
ping_b->Ping(loop.QuitClosure());
loop.Run();
}
{
base::RunLoop loop;
bindings.set_connection_error_handler(
Sequence(ExpectContext(&bindings, 1), loop.QuitClosure()));
ping_a.reset();
loop.Run();
}
{
base::RunLoop loop;
bindings.set_connection_error_handler(
Sequence(ExpectContext(&bindings, 2), loop.QuitClosure()));
ping_b.reset();
loop.Run();
}
EXPECT_TRUE(bindings.empty());
}
TEST_P(BindingSetTest, BindingSetDispatchBinding) {
PingImpl impl;
BindingSet<PingService, int> bindings;
PingServicePtr ping_a, ping_b;
BindingId id_a = bindings.AddBinding(&impl, MakeRequest(&ping_a), 1);
BindingId id_b = bindings.AddBinding(&impl, MakeRequest(&ping_b), 2);
{
impl.set_ping_handler(ExpectBindingId(&bindings, id_a));
base::RunLoop loop;
ping_a->Ping(loop.QuitClosure());
loop.Run();
}
{
impl.set_ping_handler(ExpectBindingId(&bindings, id_b));
base::RunLoop loop;
ping_b->Ping(loop.QuitClosure());
loop.Run();
}
{
base::RunLoop loop;
bindings.set_connection_error_handler(
Sequence(ExpectBindingId(&bindings, id_a), loop.QuitClosure()));
ping_a.reset();
loop.Run();
}
{
base::RunLoop loop;
bindings.set_connection_error_handler(
Sequence(ExpectBindingId(&bindings, id_b), loop.QuitClosure()));
ping_b.reset();
loop.Run();
}
EXPECT_TRUE(bindings.empty());
}
TEST_P(BindingSetTest, BindingSetConnectionErrorWithReason) {
PingImpl impl;
PingServicePtr ptr;
BindingSet<PingService> bindings;
bindings.AddBinding(&impl, MakeRequest(&ptr));
base::RunLoop run_loop;
bindings.set_connection_error_with_reason_handler(base::Bind(
[](const base::Closure& quit_closure, uint32_t custom_reason,
const std::string& description) {
EXPECT_EQ(1024u, custom_reason);
EXPECT_EQ("bye", description);
quit_closure.Run();
},
run_loop.QuitClosure()));
ptr.ResetWithReason(1024u, "bye");
}
TEST_P(BindingSetTest, BindingSetReportBadMessage) {
PingImpl impl;
std::string last_received_error;
core::SetDefaultProcessErrorCallback(
base::Bind([](std::string* out_error,
const std::string& error) { *out_error = error; },
&last_received_error));
BindingSet<PingService, int> bindings;
PingServicePtr ping_a, ping_b;
bindings.AddBinding(&impl, MakeRequest(&ping_a), 1);
bindings.AddBinding(&impl, MakeRequest(&ping_b), 2);
{
impl.set_ping_handler(ReportBadMessage(&bindings, "message 1"));
base::RunLoop loop;
ping_a.set_connection_error_handler(loop.QuitClosure());
ping_a->Ping(base::Bind([] {}));
loop.Run();
EXPECT_EQ("message 1", last_received_error);
}
{
impl.set_ping_handler(ReportBadMessage(&bindings, "message 2"));
base::RunLoop loop;
ping_b.set_connection_error_handler(loop.QuitClosure());
ping_b->Ping(base::Bind([] {}));
loop.Run();
EXPECT_EQ("message 2", last_received_error);
}
EXPECT_TRUE(bindings.empty());
core::SetDefaultProcessErrorCallback(mojo::core::ProcessErrorCallback());
}
TEST_P(BindingSetTest, BindingSetGetBadMessageCallback) {
PingImpl impl;
std::string last_received_error;
core::SetDefaultProcessErrorCallback(
base::Bind([](std::string* out_error,
const std::string& error) { *out_error = error; },
&last_received_error));
BindingSet<PingService, int> bindings;
PingServicePtr ping_a, ping_b;
bindings.AddBinding(&impl, MakeRequest(&ping_a), 1);
bindings.AddBinding(&impl, MakeRequest(&ping_b), 2);
ReportBadMessageCallback bad_message_callback_a;
ReportBadMessageCallback bad_message_callback_b;
{
impl.set_ping_handler(
SaveBadMessageCallback(&bindings, &bad_message_callback_a));
base::RunLoop loop;
ping_a->Ping(loop.QuitClosure());
loop.Run();
ping_a.reset();
}
{
impl.set_ping_handler(
SaveBadMessageCallback(&bindings, &bad_message_callback_b));
base::RunLoop loop;
ping_b->Ping(loop.QuitClosure());
loop.Run();
}
std::move(bad_message_callback_a).Run("message 1");
EXPECT_EQ("message 1", last_received_error);
{
base::RunLoop loop;
ping_b.set_connection_error_handler(loop.QuitClosure());
std::move(bad_message_callback_b).Run("message 2");
EXPECT_EQ("message 2", last_received_error);
loop.Run();
}
EXPECT_TRUE(bindings.empty());
core::SetDefaultProcessErrorCallback(mojo::core::ProcessErrorCallback());
}
TEST_P(BindingSetTest, BindingSetGetBadMessageCallbackOutlivesBindingSet) {
PingImpl impl;
std::string last_received_error;
core::SetDefaultProcessErrorCallback(
base::Bind([](std::string* out_error,
const std::string& error) { *out_error = error; },
&last_received_error));
ReportBadMessageCallback bad_message_callback;
{
BindingSet<PingService, int> bindings;
PingServicePtr ping_a;
bindings.AddBinding(&impl, MakeRequest(&ping_a), 1);
impl.set_ping_handler(
SaveBadMessageCallback(&bindings, &bad_message_callback));
base::RunLoop loop;
ping_a->Ping(loop.QuitClosure());
loop.Run();
}
std::move(bad_message_callback).Run("message 1");
EXPECT_EQ("message 1", last_received_error);
core::SetDefaultProcessErrorCallback(mojo::core::ProcessErrorCallback());
}
class PingProviderImpl : public AssociatedPingProvider, public PingService {
public:
PingProviderImpl() {}
~PingProviderImpl() override {}
void set_new_ping_context(int context) { new_ping_context_ = context; }
void set_new_ping_handler(const base::Closure& handler) {
new_ping_handler_ = handler;
}
void set_ping_handler(const base::Closure& handler) {
ping_handler_ = handler;
}
AssociatedBindingSet<PingService, int>& ping_bindings() {
return ping_bindings_;
}
private:
// AssociatedPingProvider:
void GetPing(PingServiceAssociatedRequest request) override {
ping_bindings_.AddBinding(this, std::move(request), new_ping_context_);
if (!new_ping_handler_.is_null())
new_ping_handler_.Run();
}
// PingService:
void Ping(PingCallback callback) override {
if (!ping_handler_.is_null())
ping_handler_.Run();
std::move(callback).Run();
}
AssociatedBindingSet<PingService, int> ping_bindings_;
int new_ping_context_ = -1;
base::Closure ping_handler_;
base::Closure new_ping_handler_;
};
TEST_P(BindingSetTest, AssociatedBindingSetContext) {
AssociatedPingProviderPtr provider;
PingProviderImpl impl;
Binding<AssociatedPingProvider> binding(&impl, MakeRequest(&provider));
PingServiceAssociatedPtr ping_a;
{
base::RunLoop loop;
impl.set_new_ping_context(1);
impl.set_new_ping_handler(loop.QuitClosure());
provider->GetPing(MakeRequest(&ping_a));
loop.Run();
}
PingServiceAssociatedPtr ping_b;
{
base::RunLoop loop;
impl.set_new_ping_context(2);
impl.set_new_ping_handler(loop.QuitClosure());
provider->GetPing(MakeRequest(&ping_b));
loop.Run();
}
{
impl.set_ping_handler(ExpectContext(&impl.ping_bindings(), 1));
base::RunLoop loop;
ping_a->Ping(loop.QuitClosure());
loop.Run();
}
{
impl.set_ping_handler(ExpectContext(&impl.ping_bindings(), 2));
base::RunLoop loop;
ping_b->Ping(loop.QuitClosure());
loop.Run();
}
{
base::RunLoop loop;
impl.ping_bindings().set_connection_error_handler(
Sequence(ExpectContext(&impl.ping_bindings(), 1), loop.QuitClosure()));
ping_a.reset();
loop.Run();
}
{
base::RunLoop loop;
impl.ping_bindings().set_connection_error_handler(
Sequence(ExpectContext(&impl.ping_bindings(), 2), loop.QuitClosure()));
ping_b.reset();
loop.Run();
}
EXPECT_TRUE(impl.ping_bindings().empty());
}
TEST_P(BindingSetTest, MasterInterfaceBindingSetContext) {
AssociatedPingProviderPtr provider_a, provider_b;
PingProviderImpl impl;
BindingSet<AssociatedPingProvider, int> bindings;
bindings.AddBinding(&impl, MakeRequest(&provider_a), 1);
bindings.AddBinding(&impl, MakeRequest(&provider_b), 2);
{
PingServiceAssociatedPtr ping;
base::RunLoop loop;
impl.set_new_ping_handler(
Sequence(ExpectContext(&bindings, 1), loop.QuitClosure()));
provider_a->GetPing(MakeRequest(&ping));
loop.Run();
}
{
PingServiceAssociatedPtr ping;
base::RunLoop loop;
impl.set_new_ping_handler(
Sequence(ExpectContext(&bindings, 2), loop.QuitClosure()));
provider_b->GetPing(MakeRequest(&ping));
loop.Run();
}
{
base::RunLoop loop;
bindings.set_connection_error_handler(
Sequence(ExpectContext(&bindings, 1), loop.QuitClosure()));
provider_a.reset();
loop.Run();
}
{
base::RunLoop loop;
bindings.set_connection_error_handler(
Sequence(ExpectContext(&bindings, 2), loop.QuitClosure()));
provider_b.reset();
loop.Run();
}
EXPECT_TRUE(bindings.empty());
}
TEST_P(BindingSetTest, MasterInterfaceBindingSetDispatchBinding) {
AssociatedPingProviderPtr provider_a, provider_b;
PingProviderImpl impl;
BindingSet<AssociatedPingProvider, int> bindings;
BindingId id_a = bindings.AddBinding(&impl, MakeRequest(&provider_a), 1);
BindingId id_b = bindings.AddBinding(&impl, MakeRequest(&provider_b), 2);
{
PingServiceAssociatedPtr ping;
base::RunLoop loop;
impl.set_new_ping_handler(
Sequence(ExpectBindingId(&bindings, id_a), loop.QuitClosure()));
provider_a->GetPing(MakeRequest(&ping));
loop.Run();
}
{
PingServiceAssociatedPtr ping;
base::RunLoop loop;
impl.set_new_ping_handler(
Sequence(ExpectBindingId(&bindings, id_b), loop.QuitClosure()));
provider_b->GetPing(MakeRequest(&ping));
loop.Run();
}
{
base::RunLoop loop;
bindings.set_connection_error_handler(
Sequence(ExpectBindingId(&bindings, id_a), loop.QuitClosure()));
provider_a.reset();
loop.Run();
}
{
base::RunLoop loop;
bindings.set_connection_error_handler(
Sequence(ExpectBindingId(&bindings, id_b), loop.QuitClosure()));
provider_b.reset();
loop.Run();
}
EXPECT_TRUE(bindings.empty());
}
TEST_P(BindingSetTest, PreDispatchHandler) {
PingImpl impl;
BindingSet<PingService, int> bindings;
PingServicePtr ping_a, ping_b;
bindings.AddBinding(&impl, MakeRequest(&ping_a), 1);
bindings.AddBinding(&impl, MakeRequest(&ping_b), 2);
{
bindings.set_pre_dispatch_handler(base::Bind([] (const int& context) {
EXPECT_EQ(1, context);
}));
base::RunLoop loop;
ping_a->Ping(loop.QuitClosure());
loop.Run();
}
{
bindings.set_pre_dispatch_handler(base::Bind([] (const int& context) {
EXPECT_EQ(2, context);
}));
base::RunLoop loop;
ping_b->Ping(loop.QuitClosure());
loop.Run();
}
{
base::RunLoop loop;
bindings.set_pre_dispatch_handler(
base::Bind([](base::RunLoop* loop, const int& context) {
EXPECT_EQ(1, context);
loop->Quit();
}, &loop));
ping_a.reset();
loop.Run();
}
{
base::RunLoop loop;
bindings.set_pre_dispatch_handler(
base::Bind([](base::RunLoop* loop, const int& context) {
EXPECT_EQ(2, context);
loop->Quit();
}, &loop));
ping_b.reset();
loop.Run();
}
EXPECT_TRUE(bindings.empty());
}
TEST_P(BindingSetTest, AssociatedBindingSetConnectionErrorWithReason) {
AssociatedPingProviderPtr master_ptr;
PingProviderImpl master_impl;
Binding<AssociatedPingProvider> master_binding(&master_impl,
MakeRequest(&master_ptr));
base::RunLoop run_loop;
master_impl.ping_bindings().set_connection_error_with_reason_handler(
base::Bind(
[](const base::Closure& quit_closure, uint32_t custom_reason,
const std::string& description) {
EXPECT_EQ(2048u, custom_reason);
EXPECT_EQ("bye", description);
quit_closure.Run();
},
run_loop.QuitClosure()));
PingServiceAssociatedPtr ptr;
master_ptr->GetPing(MakeRequest(&ptr));
ptr.ResetWithReason(2048u, "bye");
run_loop.Run();
}
class PingInstanceCounter : public PingService {
public:
PingInstanceCounter() { ++instance_count; }
~PingInstanceCounter() override { --instance_count; }
void Ping(PingCallback callback) override {}
static int instance_count;
};
int PingInstanceCounter::instance_count = 0;
TEST_P(BindingSetTest, StrongBinding_Destructor) {
PingServicePtr ping_a, ping_b;
auto bindings = std::make_unique<StrongBindingSet<PingService>>();
bindings->AddBinding(std::make_unique<PingInstanceCounter>(),
mojo::MakeRequest(&ping_a));
EXPECT_EQ(1, PingInstanceCounter::instance_count);
bindings->AddBinding(std::make_unique<PingInstanceCounter>(),
mojo::MakeRequest(&ping_b));
EXPECT_EQ(2, PingInstanceCounter::instance_count);
bindings.reset();
EXPECT_EQ(0, PingInstanceCounter::instance_count);
}
TEST_P(BindingSetTest, StrongBinding_ConnectionError) {
PingServicePtr ping_a, ping_b;
StrongBindingSet<PingService> bindings;
bindings.AddBinding(std::make_unique<PingInstanceCounter>(),
mojo::MakeRequest(&ping_a));
bindings.AddBinding(std::make_unique<PingInstanceCounter>(),
mojo::MakeRequest(&ping_b));
EXPECT_EQ(2, PingInstanceCounter::instance_count);
ping_a.reset();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, PingInstanceCounter::instance_count);
ping_b.reset();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0, PingInstanceCounter::instance_count);
}
TEST_P(BindingSetTest, StrongBinding_RemoveBinding) {
PingServicePtr ping_a, ping_b;
StrongBindingSet<PingService> bindings;
BindingId binding_id_a = bindings.AddBinding(
std::make_unique<PingInstanceCounter>(), mojo::MakeRequest(&ping_a));
BindingId binding_id_b = bindings.AddBinding(
std::make_unique<PingInstanceCounter>(), mojo::MakeRequest(&ping_b));
EXPECT_EQ(2, PingInstanceCounter::instance_count);
EXPECT_TRUE(bindings.RemoveBinding(binding_id_a));
EXPECT_EQ(1, PingInstanceCounter::instance_count);
EXPECT_TRUE(bindings.RemoveBinding(binding_id_b));
EXPECT_EQ(0, PingInstanceCounter::instance_count);
}
INSTANTIATE_MOJO_BINDINGS_TEST_CASE_P(BindingSetTest);
} // namespace
} // namespace test
} // namespace mojo