| // 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 |