| Index: mojo/public/cpp/bindings/tests/sync_method_unittest.cc
|
| diff --git a/mojo/public/cpp/bindings/tests/sync_method_unittest.cc b/mojo/public/cpp/bindings/tests/sync_method_unittest.cc
|
| index fd519b0f4df83b12cb62dd7acf23901cd1f45374..207a6e2bd6843def5589bb21e9e6eec44576ee43 100644
|
| --- a/mojo/public/cpp/bindings/tests/sync_method_unittest.cc
|
| +++ b/mojo/public/cpp/bindings/tests/sync_method_unittest.cc
|
| @@ -30,26 +30,47 @@ class TestSyncImpl : public TestSync {
|
| public:
|
| TestSyncImpl(TestSyncRequest request) : binding_(this, std::move(request)) {}
|
|
|
| - void set_ping_notification(const Closure& closure) {
|
| - ping_notification_ = closure;
|
| + using PingHandler = Callback<void(const PingCallback&)>;
|
| + void set_ping_handler(const PingHandler& handler) { ping_handler_ = handler; }
|
| +
|
| + using EchoHandler = Callback<void(int32_t, const EchoCallback&)>;
|
| + void set_echo_handler(const EchoHandler& handler) { echo_handler_ = handler; }
|
| +
|
| + using AsyncEchoHandler = Callback<void(int32_t, const AsyncEchoCallback&)>;
|
| + void set_async_echo_handler(const AsyncEchoHandler& handler) {
|
| + async_echo_handler_ = handler;
|
| }
|
|
|
| // TestSync implementation:
|
| - void Get(const GetCallback& callback) override { callback.Run(42); }
|
| - void Set(int32_t value, const SetCallback& callback) override {
|
| - callback.Run();
|
| - }
|
| void Ping(const PingCallback& callback) override {
|
| - ping_notification_.Run();
|
| - callback.Run();
|
| + if (ping_handler_.is_null()) {
|
| + callback.Run();
|
| + return;
|
| + }
|
| + ping_handler_.Run(callback);
|
| }
|
| void Echo(int32_t value, const EchoCallback& callback) override {
|
| - callback.Run(value);
|
| + if (echo_handler_.is_null()) {
|
| + callback.Run(value);
|
| + return;
|
| + }
|
| + echo_handler_.Run(value, callback);
|
| + }
|
| + void AsyncEcho(int32_t value, const AsyncEchoCallback& callback) override {
|
| + if (async_echo_handler_.is_null()) {
|
| + callback.Run(value);
|
| + return;
|
| + }
|
| + async_echo_handler_.Run(value, callback);
|
| }
|
|
|
| + Binding<TestSync>* binding() { return &binding_; }
|
| +
|
| private:
|
| Binding<TestSync> binding_;
|
| - Closure ping_notification_;
|
| + PingHandler ping_handler_;
|
| + EchoHandler echo_handler_;
|
| + AsyncEchoHandler async_echo_handler_;
|
|
|
| DISALLOW_COPY_AND_ASSIGN(TestSyncImpl);
|
| };
|
| @@ -67,9 +88,12 @@ class TestSyncServiceThread {
|
| void SetUp(TestSyncRequest request) {
|
| CHECK(thread_.task_runner()->BelongsToCurrentThread());
|
| impl_.reset(new TestSyncImpl(std::move(request)));
|
| - impl_->set_ping_notification([this]() {
|
| - base::AutoLock locker(lock_);
|
| - ping_called_ = true;
|
| + impl_->set_ping_handler([this](const TestSync::PingCallback& callback) {
|
| + {
|
| + base::AutoLock locker(lock_);
|
| + ping_called_ = true;
|
| + }
|
| + callback.Run();
|
| });
|
| }
|
|
|
| @@ -130,6 +154,197 @@ TEST_F(SyncMethodTest, BasicSyncCalls) {
|
| run_loop.Run();
|
| }
|
|
|
| +TEST_F(SyncMethodTest, 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.
|
| +
|
| + TestSyncPtr ptr;
|
| + // The binding lives on the same thread as the interface pointer.
|
| + TestSyncImpl impl(GetProxy(&ptr));
|
| + int32_t output_value = -1;
|
| + ASSERT_TRUE(ptr->Echo(42, &output_value));
|
| + EXPECT_EQ(42, output_value);
|
| +}
|
| +
|
| +TEST_F(SyncMethodTest, InterefacePtrDestroyedDuringSyncCall) {
|
| + // 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.
|
| +
|
| + TestSyncPtr ptr;
|
| + TestSyncImpl impl(GetProxy(&ptr));
|
| + impl.set_ping_handler([&ptr](const TestSync::PingCallback& callback) {
|
| + ptr.reset();
|
| + callback.Run();
|
| + });
|
| + ASSERT_FALSE(ptr->Ping());
|
| +}
|
| +
|
| +TEST_F(SyncMethodTest, 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.
|
| +
|
| + TestSyncPtr ptr;
|
| + TestSyncImpl impl(GetProxy(&ptr));
|
| + impl.set_ping_handler([&impl](const TestSync::PingCallback& callback) {
|
| + impl.binding()->Close();
|
| + callback.Run();
|
| + });
|
| + ASSERT_FALSE(ptr->Ping());
|
| +}
|
| +
|
| +TEST_F(SyncMethodTest, 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.
|
| +
|
| + TestSyncPtr ptr;
|
| + TestSyncImpl impl(GetProxy(&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);
|
| +}
|
| +
|
| +TEST_F(SyncMethodTest, 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.
|
| +
|
| + TestSyncPtr ptr;
|
| + TestSyncImpl impl(GetProxy(&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);
|
| +}
|
| +
|
| +TEST_F(SyncMethodTest, 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.
|
| +
|
| + TestSyncPtr ptr;
|
| + TestSyncImpl impl(GetProxy(&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,
|
| + [&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);
|
| +}
|
| +
|
| +TEST_F(SyncMethodTest, 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.
|
| +
|
| + TestSyncPtr ptr;
|
| + TestSyncImpl impl(GetProxy(&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,
|
| + [&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);
|
| +}
|
| +
|
| } // namespace
|
| } // namespace test
|
| } // namespace mojo
|
|
|