Chromium Code Reviews| Index: mojo/public/cpp/bindings/tests/binding_callback_test.cc |
| diff --git a/mojo/public/cpp/bindings/tests/binding_callback_test.cc b/mojo/public/cpp/bindings/tests/binding_callback_test.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..1fc917cab9a61a161c25cd9e819181b39b597594 |
| --- /dev/null |
| +++ b/mojo/public/cpp/bindings/tests/binding_callback_test.cc |
| @@ -0,0 +1,281 @@ |
| +// 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 "mojo/public/cpp/bindings/interface_ptr.h" |
|
jamesr
2015/03/16 21:05:56
nit: blank line after copyright header
rudominer
2015/03/16 22:57:19
Done.
|
| +#include "mojo/public/cpp/environment/environment.h" |
| +#include "mojo/public/cpp/test_support/test_support.h" |
| +#include "mojo/public/cpp/utility/run_loop.h" |
| +#include "mojo/public/interfaces/bindings/tests/binding_callback_test_interfaces.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 Binding. If a Callback is deleted before |
| +// being used we DCHECK fail--unless the associated Binding has already |
| +// been closed or deleted. This contract must be explained to the Mojo |
| +// application developer. For example it is his responsibility to ensure that |
|
jamesr
2015/03/16 21:05:57
nit: avoid gendered pronouns like "his" in documen
rudominer
2015/03/16 22:57:19
Done.
|
| +// the Binding is destroyed before an unused Callback is destroyed. |
| +// |
| +/////////////////////////////////////////////////////////////////////////////// |
| + |
| +namespace mojo { |
| +namespace test { |
| +namespace { |
| + |
| +// A Runnable object that saves the last value it sees via the |
| +// provided int32_t*. Used on the client side. |
| +class ValueSaver { |
| + public: |
| + explicit ValueSaver(int32_t* last_value_seen) |
| + : last_value_seen_(last_value_seen) {} |
| + void Run(int32_t x) const { *last_value_seen_ = x; } |
| + |
| + private: |
| + mutable int32_t* last_value_seen_; |
|
jamesr
2015/03/16 21:05:57
this can be const as the pointer value isn't chang
rudominer
2015/03/16 22:57:19
Done.
|
| +}; |
| + |
| +// An implementation of BindingCallbackTestInterface used on the server side. |
| +// All it does is save the values and Callbacks it sees. |
| +class BindingCallbackTestInterfaceImpl : public BindingCallbackTestInterface { |
| + public: |
| + BindingCallbackTestInterfaceImpl() |
| + : last_server_value_seen(0), |
| + callback_saved(new Callback<void(int32_t)>()) {} |
| + |
| + ~BindingCallbackTestInterfaceImpl() override { |
| + if (callback_saved) { |
| + delete callback_saved; |
| + } |
| + } |
| + |
| + // Run's the callback previously saved from the last invocation |
| + // of |EchoInt()|. |
| + bool RunCallback() { |
| + if (callback_saved) { |
| + callback_saved->Run(last_server_value_seen); |
| + return true; |
| + } |
| + return false; |
| + } |
| + |
| + // Delete's the previously saved callback. |
| + void DeleteCallback() { |
| + delete callback_saved; |
| + callback_saved = nullptr; |
| + } |
| + |
| + // Saves its two input values in member variables and does nothing else. |
| + void EchoInt(int32_t x, const Callback<void(int32_t)>& callback) { |
| + last_server_value_seen = x; |
| + *callback_saved = callback; |
| + } |
| + |
| + int last_server_value_seen; |
|
jamesr
2015/03/16 21:05:56
s/int/int32_t/
if you want these to be private si
rudominer
2015/03/16 22:57:19
Done.
|
| + Callback<void(int32_t)>* callback_saved; |
| +}; |
| + |
| +class BindingCallbackTest : public testing::Test { |
| + public: |
| + ~BindingCallbackTest() override {} |
| + |
| + protected: |
| + int last_client_callback_value_seen_; |
| + ScopedMessagePipeHandle handle_client_; |
|
jamesr
2015/03/16 21:05:56
can these have more specific types (i.e. are these
rudominer
2015/03/16 22:57:20
Help me out here. I'm not familiar with any more s
|
| + ScopedMessagePipeHandle handle_server_; |
| + BindingCallbackTestInterfacePtr interface_ptr_; |
| + |
| + void PumpMessages() { loop_.RunUntilIdle(); } |
| + void SetUp() { |
| + CreateMessagePipe(nullptr, &handle_client_, &handle_server_); |
| + // Create the client InterfacePtr. |
| + interface_ptr_ = |
| + MakeProxy<BindingCallbackTestInterface>(handle_client_.Pass()); |
| + } |
| + |
| + private: |
| + Environment env_; |
| + RunLoop loop_; |
| +}; |
| + |
| +// Tests that the InterfacePtr and the Binding can communicate with each |
| +// other normally. |
| +TEST_F(BindingCallbackTest, Basic) { |
| + // Create the ServerImpl and the Binding. |
| + BindingCallbackTestInterfaceImpl server_impl; |
| + Binding<BindingCallbackTestInterface> binding( |
| + &server_impl, |
| + MakeRequest<BindingCallbackTestInterface>(handle_server_.Pass())); |
| + |
| + // Initialize the test values. |
| + server_impl.last_server_value_seen = 0; |
| + last_client_callback_value_seen_ = 0; |
| + |
| + // Invoke the Echo method. |
| + interface_ptr_->EchoInt(7, ValueSaver(&last_client_callback_value_seen_)); |
| + PumpMessages(); |
| + |
| + // 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(); |
| + PumpMessages(); |
| + |
| + // 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.last_server_value_seen = 0; |
| + last_client_callback_value_seen_ = 0; |
| + |
| + // Invoke the Echo method again. |
| + interface_ptr_->EchoInt(13, ValueSaver(&last_client_callback_value_seen_)); |
| + PumpMessages(); |
| + |
| + // 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(); |
| + PumpMessages(); |
| + |
| + // 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 Binding has been deleted |
| +// results in a clean failure. |
| +TEST_F(BindingCallbackTest, DeleteBindingThenRunCallback) { |
| + // Create the ServerImpl. |
| + BindingCallbackTestInterfaceImpl server_impl; |
| + { |
| + // Create the binding in an inner scope so it can be deleted first. |
| + Binding<BindingCallbackTestInterface> binding( |
| + &server_impl, |
| + MakeRequest<BindingCallbackTestInterface>(handle_server_.Pass())); |
| + |
| + // Initialize the test values. |
| + server_impl.last_server_value_seen = 0; |
| + last_client_callback_value_seen_ = 0; |
| + |
| + // Invoke the Echo method. |
| + interface_ptr_->EchoInt(7, ValueSaver(&last_client_callback_value_seen_)); |
| + PumpMessages(); |
| + } |
| + // The binding has now been destroyed and the pipe 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. |
| + interface_ptr_->EchoInt(13, ValueSaver(&last_client_callback_value_seen_)); |
| + PumpMessages(); |
| + EXPECT_TRUE(interface_ptr_.encountered_error()); |
| +} |
| + |
| +// Tests that deleting a Callback without running it after the corresponding |
| +// binding has already been deleted does not result in a crash. |
| +TEST_F(BindingCallbackTest, DeleteBindingThenDeleteCallback) { |
| + // Create the ServerImpl. |
| + BindingCallbackTestInterfaceImpl server_impl; |
| + { |
| + // Create the binding in an inner scope so it can be deleted first. |
| + Binding<BindingCallbackTestInterface> binding( |
| + &server_impl, |
| + MakeRequest<BindingCallbackTestInterface>(handle_server_.Pass())); |
| + |
| + // Initialize the test values. |
| + server_impl.last_server_value_seen = 0; |
| + last_client_callback_value_seen_ = 0; |
| + |
| + // Invoke the Echo method. |
| + interface_ptr_->EchoInt(7, ValueSaver(&last_client_callback_value_seen_)); |
| + PumpMessages(); |
| + } |
| + // The binding has now been destroyed and the pipe 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 insfrastructure can detect that the |
| + // binding has already been destroyed and the pipe is closed. |
| + server_impl.DeleteCallback(); |
| +} |
| + |
| +// Tests that closing a Binding allows us to delete a callback |
| +// without running it without encountering a crash. |
| +TEST_F(BindingCallbackTest, CloseBindingBeforeDeletingCallback) { |
| + // Create the ServerImpl and the Binding. |
| + BindingCallbackTestInterfaceImpl server_impl; |
| + Binding<BindingCallbackTestInterface> binding( |
| + &server_impl, |
| + MakeRequest<BindingCallbackTestInterface>(handle_server_.Pass())); |
| + |
| + // Initialize the test values. |
| + server_impl.last_server_value_seen = 0; |
| + last_client_callback_value_seen_ = 0; |
| + |
| + // Invoke the Echo method. |
| + interface_ptr_->EchoInt(7, ValueSaver(&last_client_callback_value_seen_)); |
| + PumpMessages(); |
| + |
| + // 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 close the Binding. |
| + binding.Close(); |
| + |
| + // Delete the callback without running it. This should not |
| + // cause a crash because the insfrastructure can detect that the |
| + // binding 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 |
| +// Binding has been destroyed or closed results in a DCHECK. |
| +TEST_F(BindingCallbackTest, DeleteCallbackBeforeBindingDeathTest) { |
| + // Create the ServerImpl and the Binding. |
| + BindingCallbackTestInterfaceImpl server_impl; |
| + Binding<BindingCallbackTestInterface> binding( |
| + &server_impl, |
| + MakeRequest<BindingCallbackTestInterface>(handle_server_.Pass())); |
| + |
| + // Initialize the test values. |
| + server_impl.last_server_value_seen = 0; |
| + last_client_callback_value_seen_ = 0; |
| + |
| + // Invoke the Echo method. |
| + interface_ptr_->EchoInt(7, ValueSaver(&last_client_callback_value_seen_)); |
| + PumpMessages(); |
| + |
| + // 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 cause a crash |
| + // due to a DCHECK. |
| + EXPECT_DEATH(server_impl.DeleteCallback(), "Check failed: was_run_"); |
| +} |
| + |
| +} // namespace |
| +} // namespace test |
| +} // namespace mojo |