| // Copyright 2015 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <stdint.h> |
| |
| #include "base/check_op.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback.h" |
| #include "base/notreached.h" |
| #include "base/run_loop.h" |
| #include "base/test/bind.h" |
| #include "base/test/gtest_util.h" |
| #include "base/test/task_environment.h" |
| #include "build/build_config.h" |
| #include "mojo/public/cpp/bindings/receiver.h" |
| #include "mojo/public/cpp/bindings/remote.h" |
| #include "mojo/public/cpp/system/message_pipe.h" |
| #include "mojo/public/cpp/test_support/test_support.h" |
| #include "mojo/public/interfaces/bindings/tests/sample_interfaces.test-mojom.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // |
| // The tests in this file are designed to test the interaction between a |
| // Callback and its associated Receiver. If a Callback is deleted before |
| // being used we DCHECK fail--unless the associated Receiver has already |
| // been closed or deleted. This contract must be explained to the Mojo |
| // application developer. For example it is the developer's responsibility to |
| // ensure that the Receiver is destroyed before an unused Callback is destroyed. |
| // |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| namespace mojo { |
| namespace test { |
| namespace { |
| |
| // An implementation of sample::Provider used on the server side. |
| // It only implements one of the methods: EchoInt(). |
| // All it does is save the values and Callbacks it sees. |
| class InterfaceImpl : public sample::Provider { |
| public: |
| InterfaceImpl() : last_server_value_seen_(0) {} |
| |
| ~InterfaceImpl() override {} |
| |
| // Run's the callback previously saved from the last invocation |
| // of |EchoInt()|. |
| bool RunCallback() { |
| if (callback_saved_) { |
| std::move(callback_saved_).Run(last_server_value_seen_); |
| return true; |
| } |
| return false; |
| } |
| |
| // Delete's the previously saved callback. |
| void DeleteCallback() { callback_saved_.Reset(); } |
| |
| // sample::Provider implementation |
| |
| // Saves its two input values in member variables and does nothing else. |
| void EchoInt(int32_t x, EchoIntCallback callback) override { |
| last_server_value_seen_ = x; |
| callback_saved_ = std::move(callback); |
| if (closure_) |
| std::move(closure_).Run(); |
| } |
| |
| void EchoString(const std::string& a, EchoStringCallback callback) override { |
| NOTREACHED() << "Not implemented."; |
| } |
| |
| void EchoStrings(const std::string& a, |
| const std::string& b, |
| EchoStringsCallback callback) override { |
| NOTREACHED() << "Not implemented."; |
| } |
| |
| void EchoMessagePipeHandle(ScopedMessagePipeHandle a, |
| EchoMessagePipeHandleCallback callback) override { |
| NOTREACHED() << "Not implemented."; |
| } |
| |
| void EchoEnum(sample::Enum a, EchoEnumCallback callback) override { |
| NOTREACHED() << "Not implemented."; |
| } |
| |
| void ResetLastServerValueSeen() { last_server_value_seen_ = 0; } |
| |
| int32_t last_server_value_seen() const { return last_server_value_seen_; } |
| |
| void set_closure(base::OnceClosure closure) { closure_ = std::move(closure); } |
| |
| private: |
| int32_t last_server_value_seen_; |
| EchoIntCallback callback_saved_; |
| base::OnceClosure closure_; |
| }; |
| |
| class ReceiverCallbackTest : public testing::Test { |
| public: |
| ReceiverCallbackTest() {} |
| ~ReceiverCallbackTest() override {} |
| |
| protected: |
| int32_t last_client_callback_value_seen_; |
| Remote<sample::Provider> remote_; |
| |
| void PumpMessages() { base::RunLoop().RunUntilIdle(); } |
| |
| private: |
| base::test::SingleThreadTaskEnvironment task_environment_; |
| }; |
| |
| // Tests that the Remote and the Receiver can communicate with each other |
| // normally. |
| TEST_F(ReceiverCallbackTest, Basic) { |
| // Create the ServerImpl and the Receiver. |
| InterfaceImpl server_impl; |
| Receiver<sample::Provider> receiver(&server_impl, |
| remote_.BindNewPipeAndPassReceiver()); |
| |
| // Initialize the test values. |
| server_impl.ResetLastServerValueSeen(); |
| last_client_callback_value_seen_ = 0; |
| |
| // Invoke the Echo method. |
| base::RunLoop run_loop, run_loop2; |
| server_impl.set_closure(run_loop.QuitClosure()); |
| remote_->EchoInt(7, base::BindLambdaForTesting([&](int32_t value) { |
| last_client_callback_value_seen_ = value; |
| run_loop2.Quit(); |
| })); |
| run_loop.Run(); |
| |
| // Check that server saw the correct value, but the client has not yet. |
| EXPECT_EQ(7, server_impl.last_server_value_seen()); |
| EXPECT_EQ(0, last_client_callback_value_seen_); |
| |
| // Now run the Callback. |
| server_impl.RunCallback(); |
| run_loop2.Run(); |
| |
| // Check that the client has now seen the correct value. |
| EXPECT_EQ(7, last_client_callback_value_seen_); |
| |
| // Initialize the test values again. |
| server_impl.ResetLastServerValueSeen(); |
| last_client_callback_value_seen_ = 0; |
| |
| // Invoke the Echo method again. |
| base::RunLoop run_loop3, run_loop4; |
| server_impl.set_closure(run_loop3.QuitClosure()); |
| remote_->EchoInt(13, base::BindLambdaForTesting([&](int32_t value) { |
| last_client_callback_value_seen_ = value; |
| run_loop4.Quit(); |
| })); |
| run_loop3.Run(); |
| |
| // Check that server saw the correct value, but the client has not yet. |
| EXPECT_EQ(13, server_impl.last_server_value_seen()); |
| EXPECT_EQ(0, last_client_callback_value_seen_); |
| |
| // Now run the Callback again. |
| server_impl.RunCallback(); |
| run_loop4.Run(); |
| |
| // Check that the client has now seen the correct value again. |
| EXPECT_EQ(13, last_client_callback_value_seen_); |
| } |
| |
| // Tests that running the Callback after the Receiver has been deleted |
| // results in a clean failure. |
| TEST_F(ReceiverCallbackTest, DeleteReceiverThenRunCallback) { |
| // Create the ServerImpl. |
| InterfaceImpl server_impl; |
| base::RunLoop run_loop; |
| { |
| // Create the receiver in an inner scope so it can be deleted first. |
| Receiver<sample::Provider> receiver(&server_impl, |
| remote_.BindNewPipeAndPassReceiver()); |
| remote_.set_disconnect_handler(run_loop.QuitClosure()); |
| |
| // Initialize the test values. |
| server_impl.ResetLastServerValueSeen(); |
| last_client_callback_value_seen_ = 0; |
| |
| // Invoke the Echo method. |
| base::RunLoop run_loop2; |
| server_impl.set_closure(run_loop2.QuitClosure()); |
| remote_->EchoInt(7, base::BindLambdaForTesting([&](int32_t value) { |
| last_client_callback_value_seen_ = value; |
| })); |
| run_loop2.Run(); |
| } |
| // The receiver has now been destroyed and its pipe endpoint is closed. |
| |
| // Check that server saw the correct value, but the client has not yet. |
| EXPECT_EQ(7, server_impl.last_server_value_seen()); |
| EXPECT_EQ(0, last_client_callback_value_seen_); |
| |
| // Now try to run the Callback. This should do nothing since the pipe |
| // is closed. |
| EXPECT_TRUE(server_impl.RunCallback()); |
| PumpMessages(); |
| |
| // Check that the client has still not seen the correct value. |
| EXPECT_EQ(0, last_client_callback_value_seen_); |
| |
| // Attempt to invoke the method again and confirm that an error was |
| // encountered. |
| remote_->EchoInt(13, base::BindLambdaForTesting([&](int32_t value) { |
| last_client_callback_value_seen_ = value; |
| run_loop.Quit(); |
| })); |
| run_loop.Run(); |
| EXPECT_FALSE(remote_.is_connected()); |
| } |
| |
| // Tests that deleting a Callback without running it after the corresponding |
| // Receiver has already been deleted does not result in a crash. |
| TEST_F(ReceiverCallbackTest, DeleteReceiverThenDeleteCallback) { |
| // Create the ServerImpl. |
| InterfaceImpl server_impl; |
| { |
| // Create the receiver in an inner scope so it can be deleted first. |
| Receiver<sample::Provider> receiver(&server_impl, |
| remote_.BindNewPipeAndPassReceiver()); |
| |
| // Initialize the test values. |
| server_impl.ResetLastServerValueSeen(); |
| last_client_callback_value_seen_ = 0; |
| |
| // Invoke the Echo method. |
| base::RunLoop run_loop; |
| server_impl.set_closure(run_loop.QuitClosure()); |
| remote_->EchoInt(7, base::BindLambdaForTesting([&](int32_t value) { |
| last_client_callback_value_seen_ = value; |
| })); |
| run_loop.Run(); |
| } |
| // The receiver has now been destroyed and its pipe endpoint is closed. |
| |
| // Check that server saw the correct value, but the client has not yet. |
| EXPECT_EQ(7, server_impl.last_server_value_seen()); |
| EXPECT_EQ(0, last_client_callback_value_seen_); |
| |
| // Delete the callback without running it. This should not |
| // cause a problem because the infrastructure can detect that the |
| // receiver has already been destroyed. |
| server_impl.DeleteCallback(); |
| } |
| |
| // Tests that closing a Receiver allows us to delete a callback |
| // without running it without encountering a crash. |
| TEST_F(ReceiverCallbackTest, ResetReceiverBeforeDeletingCallback) { |
| // Create the ServerImpl and the Receiver. |
| InterfaceImpl server_impl; |
| Receiver<sample::Provider> receiver(&server_impl, |
| remote_.BindNewPipeAndPassReceiver()); |
| |
| // Initialize the test values. |
| server_impl.ResetLastServerValueSeen(); |
| last_client_callback_value_seen_ = 0; |
| |
| // Invoke the Echo method. |
| base::RunLoop run_loop; |
| server_impl.set_closure(run_loop.QuitClosure()); |
| remote_->EchoInt(7, base::BindLambdaForTesting([&](int32_t value) { |
| last_client_callback_value_seen_ = value; |
| })); |
| run_loop.Run(); |
| |
| // Check that server saw the correct value, but the client has not yet. |
| EXPECT_EQ(7, server_impl.last_server_value_seen()); |
| EXPECT_EQ(0, last_client_callback_value_seen_); |
| |
| // Now disconnect the Receiver. |
| receiver.reset(); |
| |
| // Delete the callback without running it. This should not |
| // cause a crash because the infrastructure can detect that the |
| // receiver has already been closed. |
| server_impl.DeleteCallback(); |
| |
| // Check that the client has still not seen the correct value. |
| EXPECT_EQ(0, last_client_callback_value_seen_); |
| } |
| |
| // Tests that deleting a Callback without using it before the |
| // Receiver has been destroyed or closed results in a DCHECK. |
| TEST_F(ReceiverCallbackTest, DeleteCallbackBeforeReceiverDeathTest) { |
| // Create the ServerImpl and the Receiver. |
| InterfaceImpl server_impl; |
| Receiver<sample::Provider> receiver(&server_impl, |
| remote_.BindNewPipeAndPassReceiver()); |
| |
| // Initialize the test values. |
| server_impl.ResetLastServerValueSeen(); |
| last_client_callback_value_seen_ = 0; |
| |
| // Invoke the Echo method. |
| base::RunLoop run_loop; |
| server_impl.set_closure(run_loop.QuitClosure()); |
| remote_->EchoInt(7, base::BindLambdaForTesting([&](int32_t value) { |
| last_client_callback_value_seen_ = value; |
| })); |
| run_loop.Run(); |
| |
| // Check that server saw the correct value, but the client has not yet. |
| EXPECT_EQ(7, server_impl.last_server_value_seen()); |
| EXPECT_EQ(0, last_client_callback_value_seen_); |
| |
| EXPECT_DCHECK_DEATH(server_impl.DeleteCallback()); |
| } |
| |
| } // namespace |
| } // namespace test |
| } // namespace mojo |