// 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. #include #include #include #include #include "base/bind.h" #include "base/callback.h" #include "base/callback_helpers.h" #include "base/memory/ptr_util.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" #include "base/synchronization/waitable_event.h" #include "base/task_scheduler/post_task.h" #include "base/test/scoped_task_environment.h" #include "base/threading/sequenced_task_runner_handle.h" #include "base/threading/thread.h" #include "base/threading/thread_task_runner_handle.h" #include "mojo/public/cpp/bindings/associated_binding.h" #include "mojo/public/cpp/bindings/associated_interface_ptr.h" #include "mojo/public/cpp/bindings/associated_interface_ptr_info.h" #include "mojo/public/cpp/bindings/associated_interface_request.h" #include "mojo/public/cpp/bindings/binding.h" #include "mojo/public/cpp/bindings/lib/multiplex_router.h" #include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/public/cpp/bindings/thread_safe_interface_ptr.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 mojo::internal::MultiplexRouter; class IntegerSenderImpl : public IntegerSender { public: explicit IntegerSenderImpl(AssociatedInterfaceRequest request) : binding_(this, std::move(request)) {} ~IntegerSenderImpl() override {} void set_notify_send_method_called( const base::Callback& callback) { notify_send_method_called_ = callback; } void Echo(int32_t value, const EchoCallback& callback) override { callback.Run(value); } void Send(int32_t value) override { notify_send_method_called_.Run(value); } AssociatedBinding* binding() { return &binding_; } void set_connection_error_handler(const base::Closure& handler) { binding_.set_connection_error_handler(handler); } private: AssociatedBinding binding_; base::Callback notify_send_method_called_; }; class IntegerSenderConnectionImpl : public IntegerSenderConnection { public: explicit IntegerSenderConnectionImpl( InterfaceRequest request) : binding_(this, std::move(request)) {} ~IntegerSenderConnectionImpl() override {} void GetSender(AssociatedInterfaceRequest sender) override { IntegerSenderImpl* sender_impl = new IntegerSenderImpl(std::move(sender)); sender_impl->set_connection_error_handler( base::Bind(&DeleteSender, sender_impl)); } void AsyncGetSender(const AsyncGetSenderCallback& callback) override { IntegerSenderAssociatedPtrInfo ptr_info; auto request = MakeRequest(&ptr_info); GetSender(std::move(request)); callback.Run(std::move(ptr_info)); } Binding* binding() { return &binding_; } private: static void DeleteSender(IntegerSenderImpl* sender) { delete sender; } Binding binding_; }; class AssociatedInterfaceTest : public testing::Test { public: AssociatedInterfaceTest() : main_runner_(base::ThreadTaskRunnerHandle::Get()) {} ~AssociatedInterfaceTest() override { base::RunLoop().RunUntilIdle(); } void PumpMessages() { base::RunLoop().RunUntilIdle(); } template AssociatedInterfacePtrInfo EmulatePassingAssociatedPtrInfo( AssociatedInterfacePtrInfo ptr_info, scoped_refptr source, scoped_refptr target) { ScopedInterfaceEndpointHandle handle = ptr_info.PassHandle(); CHECK(handle.pending_association()); auto id = source->AssociateInterface(std::move(handle)); return AssociatedInterfacePtrInfo(target->CreateLocalEndpointHandle(id), ptr_info.version()); } void CreateRouterPair(scoped_refptr* router0, scoped_refptr* router1) { MessagePipe pipe; *router0 = new MultiplexRouter(std::move(pipe.handle0), MultiplexRouter::MULTI_INTERFACE, true, main_runner_); *router1 = new MultiplexRouter(std::move(pipe.handle1), MultiplexRouter::MULTI_INTERFACE, false, main_runner_); } void CreateIntegerSenderWithExistingRouters( scoped_refptr router0, IntegerSenderAssociatedPtrInfo* ptr_info0, scoped_refptr router1, IntegerSenderAssociatedRequest* request1) { *request1 = MakeRequest(ptr_info0); *ptr_info0 = EmulatePassingAssociatedPtrInfo(std::move(*ptr_info0), router1, router0); } void CreateIntegerSender(IntegerSenderAssociatedPtrInfo* ptr_info, IntegerSenderAssociatedRequest* request) { scoped_refptr router0; scoped_refptr router1; CreateRouterPair(&router0, &router1); CreateIntegerSenderWithExistingRouters(router1, ptr_info, router0, request); } // Okay to call from any thread. void QuitRunLoop(base::RunLoop* run_loop) { if (main_runner_->RunsTasksInCurrentSequence()) { run_loop->Quit(); } else { main_runner_->PostTask( FROM_HERE, base::Bind(&AssociatedInterfaceTest::QuitRunLoop, base::Unretained(this), base::Unretained(run_loop))); } } private: base::test::ScopedTaskEnvironment task_environment; scoped_refptr main_runner_; }; void DoSetFlagAndRunClosure(bool* flag, const base::Closure& closure) { *flag = true; closure.Run(); } void DoExpectValueSetFlagAndRunClosure(int32_t expected_value, bool* flag, const base::Closure& closure, int32_t value) { EXPECT_EQ(expected_value, value); DoSetFlagAndRunClosure(flag, closure); } base::Closure SetFlagAndRunClosure(bool* flag, const base::Closure& closure) { return base::Bind(&DoSetFlagAndRunClosure, flag, closure); } base::Callback ExpectValueSetFlagAndRunClosure( int32_t expected_value, bool* flag, const base::Closure& closure) { return base::Bind( &DoExpectValueSetFlagAndRunClosure, expected_value, flag, closure); } void Fail() { FAIL() << "Unexpected connection error"; } TEST_F(AssociatedInterfaceTest, InterfacesAtBothEnds) { // Bind to the same pipe two associated interfaces, whose implementation lives // at different ends. Test that the two don't interfere with each other. scoped_refptr router0; scoped_refptr router1; CreateRouterPair(&router0, &router1); AssociatedInterfaceRequest request; IntegerSenderAssociatedPtrInfo ptr_info; CreateIntegerSenderWithExistingRouters(router1, &ptr_info, router0, &request); IntegerSenderImpl impl0(std::move(request)); AssociatedInterfacePtr ptr0; ptr0.Bind(std::move(ptr_info)); CreateIntegerSenderWithExistingRouters(router0, &ptr_info, router1, &request); IntegerSenderImpl impl1(std::move(request)); AssociatedInterfacePtr ptr1; ptr1.Bind(std::move(ptr_info)); base::RunLoop run_loop, run_loop2; bool ptr0_callback_run = false; ptr0->Echo(123, ExpectValueSetFlagAndRunClosure(123, &ptr0_callback_run, run_loop.QuitClosure())); bool ptr1_callback_run = false; ptr1->Echo(456, ExpectValueSetFlagAndRunClosure(456, &ptr1_callback_run, run_loop2.QuitClosure())); run_loop.Run(); run_loop2.Run(); EXPECT_TRUE(ptr0_callback_run); EXPECT_TRUE(ptr1_callback_run); bool ptr0_error_callback_run = false; base::RunLoop run_loop3; ptr0.set_connection_error_handler( SetFlagAndRunClosure(&ptr0_error_callback_run, run_loop3.QuitClosure())); impl0.binding()->Close(); run_loop3.Run(); EXPECT_TRUE(ptr0_error_callback_run); bool impl1_error_callback_run = false; base::RunLoop run_loop4; impl1.binding()->set_connection_error_handler( SetFlagAndRunClosure(&impl1_error_callback_run, run_loop4.QuitClosure())); ptr1.reset(); run_loop4.Run(); EXPECT_TRUE(impl1_error_callback_run); } class TestSender { public: TestSender() : task_runner_(base::CreateSequencedTaskRunnerWithTraits({})), next_sender_(nullptr), max_value_to_send_(-1) {} // The following three methods are called on the corresponding sender thread. void SetUp(IntegerSenderAssociatedPtrInfo ptr_info, TestSender* next_sender, int32_t max_value_to_send) { CHECK(task_runner()->RunsTasksInCurrentSequence()); ptr_.Bind(std::move(ptr_info)); next_sender_ = next_sender ? next_sender : this; max_value_to_send_ = max_value_to_send; } void Send(int32_t value) { CHECK(task_runner()->RunsTasksInCurrentSequence()); if (value > max_value_to_send_) return; ptr_->Send(value); next_sender_->task_runner()->PostTask( FROM_HERE, base::Bind(&TestSender::Send, base::Unretained(next_sender_), ++value)); } void TearDown() { CHECK(task_runner()->RunsTasksInCurrentSequence()); ptr_.reset(); } base::SequencedTaskRunner* task_runner() { return task_runner_.get(); } private: scoped_refptr task_runner_; TestSender* next_sender_; int32_t max_value_to_send_; AssociatedInterfacePtr ptr_; }; class TestReceiver { public: TestReceiver() : task_runner_(base::CreateSequencedTaskRunnerWithTraits({})), expected_calls_(0) {} void SetUp(AssociatedInterfaceRequest request0, AssociatedInterfaceRequest request1, size_t expected_calls, const base::Closure& notify_finish) { CHECK(task_runner()->RunsTasksInCurrentSequence()); impl0_.reset(new IntegerSenderImpl(std::move(request0))); impl0_->set_notify_send_method_called( base::Bind(&TestReceiver::SendMethodCalled, base::Unretained(this))); impl1_.reset(new IntegerSenderImpl(std::move(request1))); impl1_->set_notify_send_method_called( base::Bind(&TestReceiver::SendMethodCalled, base::Unretained(this))); expected_calls_ = expected_calls; notify_finish_ = notify_finish; } void TearDown() { CHECK(task_runner()->RunsTasksInCurrentSequence()); impl0_.reset(); impl1_.reset(); } base::SequencedTaskRunner* task_runner() { return task_runner_.get(); } const std::vector& values() const { return values_; } private: void SendMethodCalled(int32_t value) { values_.push_back(value); if (values_.size() >= expected_calls_) notify_finish_.Run(); } scoped_refptr task_runner_; size_t expected_calls_; std::unique_ptr impl0_; std::unique_ptr impl1_; std::vector values_; base::Closure notify_finish_; }; class NotificationCounter { public: NotificationCounter(size_t total_count, const base::Closure& notify_finish) : total_count_(total_count), current_count_(0), notify_finish_(notify_finish) {} ~NotificationCounter() {} // Okay to call from any thread. void OnGotNotification() { bool finshed = false; { base::AutoLock locker(lock_); CHECK_LT(current_count_, total_count_); current_count_++; finshed = current_count_ == total_count_; } if (finshed) notify_finish_.Run(); } private: base::Lock lock_; const size_t total_count_; size_t current_count_; base::Closure notify_finish_; }; TEST_F(AssociatedInterfaceTest, MultiThreadAccess) { // Set up four associated interfaces on a message pipe. Use the inteface // pointers on four threads in parallel; run the interface implementations on // two threads. Test that multi-threaded access works. const int32_t kMaxValue = 1000; MessagePipe pipe; scoped_refptr router0; scoped_refptr router1; CreateRouterPair(&router0, &router1); AssociatedInterfaceRequest requests[4]; IntegerSenderAssociatedPtrInfo ptr_infos[4]; for (size_t i = 0; i < 4; ++i) { CreateIntegerSenderWithExistingRouters(router1, &ptr_infos[i], router0, &requests[i]); } TestSender senders[4]; for (size_t i = 0; i < 4; ++i) { senders[i].task_runner()->PostTask( FROM_HERE, base::Bind(&TestSender::SetUp, base::Unretained(&senders[i]), base::Passed(&ptr_infos[i]), nullptr, kMaxValue * (i + 1) / 4)); } base::RunLoop run_loop; TestReceiver receivers[2]; NotificationCounter counter( 2, base::Bind(&AssociatedInterfaceTest::QuitRunLoop, base::Unretained(this), base::Unretained(&run_loop))); for (size_t i = 0; i < 2; ++i) { receivers[i].task_runner()->PostTask( FROM_HERE, base::Bind(&TestReceiver::SetUp, base::Unretained(&receivers[i]), base::Passed(&requests[2 * i]), base::Passed(&requests[2 * i + 1]), static_cast(kMaxValue / 2), base::Bind(&NotificationCounter::OnGotNotification, base::Unretained(&counter)))); } for (size_t i = 0; i < 4; ++i) { senders[i].task_runner()->PostTask( FROM_HERE, base::Bind(&TestSender::Send, base::Unretained(&senders[i]), kMaxValue * i / 4 + 1)); } run_loop.Run(); for (size_t i = 0; i < 4; ++i) { base::RunLoop run_loop; senders[i].task_runner()->PostTaskAndReply( FROM_HERE, base::Bind(&TestSender::TearDown, base::Unretained(&senders[i])), base::Bind(&AssociatedInterfaceTest::QuitRunLoop, base::Unretained(this), base::Unretained(&run_loop))); run_loop.Run(); } for (size_t i = 0; i < 2; ++i) { base::RunLoop run_loop; receivers[i].task_runner()->PostTaskAndReply( FROM_HERE, base::Bind(&TestReceiver::TearDown, base::Unretained(&receivers[i])), base::Bind(&AssociatedInterfaceTest::QuitRunLoop, base::Unretained(this), base::Unretained(&run_loop))); run_loop.Run(); } EXPECT_EQ(static_cast(kMaxValue / 2), receivers[0].values().size()); EXPECT_EQ(static_cast(kMaxValue / 2), receivers[1].values().size()); std::vector all_values; all_values.insert(all_values.end(), receivers[0].values().begin(), receivers[0].values().end()); all_values.insert(all_values.end(), receivers[1].values().begin(), receivers[1].values().end()); std::sort(all_values.begin(), all_values.end()); for (size_t i = 0; i < all_values.size(); ++i) ASSERT_EQ(static_cast(i + 1), all_values[i]); } TEST_F(AssociatedInterfaceTest, FIFO) { // Set up four associated interfaces on a message pipe. Use the inteface // pointers on four threads; run the interface implementations on two threads. // Take turns to make calls using the four pointers. Test that FIFO-ness is // preserved. const int32_t kMaxValue = 100; MessagePipe pipe; scoped_refptr router0; scoped_refptr router1; CreateRouterPair(&router0, &router1); AssociatedInterfaceRequest requests[4]; IntegerSenderAssociatedPtrInfo ptr_infos[4]; for (size_t i = 0; i < 4; ++i) { CreateIntegerSenderWithExistingRouters(router1, &ptr_infos[i], router0, &requests[i]); } TestSender senders[4]; for (size_t i = 0; i < 4; ++i) { senders[i].task_runner()->PostTask( FROM_HERE, base::Bind(&TestSender::SetUp, base::Unretained(&senders[i]), base::Passed(&ptr_infos[i]), base::Unretained(&senders[(i + 1) % 4]), kMaxValue)); } base::RunLoop run_loop; TestReceiver receivers[2]; NotificationCounter counter( 2, base::Bind(&AssociatedInterfaceTest::QuitRunLoop, base::Unretained(this), base::Unretained(&run_loop))); for (size_t i = 0; i < 2; ++i) { receivers[i].task_runner()->PostTask( FROM_HERE, base::Bind(&TestReceiver::SetUp, base::Unretained(&receivers[i]), base::Passed(&requests[2 * i]), base::Passed(&requests[2 * i + 1]), static_cast(kMaxValue / 2), base::Bind(&NotificationCounter::OnGotNotification, base::Unretained(&counter)))); } senders[0].task_runner()->PostTask( FROM_HERE, base::Bind(&TestSender::Send, base::Unretained(&senders[0]), 1)); run_loop.Run(); for (size_t i = 0; i < 4; ++i) { base::RunLoop run_loop; senders[i].task_runner()->PostTaskAndReply( FROM_HERE, base::Bind(&TestSender::TearDown, base::Unretained(&senders[i])), base::Bind(&AssociatedInterfaceTest::QuitRunLoop, base::Unretained(this), base::Unretained(&run_loop))); run_loop.Run(); } for (size_t i = 0; i < 2; ++i) { base::RunLoop run_loop; receivers[i].task_runner()->PostTaskAndReply( FROM_HERE, base::Bind(&TestReceiver::TearDown, base::Unretained(&receivers[i])), base::Bind(&AssociatedInterfaceTest::QuitRunLoop, base::Unretained(this), base::Unretained(&run_loop))); run_loop.Run(); } EXPECT_EQ(static_cast(kMaxValue / 2), receivers[0].values().size()); EXPECT_EQ(static_cast(kMaxValue / 2), receivers[1].values().size()); for (size_t i = 0; i < 2; ++i) { for (size_t j = 1; j < receivers[i].values().size(); ++j) EXPECT_LT(receivers[i].values()[j - 1], receivers[i].values()[j]); } } void CaptureInt32(int32_t* storage, const base::Closure& closure, int32_t value) { *storage = value; closure.Run(); } void CaptureSenderPtrInfo(IntegerSenderAssociatedPtr* storage, const base::Closure& closure, IntegerSenderAssociatedPtrInfo info) { storage->Bind(std::move(info)); closure.Run(); } TEST_F(AssociatedInterfaceTest, PassAssociatedInterfaces) { IntegerSenderConnectionPtr connection_ptr; IntegerSenderConnectionImpl connection(MakeRequest(&connection_ptr)); IntegerSenderAssociatedPtr sender0; connection_ptr->GetSender(MakeRequest(&sender0)); int32_t echoed_value = 0; base::RunLoop run_loop; sender0->Echo(123, base::Bind(&CaptureInt32, &echoed_value, run_loop.QuitClosure())); run_loop.Run(); EXPECT_EQ(123, echoed_value); IntegerSenderAssociatedPtr sender1; base::RunLoop run_loop2; connection_ptr->AsyncGetSender( base::Bind(&CaptureSenderPtrInfo, &sender1, run_loop2.QuitClosure())); run_loop2.Run(); EXPECT_TRUE(sender1); base::RunLoop run_loop3; sender1->Echo(456, base::Bind(&CaptureInt32, &echoed_value, run_loop3.QuitClosure())); run_loop3.Run(); EXPECT_EQ(456, echoed_value); } TEST_F(AssociatedInterfaceTest, BindingWaitAndPauseWhenNoAssociatedInterfaces) { IntegerSenderConnectionPtr connection_ptr; IntegerSenderConnectionImpl connection(MakeRequest(&connection_ptr)); IntegerSenderAssociatedPtr sender0; connection_ptr->GetSender(MakeRequest(&sender0)); EXPECT_FALSE(connection.binding()->HasAssociatedInterfaces()); // There are no associated interfaces running on the pipe yet. It is okay to // pause. connection.binding()->PauseIncomingMethodCallProcessing(); connection.binding()->ResumeIncomingMethodCallProcessing(); // There are no associated interfaces running on the pipe yet. It is okay to // wait. EXPECT_TRUE(connection.binding()->WaitForIncomingMethodCall()); // The previous wait has dispatched the GetSender request message, therefore // an associated interface has been set up on the pipe. It is not allowed to // wait or pause. EXPECT_TRUE(connection.binding()->HasAssociatedInterfaces()); } class PingServiceImpl : public PingService { public: explicit PingServiceImpl(PingServiceAssociatedRequest request) : binding_(this, std::move(request)) {} ~PingServiceImpl() override {} AssociatedBinding& binding() { return binding_; } void set_ping_handler(const base::Closure& handler) { ping_handler_ = handler; } // PingService: void Ping(const PingCallback& callback) override { if (!ping_handler_.is_null()) ping_handler_.Run(); callback.Run(); } private: AssociatedBinding binding_; base::Closure ping_handler_; }; class PingProviderImpl : public AssociatedPingProvider { public: explicit PingProviderImpl(AssociatedPingProviderRequest request) : binding_(this, std::move(request)) {} ~PingProviderImpl() override {} // AssociatedPingProvider: void GetPing(PingServiceAssociatedRequest request) override { ping_services_.emplace_back(new PingServiceImpl(std::move(request))); if (expected_bindings_count_ > 0 && ping_services_.size() == expected_bindings_count_ && !quit_waiting_.is_null()) { expected_bindings_count_ = 0; base::ResetAndReturn(&quit_waiting_).Run(); } } std::vector>& ping_services() { return ping_services_; } void WaitForBindings(size_t count) { DCHECK(quit_waiting_.is_null()); expected_bindings_count_ = count; base::RunLoop loop; quit_waiting_ = loop.QuitClosure(); loop.Run(); } private: Binding binding_; std::vector> ping_services_; size_t expected_bindings_count_ = 0; base::Closure quit_waiting_; }; 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 std::make_unique(callback); } // MessageReceiver: bool Accept(Message* message) override { callback_.Run(); return true; } private: const base::Closure callback_; }; // Verifies that filters work as expected on associated bindings, i.e. that // they're notified in order, before dispatch; and that each associated // binding in a group operates with its own set of filters. TEST_F(AssociatedInterfaceTest, BindingWithFilters) { AssociatedPingProviderPtr provider; PingProviderImpl provider_impl(MakeRequest(&provider)); PingServiceAssociatedPtr ping_a, ping_b; provider->GetPing(MakeRequest(&ping_a)); provider->GetPing(MakeRequest(&ping_b)); provider_impl.WaitForBindings(2); ASSERT_EQ(2u, provider_impl.ping_services().size()); PingServiceImpl& ping_a_impl = *provider_impl.ping_services()[0]; PingServiceImpl& ping_b_impl = *provider_impl.ping_services()[1]; int a_status, b_status; auto handler_helper = [] (int* a_status, int* b_status, int expected_a_status, int new_a_status, int expected_b_status, int new_b_status) { EXPECT_EQ(expected_a_status, *a_status); EXPECT_EQ(expected_b_status, *b_status); *a_status = new_a_status; *b_status = new_b_status; }; auto create_handler = [&] (int expected_a_status, int new_a_status, int expected_b_status, int new_b_status) { return base::Bind(handler_helper, &a_status, &b_status, expected_a_status, new_a_status, expected_b_status, new_b_status); }; ping_a_impl.binding().AddFilter( CallbackFilter::Wrap(create_handler(0, 1, 0, 0))); ping_a_impl.binding().AddFilter( CallbackFilter::Wrap(create_handler(1, 2, 0, 0))); ping_a_impl.set_ping_handler(create_handler(2, 3, 0, 0)); ping_b_impl.binding().AddFilter( CallbackFilter::Wrap(create_handler(3, 3, 0, 1))); ping_b_impl.binding().AddFilter( CallbackFilter::Wrap(create_handler(3, 3, 1, 2))); ping_b_impl.set_ping_handler(create_handler(3, 3, 2, 3)); for (int i = 0; i < 10; ++i) { a_status = 0; b_status = 0; { base::RunLoop loop; ping_a->Ping(loop.QuitClosure()); loop.Run(); } EXPECT_EQ(3, a_status); EXPECT_EQ(0, b_status); { base::RunLoop loop; ping_b->Ping(loop.QuitClosure()); loop.Run(); } EXPECT_EQ(3, a_status); EXPECT_EQ(3, b_status); } } TEST_F(AssociatedInterfaceTest, AssociatedPtrFlushForTesting) { AssociatedInterfaceRequest request; IntegerSenderAssociatedPtrInfo ptr_info; CreateIntegerSender(&ptr_info, &request); IntegerSenderImpl impl0(std::move(request)); AssociatedInterfacePtr ptr0; ptr0.Bind(std::move(ptr_info)); ptr0.set_connection_error_handler(base::Bind(&Fail)); bool ptr0_callback_run = false; ptr0->Echo(123, ExpectValueSetFlagAndRunClosure(123, &ptr0_callback_run, base::DoNothing())); ptr0.FlushForTesting(); EXPECT_TRUE(ptr0_callback_run); } void SetBool(bool* value) { *value = true; } template void SetBoolWithUnusedParameter(bool* value, T unused) { *value = true; } TEST_F(AssociatedInterfaceTest, AssociatedPtrFlushForTestingWithClosedPeer) { AssociatedInterfaceRequest request; IntegerSenderAssociatedPtrInfo ptr_info; CreateIntegerSender(&ptr_info, &request); AssociatedInterfacePtr ptr0; ptr0.Bind(std::move(ptr_info)); bool called = false; ptr0.set_connection_error_handler(base::Bind(&SetBool, &called)); request = nullptr; ptr0.FlushForTesting(); EXPECT_TRUE(called); ptr0.FlushForTesting(); } TEST_F(AssociatedInterfaceTest, AssociatedBindingFlushForTesting) { AssociatedInterfaceRequest request; IntegerSenderAssociatedPtrInfo ptr_info; CreateIntegerSender(&ptr_info, &request); IntegerSenderImpl impl0(std::move(request)); impl0.set_connection_error_handler(base::Bind(&Fail)); AssociatedInterfacePtr ptr0; ptr0.Bind(std::move(ptr_info)); bool ptr0_callback_run = false; ptr0->Echo(123, ExpectValueSetFlagAndRunClosure(123, &ptr0_callback_run, base::DoNothing())); // 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. impl0.binding()->FlushForTesting(); impl0.binding()->FlushForTesting(); EXPECT_TRUE(ptr0_callback_run); } TEST_F(AssociatedInterfaceTest, AssociatedBindingFlushForTestingWithClosedPeer) { scoped_refptr router0; scoped_refptr router1; CreateRouterPair(&router0, &router1); AssociatedInterfaceRequest request; { IntegerSenderAssociatedPtrInfo ptr_info; CreateIntegerSenderWithExistingRouters(router1, &ptr_info, router0, &request); } IntegerSenderImpl impl(std::move(request)); bool called = false; impl.set_connection_error_handler(base::Bind(&SetBool, &called)); impl.binding()->FlushForTesting(); EXPECT_TRUE(called); impl.binding()->FlushForTesting(); } TEST_F(AssociatedInterfaceTest, BindingFlushForTesting) { IntegerSenderConnectionPtr ptr; IntegerSenderConnectionImpl impl(MakeRequest(&ptr)); bool called = false; ptr->AsyncGetSender(base::Bind( &SetBoolWithUnusedParameter, &called)); EXPECT_FALSE(called); impl.binding()->set_connection_error_handler(base::Bind(&Fail)); // 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. impl.binding()->FlushForTesting(); impl.binding()->FlushForTesting(); EXPECT_TRUE(called); } TEST_F(AssociatedInterfaceTest, BindingFlushForTestingWithClosedPeer) { IntegerSenderConnectionPtr ptr; IntegerSenderConnectionImpl impl(MakeRequest(&ptr)); bool called = false; impl.binding()->set_connection_error_handler(base::Bind(&SetBool, &called)); ptr.reset(); EXPECT_FALSE(called); impl.binding()->FlushForTesting(); EXPECT_TRUE(called); impl.binding()->FlushForTesting(); } TEST_F(AssociatedInterfaceTest, StrongBindingFlushForTesting) { IntegerSenderConnectionPtr ptr; auto binding = MakeStrongBinding(std::make_unique( IntegerSenderConnectionRequest{}), MakeRequest(&ptr)); bool called = false; IntegerSenderAssociatedPtr sender_ptr; ptr->GetSender(MakeRequest(&sender_ptr)); sender_ptr->Echo(1, base::Bind(&SetBoolWithUnusedParameter, &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); } TEST_F(AssociatedInterfaceTest, StrongBindingFlushForTestingWithClosedPeer) { IntegerSenderConnectionPtr ptr; bool called = false; auto binding = MakeStrongBinding(std::make_unique( IntegerSenderConnectionRequest{}), MakeRequest(&ptr)); binding->set_connection_error_handler(base::Bind(&SetBool, &called)); ptr.reset(); EXPECT_FALSE(called); ASSERT_TRUE(binding); binding->FlushForTesting(); EXPECT_TRUE(called); ASSERT_FALSE(binding); } TEST_F(AssociatedInterfaceTest, PtrFlushForTesting) { IntegerSenderConnectionPtr ptr; IntegerSenderConnectionImpl impl(MakeRequest(&ptr)); bool called = false; ptr.set_connection_error_handler(base::Bind(&Fail)); ptr->AsyncGetSender(base::Bind( &SetBoolWithUnusedParameter, &called)); EXPECT_FALSE(called); ptr.FlushForTesting(); EXPECT_TRUE(called); } TEST_F(AssociatedInterfaceTest, PtrFlushForTestingWithClosedPeer) { IntegerSenderConnectionPtr ptr; MakeRequest(&ptr); bool called = false; ptr.set_connection_error_handler(base::Bind(&SetBool, &called)); EXPECT_FALSE(called); ptr.FlushForTesting(); EXPECT_TRUE(called); ptr.FlushForTesting(); } TEST_F(AssociatedInterfaceTest, AssociatedBindingConnectionErrorWithReason) { AssociatedInterfaceRequest request; IntegerSenderAssociatedPtrInfo ptr_info; CreateIntegerSender(&ptr_info, &request); IntegerSenderImpl impl(std::move(request)); AssociatedInterfacePtr ptr; ptr.Bind(std::move(ptr_info)); base::RunLoop run_loop; impl.binding()->set_connection_error_with_reason_handler(base::Bind( [](const base::Closure& quit_closure, uint32_t custom_reason, const std::string& description) { EXPECT_EQ(123u, custom_reason); EXPECT_EQ("farewell", description); quit_closure.Run(); }, run_loop.QuitClosure())); ptr.ResetWithReason(123u, "farewell"); run_loop.Run(); } TEST_F(AssociatedInterfaceTest, PendingAssociatedBindingConnectionErrorWithReason) { // Test that AssociatedBinding is notified with connection error when the // interface hasn't associated with a message pipe and the peer is closed. IntegerSenderAssociatedPtr ptr; IntegerSenderImpl impl(MakeRequest(&ptr)); base::RunLoop run_loop; impl.binding()->set_connection_error_with_reason_handler(base::Bind( [](const base::Closure& quit_closure, uint32_t custom_reason, const std::string& description) { EXPECT_EQ(123u, custom_reason); EXPECT_EQ("farewell", description); quit_closure.Run(); }, run_loop.QuitClosure())); ptr.ResetWithReason(123u, "farewell"); run_loop.Run(); } TEST_F(AssociatedInterfaceTest, AssociatedPtrConnectionErrorWithReason) { AssociatedInterfaceRequest request; IntegerSenderAssociatedPtrInfo ptr_info; CreateIntegerSender(&ptr_info, &request); IntegerSenderImpl impl(std::move(request)); AssociatedInterfacePtr ptr; ptr.Bind(std::move(ptr_info)); base::RunLoop run_loop; ptr.set_connection_error_with_reason_handler(base::Bind( [](const base::Closure& quit_closure, uint32_t custom_reason, const std::string& description) { EXPECT_EQ(456u, custom_reason); EXPECT_EQ("farewell", description); quit_closure.Run(); }, run_loop.QuitClosure())); impl.binding()->CloseWithReason(456u, "farewell"); run_loop.Run(); } TEST_F(AssociatedInterfaceTest, PendingAssociatedPtrConnectionErrorWithReason) { // Test that AssociatedInterfacePtr is notified with connection error when the // interface hasn't associated with a message pipe and the peer is closed. IntegerSenderAssociatedPtr ptr; auto request = MakeRequest(&ptr); base::RunLoop run_loop; ptr.set_connection_error_with_reason_handler(base::Bind( [](const base::Closure& quit_closure, uint32_t custom_reason, const std::string& description) { EXPECT_EQ(456u, custom_reason); EXPECT_EQ("farewell", description); quit_closure.Run(); }, run_loop.QuitClosure())); request.ResetWithReason(456u, "farewell"); run_loop.Run(); } TEST_F(AssociatedInterfaceTest, AssociatedRequestResetWithReason) { AssociatedInterfaceRequest request; IntegerSenderAssociatedPtrInfo ptr_info; CreateIntegerSender(&ptr_info, &request); AssociatedInterfacePtr ptr; ptr.Bind(std::move(ptr_info)); base::RunLoop run_loop; ptr.set_connection_error_with_reason_handler(base::Bind( [](const base::Closure& quit_closure, uint32_t custom_reason, const std::string& description) { EXPECT_EQ(789u, custom_reason); EXPECT_EQ("long time no see", description); quit_closure.Run(); }, run_loop.QuitClosure())); request.ResetWithReason(789u, "long time no see"); run_loop.Run(); } TEST_F(AssociatedInterfaceTest, ThreadSafeAssociatedInterfacePtr) { IntegerSenderConnectionPtr connection_ptr; IntegerSenderConnectionImpl connection(MakeRequest(&connection_ptr)); IntegerSenderAssociatedPtr sender; connection_ptr->GetSender(MakeRequest(&sender)); scoped_refptr thread_safe_sender = ThreadSafeIntegerSenderAssociatedPtr::Create(std::move(sender)); { // Test the thread safe pointer can be used from the interface ptr thread. int32_t echoed_value = 0; base::RunLoop run_loop; (*thread_safe_sender) ->Echo(123, base::Bind(&CaptureInt32, &echoed_value, run_loop.QuitClosure())); run_loop.Run(); EXPECT_EQ(123, echoed_value); } // Test the thread safe pointer can be used from another thread. base::RunLoop run_loop; auto run_method = base::Bind( [](const scoped_refptr& main_task_runner, const base::Closure& quit_closure, const scoped_refptr& thread_safe_sender) { auto done_callback = base::Bind( [](const scoped_refptr& main_task_runner, const base::Closure& quit_closure, scoped_refptr sender_sequence_runner, int32_t result) { EXPECT_EQ(123, result); // Validate the callback is invoked on the calling sequence. EXPECT_TRUE(sender_sequence_runner->RunsTasksInCurrentSequence()); // Notify the run_loop to quit. main_task_runner->PostTask(FROM_HERE, quit_closure); }); scoped_refptr current_sequence_runner = base::SequencedTaskRunnerHandle::Get(); (*thread_safe_sender) ->Echo(123, base::Bind(done_callback, main_task_runner, quit_closure, current_sequence_runner)); }, base::SequencedTaskRunnerHandle::Get(), run_loop.QuitClosure(), thread_safe_sender); base::CreateSequencedTaskRunnerWithTraits({})->PostTask(FROM_HERE, run_method); // Block until the method callback is called on the background thread. run_loop.Run(); } struct ForwarderTestContext { IntegerSenderConnectionPtr connection_ptr; std::unique_ptr interface_impl; IntegerSenderAssociatedRequest sender_request; }; TEST_F(AssociatedInterfaceTest, ThreadSafeAssociatedInterfacePtrWithTaskRunner) { const scoped_refptr other_thread_task_runner = base::CreateSequencedTaskRunnerWithTraits({}); ForwarderTestContext* context = new ForwarderTestContext(); IntegerSenderAssociatedPtrInfo sender_info; base::WaitableEvent sender_info_bound_event( base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); auto setup = [](base::WaitableEvent* sender_info_bound_event, IntegerSenderAssociatedPtrInfo* sender_info, ForwarderTestContext* context) { context->interface_impl = std::make_unique( MakeRequest(&context->connection_ptr)); auto sender_request = MakeRequest(sender_info); context->connection_ptr->GetSender(std::move(sender_request)); // Unblock the main thread as soon as |sender_info| is set. sender_info_bound_event->Signal(); }; other_thread_task_runner->PostTask( FROM_HERE, base::Bind(setup, &sender_info_bound_event, &sender_info, context)); sender_info_bound_event.Wait(); // Create a ThreadSafeAssociatedPtr that binds on the background thread and is // associated with |connection_ptr| there. scoped_refptr thread_safe_ptr = ThreadSafeIntegerSenderAssociatedPtr::Create(std::move(sender_info), other_thread_task_runner); // Issue a call on the thread-safe ptr immediately. Note that this may happen // before the interface is bound on the background thread, and that must be // OK. { auto echo_callback = base::Bind([](const base::Closure& quit_closure, int32_t result) { EXPECT_EQ(123, result); quit_closure.Run(); }); base::RunLoop run_loop; (*thread_safe_ptr) ->Echo(123, base::Bind(echo_callback, run_loop.QuitClosure())); // Block until the method callback is called. run_loop.Run(); } other_thread_task_runner->DeleteSoon(FROM_HERE, context); // Reset the pointer now so the InterfacePtr associated resources can be // deleted before the background thread's message loop is invalidated. thread_safe_ptr = nullptr; } class DiscardingAssociatedPingProviderProvider : public AssociatedPingProviderProvider { public: void GetPingProvider( AssociatedPingProviderAssociatedRequest request) override {} }; TEST_F(AssociatedInterfaceTest, CloseWithoutBindingAssociatedRequest) { DiscardingAssociatedPingProviderProvider ping_provider_provider; mojo::Binding binding( &ping_provider_provider); AssociatedPingProviderProviderPtr provider_provider; binding.Bind(mojo::MakeRequest(&provider_provider)); AssociatedPingProviderAssociatedPtr provider; provider_provider->GetPingProvider(mojo::MakeRequest(&provider)); PingServiceAssociatedPtr ping; provider->GetPing(mojo::MakeRequest(&ping)); base::RunLoop run_loop; ping.set_connection_error_handler(run_loop.QuitClosure()); run_loop.Run(); } TEST_F(AssociatedInterfaceTest, AssociateWithDisconnectedPipe) { IntegerSenderAssociatedPtr sender; AssociateWithDisconnectedPipe(MakeRequest(&sender).PassHandle()); sender->Send(42); } } // namespace } // namespace test } // namespace mojo