diff options
Diffstat (limited to 'mojo/public/cpp/bindings/tests/sync_method_unittest.cc')
-rw-r--r-- | mojo/public/cpp/bindings/tests/sync_method_unittest.cc | 831 |
1 files changed, 831 insertions, 0 deletions
diff --git a/mojo/public/cpp/bindings/tests/sync_method_unittest.cc b/mojo/public/cpp/bindings/tests/sync_method_unittest.cc new file mode 100644 index 0000000000..084e080ad3 --- /dev/null +++ b/mojo/public/cpp/bindings/tests/sync_method_unittest.cc @@ -0,0 +1,831 @@ +// 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 <utility> + +#include "base/bind.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "base/threading/thread.h" +#include "mojo/public/cpp/bindings/associated_binding.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/interfaces/bindings/tests/test_sync_methods.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace test { +namespace { + +template <typename... Args> +struct LambdaBinder { + using CallbackType = base::Callback<void(Args...)>; + + template <typename Func> + static void RunLambda(Func func, Args... args) { + func(std::move(args)...); + } + + template <typename Func> + static CallbackType BindLambda(Func func) { + return base::Bind(&LambdaBinder::RunLambda<Func>, func); + } +}; + +class TestSyncCommonImpl { + public: + TestSyncCommonImpl() {} + + using PingHandler = base::Callback<void(const base::Callback<void()>&)>; + using PingBinder = LambdaBinder<const base::Callback<void()>&>; + template <typename Func> + void set_ping_handler(Func handler) { + ping_handler_ = PingBinder::BindLambda(handler); + } + + using EchoHandler = + base::Callback<void(int32_t, const base::Callback<void(int32_t)>&)>; + using EchoBinder = + LambdaBinder<int32_t, const base::Callback<void(int32_t)>&>; + template <typename Func> + void set_echo_handler(Func handler) { + echo_handler_ = EchoBinder::BindLambda(handler); + } + + using AsyncEchoHandler = + base::Callback<void(int32_t, const base::Callback<void(int32_t)>&)>; + using AsyncEchoBinder = + LambdaBinder<int32_t, const base::Callback<void(int32_t)>&>; + template <typename Func> + void set_async_echo_handler(Func handler) { + async_echo_handler_ = AsyncEchoBinder::BindLambda(handler); + } + + using SendInterfaceHandler = base::Callback<void(TestSyncAssociatedPtrInfo)>; + using SendInterfaceBinder = LambdaBinder<TestSyncAssociatedPtrInfo>; + template <typename Func> + void set_send_interface_handler(Func handler) { + send_interface_handler_ = SendInterfaceBinder::BindLambda(handler); + } + + using SendRequestHandler = base::Callback<void(TestSyncAssociatedRequest)>; + using SendRequestBinder = LambdaBinder<TestSyncAssociatedRequest>; + template <typename Func> + void set_send_request_handler(Func handler) { + send_request_handler_ = SendRequestBinder::BindLambda(handler); + } + + void PingImpl(const base::Callback<void()>& callback) { + if (ping_handler_.is_null()) { + callback.Run(); + return; + } + ping_handler_.Run(callback); + } + void EchoImpl(int32_t value, const base::Callback<void(int32_t)>& callback) { + if (echo_handler_.is_null()) { + callback.Run(value); + return; + } + echo_handler_.Run(value, callback); + } + void AsyncEchoImpl(int32_t value, + const base::Callback<void(int32_t)>& callback) { + if (async_echo_handler_.is_null()) { + callback.Run(value); + return; + } + async_echo_handler_.Run(value, callback); + } + void SendInterfaceImpl(TestSyncAssociatedPtrInfo ptr) { + send_interface_handler_.Run(std::move(ptr)); + } + void SendRequestImpl(TestSyncAssociatedRequest request) { + send_request_handler_.Run(std::move(request)); + } + + private: + PingHandler ping_handler_; + EchoHandler echo_handler_; + AsyncEchoHandler async_echo_handler_; + SendInterfaceHandler send_interface_handler_; + SendRequestHandler send_request_handler_; + + DISALLOW_COPY_AND_ASSIGN(TestSyncCommonImpl); +}; + +class TestSyncImpl : public TestSync, public TestSyncCommonImpl { + public: + explicit TestSyncImpl(TestSyncRequest request) + : binding_(this, std::move(request)) {} + + // TestSync implementation: + void Ping(const PingCallback& callback) override { PingImpl(callback); } + void Echo(int32_t value, const EchoCallback& callback) override { + EchoImpl(value, callback); + } + void AsyncEcho(int32_t value, const AsyncEchoCallback& callback) override { + AsyncEchoImpl(value, callback); + } + + Binding<TestSync>* binding() { return &binding_; } + + private: + Binding<TestSync> binding_; + + DISALLOW_COPY_AND_ASSIGN(TestSyncImpl); +}; + +class TestSyncMasterImpl : public TestSyncMaster, public TestSyncCommonImpl { + public: + explicit TestSyncMasterImpl(TestSyncMasterRequest request) + : binding_(this, std::move(request)) {} + + // TestSyncMaster implementation: + void Ping(const PingCallback& callback) override { PingImpl(callback); } + void Echo(int32_t value, const EchoCallback& callback) override { + EchoImpl(value, callback); + } + void AsyncEcho(int32_t value, const AsyncEchoCallback& callback) override { + AsyncEchoImpl(value, callback); + } + void SendInterface(TestSyncAssociatedPtrInfo ptr) override { + SendInterfaceImpl(std::move(ptr)); + } + void SendRequest(TestSyncAssociatedRequest request) override { + SendRequestImpl(std::move(request)); + } + + Binding<TestSyncMaster>* binding() { return &binding_; } + + private: + Binding<TestSyncMaster> binding_; + + DISALLOW_COPY_AND_ASSIGN(TestSyncMasterImpl); +}; + +class TestSyncAssociatedImpl : public TestSync, public TestSyncCommonImpl { + public: + explicit TestSyncAssociatedImpl(TestSyncAssociatedRequest request) + : binding_(this, std::move(request)) {} + + // TestSync implementation: + void Ping(const PingCallback& callback) override { PingImpl(callback); } + void Echo(int32_t value, const EchoCallback& callback) override { + EchoImpl(value, callback); + } + void AsyncEcho(int32_t value, const AsyncEchoCallback& callback) override { + AsyncEchoImpl(value, callback); + } + + AssociatedBinding<TestSync>* binding() { return &binding_; } + + private: + AssociatedBinding<TestSync> binding_; + + DISALLOW_COPY_AND_ASSIGN(TestSyncAssociatedImpl); +}; + +template <typename Interface> +struct ImplTraits; + +template <> +struct ImplTraits<TestSync> { + using Type = TestSyncImpl; +}; + +template <> +struct ImplTraits<TestSyncMaster> { + using Type = TestSyncMasterImpl; +}; + +template <typename Interface> +using ImplTypeFor = typename ImplTraits<Interface>::Type; + +// A wrapper for either an InterfacePtr or scoped_refptr<ThreadSafeInterfacePtr> +// that exposes the InterfacePtr interface. +template <typename Interface> +class PtrWrapper { + public: + explicit PtrWrapper(InterfacePtr<Interface> ptr) : ptr_(std::move(ptr)) {} + + explicit PtrWrapper( + scoped_refptr<ThreadSafeInterfacePtr<Interface>> thread_safe_ptr) + : thread_safe_ptr_(thread_safe_ptr) {} + + PtrWrapper(PtrWrapper&& other) = default; + + Interface* operator->() { + return thread_safe_ptr_ ? thread_safe_ptr_->get() : ptr_.get(); + } + + void set_connection_error_handler(const base::Closure& error_handler) { + DCHECK(!thread_safe_ptr_); + ptr_.set_connection_error_handler(error_handler); + } + + void reset() { + ptr_ = nullptr; + thread_safe_ptr_ = nullptr; + } + + private: + InterfacePtr<Interface> ptr_; + scoped_refptr<ThreadSafeInterfacePtr<Interface>> thread_safe_ptr_; + + DISALLOW_COPY_AND_ASSIGN(PtrWrapper); +}; + +// The type parameter for SyncMethodCommonTests for varying the Interface and +// whether to use InterfacePtr or ThreadSafeInterfacePtr. +template <typename InterfaceT, bool use_thread_safe_ptr> +struct TestParams { + using Interface = InterfaceT; + static const bool kIsThreadSafeInterfacePtrTest = use_thread_safe_ptr; + + static PtrWrapper<InterfaceT> Wrap(InterfacePtr<Interface> ptr) { + if (kIsThreadSafeInterfacePtrTest) { + return PtrWrapper<Interface>( + ThreadSafeInterfacePtr<Interface>::Create(std::move(ptr))); + } else { + return PtrWrapper<Interface>(std::move(ptr)); + } + } +}; + +template <typename Interface> +class TestSyncServiceThread { + public: + TestSyncServiceThread() + : thread_("TestSyncServiceThread"), ping_called_(false) { + thread_.Start(); + } + + void SetUp(InterfaceRequest<Interface> request) { + CHECK(thread_.task_runner()->BelongsToCurrentThread()); + impl_.reset(new ImplTypeFor<Interface>(std::move(request))); + impl_->set_ping_handler( + [this](const typename Interface::PingCallback& callback) { + { + base::AutoLock locker(lock_); + ping_called_ = true; + } + callback.Run(); + }); + } + + void TearDown() { + CHECK(thread_.task_runner()->BelongsToCurrentThread()); + impl_.reset(); + } + + base::Thread* thread() { return &thread_; } + bool ping_called() const { + base::AutoLock locker(lock_); + return ping_called_; + } + + private: + base::Thread thread_; + + std::unique_ptr<ImplTypeFor<Interface>> impl_; + + mutable base::Lock lock_; + bool ping_called_; + + DISALLOW_COPY_AND_ASSIGN(TestSyncServiceThread); +}; + +class SyncMethodTest : public testing::Test { + public: + SyncMethodTest() {} + ~SyncMethodTest() override { base::RunLoop().RunUntilIdle(); } + + protected: + base::MessageLoop loop_; +}; + +template <typename T> +class SyncMethodCommonTest : public SyncMethodTest { + public: + SyncMethodCommonTest() {} + ~SyncMethodCommonTest() override {} +}; + +class SyncMethodAssociatedTest : public SyncMethodTest { + public: + SyncMethodAssociatedTest() {} + ~SyncMethodAssociatedTest() override {} + + protected: + void SetUp() override { + master_impl_.reset(new TestSyncMasterImpl(MakeRequest(&master_ptr_))); + + asso_request_ = MakeRequest(&asso_ptr_info_); + opposite_asso_request_ = MakeRequest(&opposite_asso_ptr_info_); + + master_impl_->set_send_interface_handler( + [this](TestSyncAssociatedPtrInfo ptr) { + opposite_asso_ptr_info_ = std::move(ptr); + }); + base::RunLoop run_loop; + master_impl_->set_send_request_handler( + [this, &run_loop](TestSyncAssociatedRequest request) { + asso_request_ = std::move(request); + run_loop.Quit(); + }); + + master_ptr_->SendInterface(std::move(opposite_asso_ptr_info_)); + master_ptr_->SendRequest(std::move(asso_request_)); + run_loop.Run(); + } + + void TearDown() override { + asso_ptr_info_ = TestSyncAssociatedPtrInfo(); + asso_request_ = TestSyncAssociatedRequest(); + opposite_asso_ptr_info_ = TestSyncAssociatedPtrInfo(); + opposite_asso_request_ = TestSyncAssociatedRequest(); + + master_ptr_ = nullptr; + master_impl_.reset(); + } + + InterfacePtr<TestSyncMaster> master_ptr_; + std::unique_ptr<TestSyncMasterImpl> master_impl_; + + // An associated interface whose binding lives at the |master_impl_| side. + TestSyncAssociatedPtrInfo asso_ptr_info_; + TestSyncAssociatedRequest asso_request_; + + // An associated interface whose binding lives at the |master_ptr_| side. + TestSyncAssociatedPtrInfo opposite_asso_ptr_info_; + TestSyncAssociatedRequest opposite_asso_request_; +}; + +void SetFlagAndRunClosure(bool* flag, const base::Closure& closure) { + *flag = true; + closure.Run(); +} + +void ExpectValueAndRunClosure(int32_t expected_value, + const base::Closure& closure, + int32_t value) { + EXPECT_EQ(expected_value, value); + closure.Run(); +} + +template <typename Func> +void CallAsyncEchoCallback(Func func, int32_t value) { + func(value); +} + +template <typename Func> +TestSync::AsyncEchoCallback BindAsyncEchoCallback(Func func) { + return base::Bind(&CallAsyncEchoCallback<Func>, func); +} + +// TestSync (without associated interfaces) and TestSyncMaster (with associated +// interfaces) exercise MultiplexRouter with different configurations. +// Each test is run once with an InterfacePtr and once with a +// ThreadSafeInterfacePtr to ensure that they behave the same with respect to +// sync calls. +using InterfaceTypes = testing::Types<TestParams<TestSync, true>, + TestParams<TestSync, false>, + TestParams<TestSyncMaster, true>, + TestParams<TestSyncMaster, false>>; +TYPED_TEST_CASE(SyncMethodCommonTest, InterfaceTypes); + +TYPED_TEST(SyncMethodCommonTest, CallSyncMethodAsynchronously) { + using Interface = typename TypeParam::Interface; + InterfacePtr<Interface> interface_ptr; + ImplTypeFor<Interface> impl(MakeRequest(&interface_ptr)); + auto ptr = TypeParam::Wrap(std::move(interface_ptr)); + + base::RunLoop run_loop; + ptr->Echo(123, base::Bind(&ExpectValueAndRunClosure, 123, + run_loop.QuitClosure())); + run_loop.Run(); +} + +TYPED_TEST(SyncMethodCommonTest, BasicSyncCalls) { + using Interface = typename TypeParam::Interface; + InterfacePtr<Interface> interface_ptr; + InterfaceRequest<Interface> request = MakeRequest(&interface_ptr); + auto ptr = TypeParam::Wrap(std::move(interface_ptr)); + + TestSyncServiceThread<Interface> service_thread; + service_thread.thread()->task_runner()->PostTask( + FROM_HERE, + base::Bind(&TestSyncServiceThread<Interface>::SetUp, + base::Unretained(&service_thread), base::Passed(&request))); + ASSERT_TRUE(ptr->Ping()); + ASSERT_TRUE(service_thread.ping_called()); + + int32_t output_value = -1; + ASSERT_TRUE(ptr->Echo(42, &output_value)); + ASSERT_EQ(42, output_value); + + base::RunLoop run_loop; + service_thread.thread()->task_runner()->PostTaskAndReply( + FROM_HERE, + base::Bind(&TestSyncServiceThread<Interface>::TearDown, + base::Unretained(&service_thread)), + run_loop.QuitClosure()); + run_loop.Run(); +} + +TYPED_TEST(SyncMethodCommonTest, ReenteredBySyncMethodBinding) { + // Test that an interface pointer waiting for a sync call response can be + // reentered by a binding serving sync methods on the same thread. + + using Interface = typename TypeParam::Interface; + InterfacePtr<Interface> interface_ptr; + // The binding lives on the same thread as the interface pointer. + ImplTypeFor<Interface> impl(MakeRequest(&interface_ptr)); + auto ptr = TypeParam::Wrap(std::move(interface_ptr)); + int32_t output_value = -1; + ASSERT_TRUE(ptr->Echo(42, &output_value)); + EXPECT_EQ(42, output_value); +} + +TYPED_TEST(SyncMethodCommonTest, InterfacePtrDestroyedDuringSyncCall) { + // Test that it won't result in crash or hang if an interface pointer is + // destroyed while it is waiting for a sync call response. + + using Interface = typename TypeParam::Interface; + InterfacePtr<Interface> interface_ptr; + ImplTypeFor<Interface> impl(MakeRequest(&interface_ptr)); + auto ptr = TypeParam::Wrap(std::move(interface_ptr)); + impl.set_ping_handler([&ptr](const TestSync::PingCallback& callback) { + ptr.reset(); + callback.Run(); + }); + ASSERT_FALSE(ptr->Ping()); +} + +TYPED_TEST(SyncMethodCommonTest, BindingDestroyedDuringSyncCall) { + // Test that it won't result in crash or hang if a binding is + // closed (and therefore the message pipe handle is closed) while the + // corresponding interface pointer is waiting for a sync call response. + + using Interface = typename TypeParam::Interface; + InterfacePtr<Interface> interface_ptr; + ImplTypeFor<Interface> impl(MakeRequest(&interface_ptr)); + auto ptr = TypeParam::Wrap(std::move(interface_ptr)); + impl.set_ping_handler([&impl](const TestSync::PingCallback& callback) { + impl.binding()->Close(); + callback.Run(); + }); + ASSERT_FALSE(ptr->Ping()); +} + +TYPED_TEST(SyncMethodCommonTest, NestedSyncCallsWithInOrderResponses) { + // Test that we can call a sync method on an interface ptr, while there is + // already a sync call ongoing. The responses arrive in order. + + using Interface = typename TypeParam::Interface; + InterfacePtr<Interface> interface_ptr; + ImplTypeFor<Interface> impl(MakeRequest(&interface_ptr)); + auto ptr = TypeParam::Wrap(std::move(interface_ptr)); + + // The same variable is used to store the output of the two sync calls, in + // order to test that responses are handled in the correct order. + int32_t result_value = -1; + + bool first_call = true; + impl.set_echo_handler([&first_call, &ptr, &result_value]( + int32_t value, const TestSync::EchoCallback& callback) { + if (first_call) { + first_call = false; + ASSERT_TRUE(ptr->Echo(456, &result_value)); + EXPECT_EQ(456, result_value); + } + callback.Run(value); + }); + + ASSERT_TRUE(ptr->Echo(123, &result_value)); + EXPECT_EQ(123, result_value); +} + +TYPED_TEST(SyncMethodCommonTest, NestedSyncCallsWithOutOfOrderResponses) { + // Test that we can call a sync method on an interface ptr, while there is + // already a sync call ongoing. The responses arrive out of order. + + using Interface = typename TypeParam::Interface; + InterfacePtr<Interface> interface_ptr; + ImplTypeFor<Interface> impl(MakeRequest(&interface_ptr)); + auto ptr = TypeParam::Wrap(std::move(interface_ptr)); + + // The same variable is used to store the output of the two sync calls, in + // order to test that responses are handled in the correct order. + int32_t result_value = -1; + + bool first_call = true; + impl.set_echo_handler([&first_call, &ptr, &result_value]( + int32_t value, const TestSync::EchoCallback& callback) { + callback.Run(value); + if (first_call) { + first_call = false; + ASSERT_TRUE(ptr->Echo(456, &result_value)); + EXPECT_EQ(456, result_value); + } + }); + + ASSERT_TRUE(ptr->Echo(123, &result_value)); + EXPECT_EQ(123, result_value); +} + +TYPED_TEST(SyncMethodCommonTest, AsyncResponseQueuedDuringSyncCall) { + // Test that while an interface pointer is waiting for the response to a sync + // call, async responses are queued until the sync call completes. + + using Interface = typename TypeParam::Interface; + InterfacePtr<Interface> interface_ptr; + ImplTypeFor<Interface> impl(MakeRequest(&interface_ptr)); + auto ptr = TypeParam::Wrap(std::move(interface_ptr)); + + int32_t async_echo_request_value = -1; + TestSync::AsyncEchoCallback async_echo_request_callback; + base::RunLoop run_loop1; + impl.set_async_echo_handler( + [&async_echo_request_value, &async_echo_request_callback, &run_loop1]( + int32_t value, const TestSync::AsyncEchoCallback& callback) { + async_echo_request_value = value; + async_echo_request_callback = callback; + run_loop1.Quit(); + }); + + bool async_echo_response_dispatched = false; + base::RunLoop run_loop2; + ptr->AsyncEcho( + 123, + BindAsyncEchoCallback( + [&async_echo_response_dispatched, &run_loop2](int32_t result) { + async_echo_response_dispatched = true; + EXPECT_EQ(123, result); + run_loop2.Quit(); + })); + // Run until the AsyncEcho request reaches the service side. + run_loop1.Run(); + + impl.set_echo_handler( + [&async_echo_request_value, &async_echo_request_callback]( + int32_t value, const TestSync::EchoCallback& callback) { + // Send back the async response first. + EXPECT_FALSE(async_echo_request_callback.is_null()); + async_echo_request_callback.Run(async_echo_request_value); + + callback.Run(value); + }); + + int32_t result_value = -1; + ASSERT_TRUE(ptr->Echo(456, &result_value)); + EXPECT_EQ(456, result_value); + + // Although the AsyncEcho response arrives before the Echo response, it should + // be queued and not yet dispatched. + EXPECT_FALSE(async_echo_response_dispatched); + + // Run until the AsyncEcho response is dispatched. + run_loop2.Run(); + + EXPECT_TRUE(async_echo_response_dispatched); +} + +TYPED_TEST(SyncMethodCommonTest, AsyncRequestQueuedDuringSyncCall) { + // Test that while an interface pointer is waiting for the response to a sync + // call, async requests for a binding running on the same thread are queued + // until the sync call completes. + + using Interface = typename TypeParam::Interface; + InterfacePtr<Interface> interface_ptr; + ImplTypeFor<Interface> impl(MakeRequest(&interface_ptr)); + auto ptr = TypeParam::Wrap(std::move(interface_ptr)); + + bool async_echo_request_dispatched = false; + impl.set_async_echo_handler([&async_echo_request_dispatched]( + int32_t value, const TestSync::AsyncEchoCallback& callback) { + async_echo_request_dispatched = true; + callback.Run(value); + }); + + bool async_echo_response_dispatched = false; + base::RunLoop run_loop; + ptr->AsyncEcho( + 123, + BindAsyncEchoCallback( + [&async_echo_response_dispatched, &run_loop](int32_t result) { + async_echo_response_dispatched = true; + EXPECT_EQ(123, result); + run_loop.Quit(); + })); + + impl.set_echo_handler([&async_echo_request_dispatched]( + int32_t value, const TestSync::EchoCallback& callback) { + // Although the AsyncEcho request is sent before the Echo request, it + // shouldn't be dispatched yet at this point, because there is an ongoing + // sync call on the same thread. + EXPECT_FALSE(async_echo_request_dispatched); + callback.Run(value); + }); + + int32_t result_value = -1; + ASSERT_TRUE(ptr->Echo(456, &result_value)); + EXPECT_EQ(456, result_value); + + // Although the AsyncEcho request is sent before the Echo request, it + // shouldn't be dispatched yet. + EXPECT_FALSE(async_echo_request_dispatched); + + // Run until the AsyncEcho response is dispatched. + run_loop.Run(); + + EXPECT_TRUE(async_echo_response_dispatched); +} + +TYPED_TEST(SyncMethodCommonTest, + QueuedMessagesProcessedBeforeErrorNotification) { + // Test that while an interface pointer is waiting for the response to a sync + // call, async responses are queued. If the message pipe is disconnected + // before the queued messages are processed, the connection error + // notification is delayed until all the queued messages are processed. + + // ThreadSafeInterfacePtr doesn't guarantee that messages are delivered before + // error notifications, so skip it for this test. + if (TypeParam::kIsThreadSafeInterfacePtrTest) + return; + + using Interface = typename TypeParam::Interface; + InterfacePtr<Interface> ptr; + ImplTypeFor<Interface> impl(MakeRequest(&ptr)); + + int32_t async_echo_request_value = -1; + TestSync::AsyncEchoCallback async_echo_request_callback; + base::RunLoop run_loop1; + impl.set_async_echo_handler( + [&async_echo_request_value, &async_echo_request_callback, &run_loop1]( + int32_t value, const TestSync::AsyncEchoCallback& callback) { + async_echo_request_value = value; + async_echo_request_callback = callback; + run_loop1.Quit(); + }); + + bool async_echo_response_dispatched = false; + bool connection_error_dispatched = false; + base::RunLoop run_loop2; + ptr->AsyncEcho( + 123, + BindAsyncEchoCallback( + [&async_echo_response_dispatched, &connection_error_dispatched, &ptr, + &run_loop2](int32_t result) { + async_echo_response_dispatched = true; + // At this point, error notification should not be dispatched + // yet. + EXPECT_FALSE(connection_error_dispatched); + EXPECT_FALSE(ptr.encountered_error()); + EXPECT_EQ(123, result); + run_loop2.Quit(); + })); + // Run until the AsyncEcho request reaches the service side. + run_loop1.Run(); + + impl.set_echo_handler( + [&impl, &async_echo_request_value, &async_echo_request_callback]( + int32_t value, const TestSync::EchoCallback& callback) { + // Send back the async response first. + EXPECT_FALSE(async_echo_request_callback.is_null()); + async_echo_request_callback.Run(async_echo_request_value); + + impl.binding()->Close(); + }); + + base::RunLoop run_loop3; + ptr.set_connection_error_handler( + base::Bind(&SetFlagAndRunClosure, &connection_error_dispatched, + run_loop3.QuitClosure())); + + int32_t result_value = -1; + ASSERT_FALSE(ptr->Echo(456, &result_value)); + EXPECT_EQ(-1, result_value); + ASSERT_FALSE(connection_error_dispatched); + EXPECT_FALSE(ptr.encountered_error()); + + // Although the AsyncEcho response arrives before the Echo response, it should + // be queued and not yet dispatched. + EXPECT_FALSE(async_echo_response_dispatched); + + // Run until the AsyncEcho response is dispatched. + run_loop2.Run(); + + EXPECT_TRUE(async_echo_response_dispatched); + + // Run until the error notification is dispatched. + run_loop3.Run(); + + ASSERT_TRUE(connection_error_dispatched); + EXPECT_TRUE(ptr.encountered_error()); +} + +TYPED_TEST(SyncMethodCommonTest, InvalidMessageDuringSyncCall) { + // Test that while an interface pointer is waiting for the response to a sync + // call, an invalid incoming message will disconnect the message pipe, cause + // the sync call to return false, and run the connection error handler + // asynchronously. + + using Interface = typename TypeParam::Interface; + MessagePipe pipe; + + InterfacePtr<Interface> interface_ptr; + interface_ptr.Bind(InterfacePtrInfo<Interface>(std::move(pipe.handle0), 0u)); + auto ptr = TypeParam::Wrap(std::move(interface_ptr)); + + MessagePipeHandle raw_binding_handle = pipe.handle1.get(); + ImplTypeFor<Interface> impl(MakeRequest<Interface>(std::move(pipe.handle1))); + + impl.set_echo_handler([&raw_binding_handle]( + int32_t value, const TestSync::EchoCallback& callback) { + // Write a 1-byte message, which is considered invalid. + char invalid_message = 0; + MojoResult result = + WriteMessageRaw(raw_binding_handle, &invalid_message, 1u, nullptr, 0u, + MOJO_WRITE_MESSAGE_FLAG_NONE); + ASSERT_EQ(MOJO_RESULT_OK, result); + callback.Run(value); + }); + + bool connection_error_dispatched = false; + base::RunLoop run_loop; + // ThreadSafeInterfacePtr doesn't support setting connection error handlers. + if (!TypeParam::kIsThreadSafeInterfacePtrTest) { + ptr.set_connection_error_handler(base::Bind(&SetFlagAndRunClosure, + &connection_error_dispatched, + run_loop.QuitClosure())); + } + + int32_t result_value = -1; + ASSERT_FALSE(ptr->Echo(456, &result_value)); + EXPECT_EQ(-1, result_value); + ASSERT_FALSE(connection_error_dispatched); + + if (!TypeParam::kIsThreadSafeInterfacePtrTest) { + run_loop.Run(); + ASSERT_TRUE(connection_error_dispatched); + } +} + +TEST_F(SyncMethodAssociatedTest, ReenteredBySyncMethodAssoBindingOfSameRouter) { + // Test that an interface pointer waiting for a sync call response can be + // reentered by an associated binding serving sync methods on the same thread. + // The associated binding belongs to the same MultiplexRouter as the waiting + // interface pointer. + + TestSyncAssociatedImpl opposite_asso_impl(std::move(opposite_asso_request_)); + TestSyncAssociatedPtr opposite_asso_ptr; + opposite_asso_ptr.Bind(std::move(opposite_asso_ptr_info_)); + + master_impl_->set_echo_handler([&opposite_asso_ptr]( + int32_t value, const TestSyncMaster::EchoCallback& callback) { + int32_t result_value = -1; + + ASSERT_TRUE(opposite_asso_ptr->Echo(123, &result_value)); + EXPECT_EQ(123, result_value); + callback.Run(value); + }); + + int32_t result_value = -1; + ASSERT_TRUE(master_ptr_->Echo(456, &result_value)); + EXPECT_EQ(456, result_value); +} + +TEST_F(SyncMethodAssociatedTest, + ReenteredBySyncMethodAssoBindingOfDifferentRouter) { + // Test that an interface pointer waiting for a sync call response can be + // reentered by an associated binding serving sync methods on the same thread. + // The associated binding belongs to a different MultiplexRouter as the + // waiting interface pointer. + + TestSyncAssociatedImpl asso_impl(std::move(asso_request_)); + TestSyncAssociatedPtr asso_ptr; + asso_ptr.Bind(std::move(asso_ptr_info_)); + + master_impl_->set_echo_handler( + [&asso_ptr](int32_t value, const TestSyncMaster::EchoCallback& callback) { + int32_t result_value = -1; + + ASSERT_TRUE(asso_ptr->Echo(123, &result_value)); + EXPECT_EQ(123, result_value); + callback.Run(value); + }); + + int32_t result_value = -1; + ASSERT_TRUE(master_ptr_->Echo(456, &result_value)); + EXPECT_EQ(456, result_value); +} + +// TODO(yzshen): Add more tests related to associated interfaces. + +} // namespace +} // namespace test +} // namespace mojo |