blob: 07acfbebe00df09c52a2e5d2f11c7ea3468fae6a [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/message_loop/message_loop.h"
#include "base/run_loop.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/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 {
class BindingSetTest : public testing::Test {
public:
BindingSetTest() {}
~BindingSetTest() override {}
base::MessageLoop& loop() { return loop_; }
private:
base::MessageLoop loop_;
DISALLOW_COPY_AND_ASSIGN(BindingSetTest);
};
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);
}
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::Closure& handler) {
ping_handler_ = handler;
}
private:
// PingService:
void Ping(const PingCallback& callback) override {
if (!ping_handler_.is_null())
ping_handler_.Run();
callback.Run();
}
base::Closure ping_handler_;
};
TEST_F(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_F(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");
}
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(const PingCallback& callback) override {
if (!ping_handler_.is_null())
ping_handler_.Run();
callback.Run();
}
AssociatedBindingSet<PingService, int> ping_bindings_;
int new_ping_context_ = -1;
base::Closure ping_handler_;
base::Closure new_ping_handler_;
};
TEST_F(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_F(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_F(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_F(BindingSetTest, AssociatedBindingSetConnectionErrorWithReason) {
AssociatedPingProviderPtr master_ptr;
PingProviderImpl master_impl;
Binding<AssociatedPingProvider> master_binding(&master_impl, &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(const PingCallback& callback) override {}
static int instance_count;
};
int PingInstanceCounter::instance_count = 0;
TEST_F(BindingSetTest, StrongBinding_Destructor) {
PingServicePtr ping_a, ping_b;
auto bindings = base::MakeUnique<StrongBindingSet<PingService>>();
bindings->AddBinding(base::MakeUnique<PingInstanceCounter>(),
mojo::MakeRequest(&ping_a));
EXPECT_EQ(1, PingInstanceCounter::instance_count);
bindings->AddBinding(base::MakeUnique<PingInstanceCounter>(),
mojo::MakeRequest(&ping_b));
EXPECT_EQ(2, PingInstanceCounter::instance_count);
bindings.reset();
EXPECT_EQ(0, PingInstanceCounter::instance_count);
}
TEST_F(BindingSetTest, StrongBinding_ConnectionError) {
PingServicePtr ping_a, ping_b;
StrongBindingSet<PingService> bindings;
bindings.AddBinding(base::MakeUnique<PingInstanceCounter>(),
mojo::MakeRequest(&ping_a));
bindings.AddBinding(base::MakeUnique<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_F(BindingSetTest, StrongBinding_RemoveBinding) {
PingServicePtr ping_a, ping_b;
StrongBindingSet<PingService> bindings;
BindingId binding_id_a = bindings.AddBinding(
base::MakeUnique<PingInstanceCounter>(), mojo::MakeRequest(&ping_a));
BindingId binding_id_b = bindings.AddBinding(
base::MakeUnique<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);
}
} // namespace
} // namespace test
} // namespace mojo