diff options
author | Hidehiko Abe <hidehiko@google.com> | 2018-04-23 19:57:41 -0700 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2018-04-23 19:57:41 -0700 |
commit | 0ab20ac2283987e63b0e7c1318db2a5cf7c668d2 (patch) | |
tree | bd2d04362f66c36d4279f7a9735ba21ea3a2a021 /mojo/public/cpp/bindings/tests/binding_unittest.cc | |
parent | e389a13ad8648d89ac670ca06f68c9b32976351c (diff) | |
parent | b268b43ac6fdbc4f3a2ed1429b99ace424906090 (diff) | |
download | libchrome-0ab20ac2283987e63b0e7c1318db2a5cf7c668d2.tar.gz |
Migrate libmojo repository into libchrome, part 2.
am: b268b43ac6
Change-Id: I7b2c3d7cfed03673c33eb497be20e238bd0aac33
Diffstat (limited to 'mojo/public/cpp/bindings/tests/binding_unittest.cc')
-rw-r--r-- | mojo/public/cpp/bindings/tests/binding_unittest.cc | 611 |
1 files changed, 611 insertions, 0 deletions
diff --git a/mojo/public/cpp/bindings/tests/binding_unittest.cc b/mojo/public/cpp/bindings/tests/binding_unittest.cc new file mode 100644 index 0000000000..e76993bb68 --- /dev/null +++ b/mojo/public/cpp/bindings/tests/binding_unittest.cc @@ -0,0 +1,611 @@ +// Copyright 2015 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. + +// Note: This file tests both binding.h (mojo::Binding) and strong_binding.h +// (mojo::StrongBinding). + +#include "mojo/public/cpp/bindings/binding.h" + +#include <stdint.h> +#include <utility> + +#include "base/macros.h" +#include "base/memory/ptr_util.h" +#include "base/memory/weak_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "mojo/public/cpp/bindings/strong_binding.h" +#include "mojo/public/interfaces/bindings/tests/ping_service.mojom.h" +#include "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.h" +#include "mojo/public/interfaces/bindings/tests/sample_service.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace { + +class BindingTestBase : public testing::Test { + public: + BindingTestBase() {} + ~BindingTestBase() override {} + + base::MessageLoop& loop() { return loop_; } + + private: + base::MessageLoop loop_; + + DISALLOW_COPY_AND_ASSIGN(BindingTestBase); +}; + +class ServiceImpl : public sample::Service { + public: + explicit ServiceImpl(bool* was_deleted = nullptr) + : was_deleted_(was_deleted) {} + ~ServiceImpl() override { + if (was_deleted_) + *was_deleted_ = true; + } + + private: + // sample::Service implementation + void Frobinate(sample::FooPtr foo, + BazOptions options, + sample::PortPtr port, + const FrobinateCallback& callback) override { + callback.Run(1); + } + void GetPort(InterfaceRequest<sample::Port> port) override {} + + bool* const was_deleted_; + + DISALLOW_COPY_AND_ASSIGN(ServiceImpl); +}; + +template <typename... Args> +void DoSetFlagAndRunClosure(bool* flag, + const base::Closure& closure, + Args... args) { + *flag = true; + if (!closure.is_null()) + closure.Run(); +} + +template <typename... Args> +base::Callback<void(Args...)> SetFlagAndRunClosure( + bool* flag, + const base::Closure& callback = base::Closure()) { + return base::Bind(&DoSetFlagAndRunClosure<Args...>, flag, callback); +} + +// BindingTest ----------------------------------------------------------------- + +using BindingTest = BindingTestBase; + +TEST_F(BindingTest, Close) { + bool called = false; + sample::ServicePtr ptr; + auto request = MakeRequest(&ptr); + base::RunLoop run_loop; + ptr.set_connection_error_handler( + SetFlagAndRunClosure(&called, run_loop.QuitClosure())); + ServiceImpl impl; + Binding<sample::Service> binding(&impl, std::move(request)); + + binding.Close(); + EXPECT_FALSE(called); + run_loop.Run(); + EXPECT_TRUE(called); +} + +// Tests that destroying a mojo::Binding closes the bound message pipe handle. +TEST_F(BindingTest, DestroyClosesMessagePipe) { + bool encountered_error = false; + ServiceImpl impl; + sample::ServicePtr ptr; + auto request = MakeRequest(&ptr); + base::RunLoop run_loop; + ptr.set_connection_error_handler( + SetFlagAndRunClosure(&encountered_error, run_loop.QuitClosure())); + bool called = false; + base::RunLoop run_loop2; + { + Binding<sample::Service> binding(&impl, std::move(request)); + ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr, + SetFlagAndRunClosure<int32_t>(&called, + run_loop2.QuitClosure())); + run_loop2.Run(); + EXPECT_TRUE(called); + EXPECT_FALSE(encountered_error); + } + // Now that the Binding is out of scope we should detect an error on the other + // end of the pipe. + run_loop.Run(); + EXPECT_TRUE(encountered_error); + + // And calls should fail. + called = false; + ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr, + SetFlagAndRunClosure<int32_t>(&called, + run_loop2.QuitClosure())); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(called); +} + +// Tests that the binding's connection error handler gets called when the other +// end is closed. +TEST_F(BindingTest, ConnectionError) { + bool called = false; + { + ServiceImpl impl; + sample::ServicePtr ptr; + Binding<sample::Service> binding(&impl, MakeRequest(&ptr)); + base::RunLoop run_loop; + binding.set_connection_error_handler( + SetFlagAndRunClosure(&called, run_loop.QuitClosure())); + ptr.reset(); + EXPECT_FALSE(called); + run_loop.Run(); + EXPECT_TRUE(called); + // We want to make sure that it isn't called again during destruction. + called = false; + } + EXPECT_FALSE(called); +} + +// Tests that calling Close doesn't result in the connection error handler being +// called. +TEST_F(BindingTest, CloseDoesntCallConnectionErrorHandler) { + ServiceImpl impl; + sample::ServicePtr ptr; + Binding<sample::Service> binding(&impl, MakeRequest(&ptr)); + bool called = false; + binding.set_connection_error_handler(SetFlagAndRunClosure(&called)); + binding.Close(); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(called); + + // We can also close the other end, and the error handler still won't be + // called. + ptr.reset(); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(called); +} + +class ServiceImplWithBinding : public ServiceImpl { + public: + ServiceImplWithBinding(bool* was_deleted, + const base::Closure& closure, + InterfaceRequest<sample::Service> request) + : ServiceImpl(was_deleted), + binding_(this, std::move(request)), + closure_(closure) { + binding_.set_connection_error_handler( + base::Bind(&ServiceImplWithBinding::OnConnectionError, + base::Unretained(this))); + } + + private: + ~ServiceImplWithBinding() override{ + closure_.Run(); + } + + void OnConnectionError() { delete this; } + + Binding<sample::Service> binding_; + base::Closure closure_; + + DISALLOW_COPY_AND_ASSIGN(ServiceImplWithBinding); +}; + +// Tests that the binding may be deleted in the connection error handler. +TEST_F(BindingTest, SelfDeleteOnConnectionError) { + bool was_deleted = false; + sample::ServicePtr ptr; + // This should delete itself on connection error. + base::RunLoop run_loop; + new ServiceImplWithBinding(&was_deleted, run_loop.QuitClosure(), + MakeRequest(&ptr)); + ptr.reset(); + EXPECT_FALSE(was_deleted); + run_loop.Run(); + EXPECT_TRUE(was_deleted); +} + +// Tests that explicitly calling Unbind followed by rebinding works. +TEST_F(BindingTest, Unbind) { + ServiceImpl impl; + sample::ServicePtr ptr; + Binding<sample::Service> binding(&impl, MakeRequest(&ptr)); + + bool called = false; + base::RunLoop run_loop; + ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr, + SetFlagAndRunClosure<int32_t>(&called, + run_loop.QuitClosure())); + run_loop.Run(); + EXPECT_TRUE(called); + + called = false; + auto request = binding.Unbind(); + EXPECT_FALSE(binding.is_bound()); + // All calls should fail when not bound... + ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr, + SetFlagAndRunClosure<int32_t>(&called, + run_loop.QuitClosure())); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(called); + + called = false; + binding.Bind(std::move(request)); + EXPECT_TRUE(binding.is_bound()); + // ...and should succeed again when the rebound. + base::RunLoop run_loop2; + ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr, + SetFlagAndRunClosure<int32_t>(&called, + run_loop2.QuitClosure())); + run_loop2.Run(); + EXPECT_TRUE(called); +} + +class IntegerAccessorImpl : public sample::IntegerAccessor { + public: + IntegerAccessorImpl() {} + ~IntegerAccessorImpl() override {} + + private: + // sample::IntegerAccessor implementation. + void GetInteger(const GetIntegerCallback& callback) override { + callback.Run(1, sample::Enum::VALUE); + } + void SetInteger(int64_t data, sample::Enum type) override {} + + DISALLOW_COPY_AND_ASSIGN(IntegerAccessorImpl); +}; + +TEST_F(BindingTest, SetInterfacePtrVersion) { + IntegerAccessorImpl impl; + sample::IntegerAccessorPtr ptr; + Binding<sample::IntegerAccessor> binding(&impl, &ptr); + EXPECT_EQ(3u, ptr.version()); +} + +TEST_F(BindingTest, PauseResume) { + bool called = false; + base::RunLoop run_loop; + sample::ServicePtr ptr; + auto request = MakeRequest(&ptr); + ServiceImpl impl; + Binding<sample::Service> binding(&impl, std::move(request)); + binding.PauseIncomingMethodCallProcessing(); + ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr, + SetFlagAndRunClosure<int32_t>(&called, + run_loop.QuitClosure())); + EXPECT_FALSE(called); + base::RunLoop().RunUntilIdle(); + // Frobinate() should not be called as the binding is paused. + EXPECT_FALSE(called); + + // Resume the binding, which should trigger processing. + binding.ResumeIncomingMethodCallProcessing(); + run_loop.Run(); + EXPECT_TRUE(called); +} + +// Verifies the connection error handler is not run while a binding is paused. +TEST_F(BindingTest, ErrorHandleNotRunWhilePaused) { + bool called = false; + base::RunLoop run_loop; + sample::ServicePtr ptr; + auto request = MakeRequest(&ptr); + ServiceImpl impl; + Binding<sample::Service> binding(&impl, std::move(request)); + binding.set_connection_error_handler( + SetFlagAndRunClosure(&called, run_loop.QuitClosure())); + binding.PauseIncomingMethodCallProcessing(); + + ptr.reset(); + base::RunLoop().RunUntilIdle(); + // The connection error handle should not be called as the binding is paused. + EXPECT_FALSE(called); + + // Resume the binding, which should trigger the error handler. + binding.ResumeIncomingMethodCallProcessing(); + run_loop.Run(); + EXPECT_TRUE(called); +} + +class PingServiceImpl : public test::PingService { + public: + PingServiceImpl() {} + ~PingServiceImpl() override {} + + // test::PingService: + void Ping(const PingCallback& callback) override { + if (!ping_handler_.is_null()) + ping_handler_.Run(); + callback.Run(); + } + + void set_ping_handler(const base::Closure& handler) { + ping_handler_ = handler; + } + + private: + base::Closure ping_handler_; + + DISALLOW_COPY_AND_ASSIGN(PingServiceImpl); +}; + +class CallbackFilter : public MessageReceiver { + public: + explicit CallbackFilter(const base::Closure& callback) + : callback_(callback) {} + ~CallbackFilter() override {} + + static std::unique_ptr<CallbackFilter> Wrap(const base::Closure& callback) { + return base::MakeUnique<CallbackFilter>(callback); + } + + // MessageReceiver: + bool Accept(Message* message) override { + callback_.Run(); + return true; + } + + private: + const base::Closure callback_; +}; + +// Verifies that message filters are notified in the order they were added and +// are always notified before a message is dispatched. +TEST_F(BindingTest, MessageFilter) { + test::PingServicePtr ptr; + PingServiceImpl impl; + mojo::Binding<test::PingService> binding(&impl, MakeRequest(&ptr)); + + int status = 0; + auto handler_helper = [] (int* status, int expected_status, int new_status) { + EXPECT_EQ(expected_status, *status); + *status = new_status; + }; + auto create_handler = [&] (int expected_status, int new_status) { + return base::Bind(handler_helper, &status, expected_status, new_status); + }; + + binding.AddFilter(CallbackFilter::Wrap(create_handler(0, 1))); + binding.AddFilter(CallbackFilter::Wrap(create_handler(1, 2))); + impl.set_ping_handler(create_handler(2, 3)); + + for (int i = 0; i < 10; ++i) { + status = 0; + base::RunLoop loop; + ptr->Ping(loop.QuitClosure()); + loop.Run(); + EXPECT_EQ(3, status); + } +} + +void Fail() { + FAIL() << "Unexpected connection error"; +} + +TEST_F(BindingTest, FlushForTesting) { + bool called = false; + sample::ServicePtr ptr; + auto request = MakeRequest(&ptr); + ServiceImpl impl; + Binding<sample::Service> binding(&impl, std::move(request)); + binding.set_connection_error_handler(base::Bind(&Fail)); + + ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr, + SetFlagAndRunClosure<int32_t>(&called)); + EXPECT_FALSE(called); + // Because the flush is sent from the binding, it only guarantees that the + // request has been received, not the response. The second flush waits for the + // response to be received. + binding.FlushForTesting(); + binding.FlushForTesting(); + EXPECT_TRUE(called); +} + +TEST_F(BindingTest, FlushForTestingWithClosedPeer) { + bool called = false; + sample::ServicePtr ptr; + auto request = MakeRequest(&ptr); + ServiceImpl impl; + Binding<sample::Service> binding(&impl, std::move(request)); + binding.set_connection_error_handler(SetFlagAndRunClosure(&called)); + ptr.reset(); + + EXPECT_FALSE(called); + binding.FlushForTesting(); + EXPECT_TRUE(called); + binding.FlushForTesting(); +} + +TEST_F(BindingTest, ConnectionErrorWithReason) { + sample::ServicePtr ptr; + auto request = MakeRequest(&ptr); + ServiceImpl impl; + Binding<sample::Service> binding(&impl, std::move(request)); + + base::RunLoop run_loop; + binding.set_connection_error_with_reason_handler(base::Bind( + [](const base::Closure& quit_closure, uint32_t custom_reason, + const std::string& description) { + EXPECT_EQ(1234u, custom_reason); + EXPECT_EQ("hello", description); + quit_closure.Run(); + }, + run_loop.QuitClosure())); + + ptr.ResetWithReason(1234u, "hello"); + + run_loop.Run(); +} + +template <typename T> +struct WeakPtrImplRefTraits { + using PointerType = base::WeakPtr<T>; + + static bool IsNull(const base::WeakPtr<T>& ptr) { return !ptr; } + static T* GetRawPointer(base::WeakPtr<T>* ptr) { return ptr->get(); } +}; + +template <typename T> +using WeakBinding = Binding<T, WeakPtrImplRefTraits<T>>; + +TEST_F(BindingTest, CustomImplPointerType) { + PingServiceImpl impl; + base::WeakPtrFactory<test::PingService> weak_factory(&impl); + + test::PingServicePtr proxy; + WeakBinding<test::PingService> binding(weak_factory.GetWeakPtr(), + MakeRequest(&proxy)); + + { + // Ensure the binding is functioning. + base::RunLoop run_loop; + proxy->Ping(run_loop.QuitClosure()); + run_loop.Run(); + } + + { + // Attempt to dispatch another message after the WeakPtr is invalidated. + base::Closure assert_not_reached = base::Bind([] { NOTREACHED(); }); + impl.set_ping_handler(assert_not_reached); + proxy->Ping(assert_not_reached); + + // The binding will close its end of the pipe which will trigger a + // connection error on |proxy|. + base::RunLoop run_loop; + proxy.set_connection_error_handler(run_loop.QuitClosure()); + weak_factory.InvalidateWeakPtrs(); + run_loop.Run(); + } +} + +// StrongBindingTest ----------------------------------------------------------- + +using StrongBindingTest = BindingTestBase; + +// Tests that destroying a mojo::StrongBinding closes the bound message pipe +// handle but does *not* destroy the implementation object. +TEST_F(StrongBindingTest, DestroyClosesMessagePipe) { + base::RunLoop run_loop; + bool encountered_error = false; + bool was_deleted = false; + sample::ServicePtr ptr; + auto request = MakeRequest(&ptr); + ptr.set_connection_error_handler( + SetFlagAndRunClosure(&encountered_error, run_loop.QuitClosure())); + bool called = false; + base::RunLoop run_loop2; + + auto binding = MakeStrongBinding(base::MakeUnique<ServiceImpl>(&was_deleted), + std::move(request)); + ptr->Frobinate( + nullptr, sample::Service::BazOptions::REGULAR, nullptr, + SetFlagAndRunClosure<int32_t>(&called, run_loop2.QuitClosure())); + run_loop2.Run(); + EXPECT_TRUE(called); + EXPECT_FALSE(encountered_error); + binding->Close(); + + // Now that the StrongBinding is closed we should detect an error on the other + // end of the pipe. + run_loop.Run(); + EXPECT_TRUE(encountered_error); + + // Destroying the StrongBinding also destroys the impl. + ASSERT_TRUE(was_deleted); +} + +// Tests the typical case, where the implementation object owns the +// StrongBinding (and should be destroyed on connection error). +TEST_F(StrongBindingTest, ConnectionErrorDestroysImpl) { + sample::ServicePtr ptr; + bool was_deleted = false; + // Will delete itself. + base::RunLoop run_loop; + new ServiceImplWithBinding(&was_deleted, run_loop.QuitClosure(), + MakeRequest(&ptr)); + + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(was_deleted); + + ptr.reset(); + EXPECT_FALSE(was_deleted); + run_loop.Run(); + EXPECT_TRUE(was_deleted); +} + +TEST_F(StrongBindingTest, FlushForTesting) { + bool called = false; + bool was_deleted = false; + sample::ServicePtr ptr; + auto request = MakeRequest(&ptr); + auto binding = MakeStrongBinding(base::MakeUnique<ServiceImpl>(&was_deleted), + std::move(request)); + binding->set_connection_error_handler(base::Bind(&Fail)); + + ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr, + SetFlagAndRunClosure<int32_t>(&called)); + EXPECT_FALSE(called); + // Because the flush is sent from the binding, it only guarantees that the + // request has been received, not the response. The second flush waits for the + // response to be received. + ASSERT_TRUE(binding); + binding->FlushForTesting(); + ASSERT_TRUE(binding); + binding->FlushForTesting(); + EXPECT_TRUE(called); + EXPECT_FALSE(was_deleted); + ptr.reset(); + ASSERT_TRUE(binding); + binding->set_connection_error_handler(base::Closure()); + binding->FlushForTesting(); + EXPECT_TRUE(was_deleted); +} + +TEST_F(StrongBindingTest, FlushForTestingWithClosedPeer) { + bool called = false; + bool was_deleted = false; + sample::ServicePtr ptr; + auto request = MakeRequest(&ptr); + auto binding = MakeStrongBinding(base::MakeUnique<ServiceImpl>(&was_deleted), + std::move(request)); + binding->set_connection_error_handler(SetFlagAndRunClosure(&called)); + ptr.reset(); + + EXPECT_FALSE(called); + EXPECT_FALSE(was_deleted); + ASSERT_TRUE(binding); + binding->FlushForTesting(); + EXPECT_TRUE(called); + EXPECT_TRUE(was_deleted); + ASSERT_FALSE(binding); +} + +TEST_F(StrongBindingTest, ConnectionErrorWithReason) { + sample::ServicePtr ptr; + auto request = MakeRequest(&ptr); + auto binding = + MakeStrongBinding(base::MakeUnique<ServiceImpl>(), std::move(request)); + base::RunLoop run_loop; + binding->set_connection_error_with_reason_handler(base::Bind( + [](const base::Closure& quit_closure, uint32_t custom_reason, + const std::string& description) { + EXPECT_EQ(5678u, custom_reason); + EXPECT_EQ("hello", description); + quit_closure.Run(); + }, + run_loop.QuitClosure())); + + ptr.ResetWithReason(5678u, "hello"); + + run_loop.Run(); +} + +} // namespace +} // mojo |