Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(939)

Unified Diff: mojo/public/cpp/bindings/tests/binding_callback_test.cc

Issue 1003773002: CPP bindings: DCHECK when a Callback is destructed without being invoked (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Fixed BUILD.gn file. Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698