| Index: third_party/mojo/src/mojo/public/cpp/bindings/tests/binding_unittest.cc
|
| diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/tests/binding_unittest.cc b/third_party/mojo/src/mojo/public/cpp/bindings/tests/binding_unittest.cc
|
| index ef232797a43cd12e79e8f7f55b4079c1d4b30a07..59d250199812f8c00e2b76cb32d513144e9eaf76 100644
|
| --- a/third_party/mojo/src/mojo/public/cpp/bindings/tests/binding_unittest.cc
|
| +++ b/third_party/mojo/src/mojo/public/cpp/bindings/tests/binding_unittest.cc
|
| @@ -2,8 +2,13 @@
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| +// Note: This file tests both binding.h (mojo::Binding) and strong_binding.h
|
| +// (mojo::StrongBinding).
|
| +
|
| #include "mojo/public/cpp/bindings/binding.h"
|
| +#include "mojo/public/cpp/bindings/strong_binding.h"
|
| #include "mojo/public/cpp/environment/environment.h"
|
| +#include "mojo/public/cpp/system/macros.h"
|
| #include "mojo/public/cpp/utility/run_loop.h"
|
| #include "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.h"
|
| #include "mojo/public/interfaces/bindings/tests/sample_service.mojom.h"
|
| @@ -12,10 +17,28 @@
|
| namespace mojo {
|
| namespace {
|
|
|
| +class BindingTestBase : public testing::Test {
|
| + public:
|
| + BindingTestBase() {}
|
| + ~BindingTestBase() override {}
|
| +
|
| + RunLoop& loop() { return loop_; }
|
| +
|
| + private:
|
| + Environment env_;
|
| + RunLoop loop_;
|
| +
|
| + MOJO_DISALLOW_COPY_AND_ASSIGN(BindingTestBase);
|
| +};
|
| +
|
| class ServiceImpl : public sample::Service {
|
| public:
|
| - ServiceImpl() {}
|
| - ~ServiceImpl() override {}
|
| + explicit ServiceImpl(bool* was_deleted = nullptr)
|
| + : was_deleted_(was_deleted) {}
|
| + ~ServiceImpl() override {
|
| + if (was_deleted_)
|
| + *was_deleted_ = true;
|
| + }
|
|
|
| private:
|
| // sample::Service implementation
|
| @@ -26,30 +49,29 @@ class ServiceImpl : public sample::Service {
|
| callback.Run(1);
|
| }
|
| void GetPort(InterfaceRequest<sample::Port> port) override {}
|
| -};
|
|
|
| -class IntegerAccessorImpl : public sample::IntegerAccessor {
|
| - public:
|
| - IntegerAccessorImpl() {}
|
| - ~IntegerAccessorImpl() override {}
|
| + bool* const was_deleted_;
|
|
|
| - private:
|
| - // sample::IntegerAccessor implementation.
|
| - void GetInteger(const GetIntegerCallback& callback) override {
|
| - callback.Run(1, sample::ENUM_VALUE);
|
| - }
|
| - void SetInteger(int64_t data, sample::Enum type) override {}
|
| + MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceImpl);
|
| };
|
|
|
| -class BindingTest : public testing::Test {
|
| - public:
|
| - BindingTest() {}
|
| - ~BindingTest() override {}
|
| +// BindingTest -----------------------------------------------------------------
|
|
|
| - protected:
|
| - Environment env_;
|
| - RunLoop loop_;
|
| -};
|
| +using BindingTest = BindingTestBase;
|
| +
|
| +TEST_F(BindingTest, Close) {
|
| + bool called = false;
|
| + sample::ServicePtr ptr;
|
| + auto request = GetProxy(&ptr);
|
| + ptr.set_connection_error_handler([&called]() { called = true; });
|
| + ServiceImpl impl;
|
| + Binding<sample::Service> binding(&impl, request.Pass());
|
| +
|
| + binding.Close();
|
| + EXPECT_FALSE(called);
|
| + loop().RunUntilIdle();
|
| + EXPECT_TRUE(called);
|
| +}
|
|
|
| // Tests that destroying a mojo::Binding closes the bound message pipe handle.
|
| TEST_F(BindingTest, DestroyClosesMessagePipe) {
|
| @@ -65,23 +87,87 @@ TEST_F(BindingTest, DestroyClosesMessagePipe) {
|
| Binding<sample::Service> binding(&impl, request.Pass());
|
| ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr,
|
| called_cb);
|
| - loop_.RunUntilIdle();
|
| + loop().RunUntilIdle();
|
| EXPECT_TRUE(called);
|
| EXPECT_FALSE(encountered_error);
|
| }
|
| // Now that the Binding is out of scope we should detect an error on the other
|
| // end of the pipe.
|
| - loop_.RunUntilIdle();
|
| + loop().RunUntilIdle();
|
| EXPECT_TRUE(encountered_error);
|
|
|
| // And calls should fail.
|
| called = false;
|
| ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr,
|
| called_cb);
|
| - loop_.RunUntilIdle();
|
| + loop().RunUntilIdle();
|
| + EXPECT_FALSE(called);
|
| +}
|
| +
|
| +// Tests that the binding's connection error handler gets called when the other
|
| +// end is closed.
|
| +TEST_F(BindingTest, ConnectionError) {
|
| + bool called = false;
|
| + {
|
| + ServiceImpl impl;
|
| + sample::ServicePtr ptr;
|
| + Binding<sample::Service> binding(&impl, GetProxy(&ptr));
|
| + binding.set_connection_error_handler([&called]() { called = true; });
|
| + ptr.reset();
|
| + EXPECT_FALSE(called);
|
| + loop().RunUntilIdle();
|
| + EXPECT_TRUE(called);
|
| + // We want to make sure that it isn't called again during destruction.
|
| + called = false;
|
| + }
|
| + EXPECT_FALSE(called);
|
| +}
|
| +
|
| +// Tests that calling Close doesn't result in the connection error handler being
|
| +// called.
|
| +TEST_F(BindingTest, CloseDoesntCallConnectionErrorHandler) {
|
| + ServiceImpl impl;
|
| + sample::ServicePtr ptr;
|
| + Binding<sample::Service> binding(&impl, GetProxy(&ptr));
|
| + bool called = false;
|
| + binding.set_connection_error_handler([&called]() { called = true; });
|
| + binding.Close();
|
| + loop().RunUntilIdle();
|
| + EXPECT_FALSE(called);
|
| +
|
| + // We can also close the other end, and the error handler still won't be
|
| + // called.
|
| + ptr.reset();
|
| + loop().RunUntilIdle();
|
| EXPECT_FALSE(called);
|
| }
|
|
|
| +class ServiceImplWithBinding : public ServiceImpl {
|
| + public:
|
| + ServiceImplWithBinding(bool* was_deleted,
|
| + InterfaceRequest<sample::Service> request)
|
| + : ServiceImpl(was_deleted), binding_(this, request.Pass()) {
|
| + binding_.set_connection_error_handler([this]() { delete this; });
|
| + }
|
| +
|
| + private:
|
| + Binding<sample::Service> binding_;
|
| +
|
| + MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceImplWithBinding);
|
| +};
|
| +
|
| +// Tests that the binding may be deleted in the connection error handler.
|
| +TEST_F(BindingTest, SelfDeleteOnConnectionError) {
|
| + bool was_deleted = false;
|
| + sample::ServicePtr ptr;
|
| + // This should delete itself on connection error.
|
| + new ServiceImplWithBinding(&was_deleted, GetProxy(&ptr));
|
| + ptr.reset();
|
| + EXPECT_FALSE(was_deleted);
|
| + loop().RunUntilIdle();
|
| + EXPECT_TRUE(was_deleted);
|
| +}
|
| +
|
| // Tests that explicitly calling Unbind followed by rebinding works.
|
| TEST_F(BindingTest, Unbind) {
|
| ServiceImpl impl;
|
| @@ -92,7 +178,7 @@ TEST_F(BindingTest, Unbind) {
|
| auto called_cb = [&called](int32_t result) { called = true; };
|
| ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr,
|
| called_cb);
|
| - loop_.RunUntilIdle();
|
| + loop().RunUntilIdle();
|
| EXPECT_TRUE(called);
|
|
|
| called = false;
|
| @@ -101,7 +187,7 @@ TEST_F(BindingTest, Unbind) {
|
| // All calls should fail when not bound...
|
| ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr,
|
| called_cb);
|
| - loop_.RunUntilIdle();
|
| + loop().RunUntilIdle();
|
| EXPECT_FALSE(called);
|
|
|
| called = false;
|
| @@ -110,10 +196,25 @@ TEST_F(BindingTest, Unbind) {
|
| // ...and should succeed again when the rebound.
|
| ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr,
|
| called_cb);
|
| - loop_.RunUntilIdle();
|
| + loop().RunUntilIdle();
|
| EXPECT_TRUE(called);
|
| }
|
|
|
| +class IntegerAccessorImpl : public sample::IntegerAccessor {
|
| + public:
|
| + IntegerAccessorImpl() {}
|
| + ~IntegerAccessorImpl() override {}
|
| +
|
| + private:
|
| + // sample::IntegerAccessor implementation.
|
| + void GetInteger(const GetIntegerCallback& callback) override {
|
| + callback.Run(1, sample::ENUM_VALUE);
|
| + }
|
| + void SetInteger(int64_t data, sample::Enum type) override {}
|
| +
|
| + MOJO_DISALLOW_COPY_AND_ASSIGN(IntegerAccessorImpl);
|
| +};
|
| +
|
| TEST_F(BindingTest, SetInterfacePtrVersion) {
|
| IntegerAccessorImpl impl;
|
| sample::IntegerAccessorPtr ptr;
|
| @@ -121,5 +222,102 @@ TEST_F(BindingTest, SetInterfacePtrVersion) {
|
| EXPECT_EQ(3u, ptr.version());
|
| }
|
|
|
| +// StrongBindingTest -----------------------------------------------------------
|
| +
|
| +using StrongBindingTest = BindingTestBase;
|
| +
|
| +// Tests that destroying a mojo::StrongBinding closes the bound message pipe
|
| +// handle but does *not* destroy the implementation object.
|
| +TEST_F(StrongBindingTest, DestroyClosesMessagePipe) {
|
| + bool encountered_error = false;
|
| + bool was_deleted = false;
|
| + ServiceImpl impl(&was_deleted);
|
| + sample::ServicePtr ptr;
|
| + auto request = GetProxy(&ptr);
|
| + ptr.set_connection_error_handler(
|
| + [&encountered_error]() { encountered_error = true; });
|
| + bool called = false;
|
| + auto called_cb = [&called](int32_t result) { called = true; };
|
| + {
|
| + StrongBinding<sample::Service> binding(&impl, request.Pass());
|
| + ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr,
|
| + called_cb);
|
| + loop().RunUntilIdle();
|
| + EXPECT_TRUE(called);
|
| + EXPECT_FALSE(encountered_error);
|
| + }
|
| + // Now that the StrongBinding is out of scope we should detect an error on the
|
| + // other end of the pipe.
|
| + loop().RunUntilIdle();
|
| + EXPECT_TRUE(encountered_error);
|
| + // But destroying the StrongBinding doesn't destroy the object.
|
| + ASSERT_FALSE(was_deleted);
|
| +}
|
| +
|
| +class ServiceImplWithStrongBinding : public ServiceImpl {
|
| + public:
|
| + ServiceImplWithStrongBinding(bool* was_deleted,
|
| + InterfaceRequest<sample::Service> request)
|
| + : ServiceImpl(was_deleted), binding_(this, request.Pass()) {}
|
| +
|
| + StrongBinding<sample::Service>& binding() { return binding_; }
|
| +
|
| + private:
|
| + StrongBinding<sample::Service> binding_;
|
| +
|
| + MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceImplWithStrongBinding);
|
| +};
|
| +
|
| +// Tests the typical case, where the implementation object owns the
|
| +// StrongBinding (and should be destroyed on connection error).
|
| +TEST_F(StrongBindingTest, ConnectionErrorDestroysImpl) {
|
| + sample::ServicePtr ptr;
|
| + bool was_deleted = false;
|
| + // Will delete itself.
|
| + new ServiceImplWithBinding(&was_deleted, GetProxy(&ptr));
|
| +
|
| + loop().RunUntilIdle();
|
| + EXPECT_FALSE(was_deleted);
|
| +
|
| + ptr.reset();
|
| + EXPECT_FALSE(was_deleted);
|
| + loop().RunUntilIdle();
|
| + EXPECT_TRUE(was_deleted);
|
| +}
|
| +
|
| +// Tests that even when the implementation object owns the StrongBinding, that
|
| +// the implementation can still be deleted (which should result in the message
|
| +// pipe being closed). Also checks that the connection error handler doesn't get
|
| +// called.
|
| +TEST_F(StrongBindingTest, ExplicitDeleteImpl) {
|
| + bool ptr_error_handler_called = false;
|
| + sample::ServicePtr ptr;
|
| + auto request = GetProxy(&ptr);
|
| + ptr.set_connection_error_handler(
|
| + [&ptr_error_handler_called]() { ptr_error_handler_called = true; });
|
| + bool was_deleted = false;
|
| + ServiceImplWithStrongBinding* impl =
|
| + new ServiceImplWithStrongBinding(&was_deleted, request.Pass());
|
| + bool binding_error_handler_called = false;
|
| + impl->binding().set_connection_error_handler(
|
| + [&binding_error_handler_called]() {
|
| + binding_error_handler_called = true;
|
| + });
|
| +
|
| + loop().RunUntilIdle();
|
| + EXPECT_FALSE(ptr_error_handler_called);
|
| + EXPECT_FALSE(was_deleted);
|
| +
|
| + delete impl;
|
| + EXPECT_FALSE(ptr_error_handler_called);
|
| + EXPECT_TRUE(was_deleted);
|
| + was_deleted = false; // It shouldn't be double-deleted!
|
| + loop().RunUntilIdle();
|
| + EXPECT_TRUE(ptr_error_handler_called);
|
| + EXPECT_FALSE(was_deleted);
|
| +
|
| + EXPECT_FALSE(binding_error_handler_called);
|
| +}
|
| +
|
| } // namespace
|
| } // mojo
|
|
|