From b268b43ac6fdbc4f3a2ed1429b99ace424906090 Mon Sep 17 00:00:00 2001 From: Hidehiko Abe Date: Tue, 24 Apr 2018 01:37:19 +0900 Subject: Migrate libmojo repository into libchrome, part 2. This CL moves following files. - .gitignore - Android.bp is merged into libchrome's Android.bp. - base/android/* - build/* except build_config.h which is exactly same with libchrome's. - ipc/* - mojo/* except mojo/public/tools/bindings/generators/__init__.py which is unused and not in chrome repository. - soong/* into libchrome_tools/ - third_party/{catapult,jinja2,markupsafe,ply}/* - ui/gfx/{geometry,range}/mojo/* Then, update several paths/build rules to be adapted. Bug: 73606903 Test: Built locally. Ran on DUT. Change-Id: I2a532a42aa68dcb215dbd71d8673192311509726 --- mojo/public/cpp/bindings/tests/binding_unittest.cc | 611 +++++++++++++++++++++ 1 file changed, 611 insertions(+) create mode 100644 mojo/public/cpp/bindings/tests/binding_unittest.cc (limited to 'mojo/public/cpp/bindings/tests/binding_unittest.cc') 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 +#include + +#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 port) override {} + + bool* const was_deleted_; + + DISALLOW_COPY_AND_ASSIGN(ServiceImpl); +}; + +template +void DoSetFlagAndRunClosure(bool* flag, + const base::Closure& closure, + Args... args) { + *flag = true; + if (!closure.is_null()) + closure.Run(); +} + +template +base::Callback SetFlagAndRunClosure( + bool* flag, + const base::Closure& callback = base::Closure()) { + return base::Bind(&DoSetFlagAndRunClosure, 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 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 binding(&impl, std::move(request)); + ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr, + SetFlagAndRunClosure(&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(&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 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 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 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 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 binding(&impl, MakeRequest(&ptr)); + + bool called = false; + base::RunLoop run_loop; + ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr, + SetFlagAndRunClosure(&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(&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(&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 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 binding(&impl, std::move(request)); + binding.PauseIncomingMethodCallProcessing(); + ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr, + SetFlagAndRunClosure(&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 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 Wrap(const base::Closure& callback) { + return base::MakeUnique(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 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 binding(&impl, std::move(request)); + binding.set_connection_error_handler(base::Bind(&Fail)); + + ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr, + SetFlagAndRunClosure(&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 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 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 +struct WeakPtrImplRefTraits { + using PointerType = base::WeakPtr; + + static bool IsNull(const base::WeakPtr& ptr) { return !ptr; } + static T* GetRawPointer(base::WeakPtr* ptr) { return ptr->get(); } +}; + +template +using WeakBinding = Binding>; + +TEST_F(BindingTest, CustomImplPointerType) { + PingServiceImpl impl; + base::WeakPtrFactory weak_factory(&impl); + + test::PingServicePtr proxy; + WeakBinding 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(&was_deleted), + std::move(request)); + ptr->Frobinate( + nullptr, sample::Service::BazOptions::REGULAR, nullptr, + SetFlagAndRunClosure(&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(&was_deleted), + std::move(request)); + binding->set_connection_error_handler(base::Bind(&Fail)); + + ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr, + SetFlagAndRunClosure(&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(&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(), 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 -- cgit v1.2.3