| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // Note: This file tests both binding.h (mojo::Binding) and strong_binding.h |
| 6 // (mojo::StrongBinding). |
| 7 |
| 5 #include "mojo/public/cpp/bindings/binding.h" | 8 #include "mojo/public/cpp/bindings/binding.h" |
| 9 #include "mojo/public/cpp/bindings/strong_binding.h" |
| 6 #include "mojo/public/cpp/environment/environment.h" | 10 #include "mojo/public/cpp/environment/environment.h" |
| 11 #include "mojo/public/cpp/system/macros.h" |
| 7 #include "mojo/public/cpp/utility/run_loop.h" | 12 #include "mojo/public/cpp/utility/run_loop.h" |
| 8 #include "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.h" | 13 #include "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.h" |
| 9 #include "mojo/public/interfaces/bindings/tests/sample_service.mojom.h" | 14 #include "mojo/public/interfaces/bindings/tests/sample_service.mojom.h" |
| 10 #include "testing/gtest/include/gtest/gtest.h" | 15 #include "testing/gtest/include/gtest/gtest.h" |
| 11 | 16 |
| 12 namespace mojo { | 17 namespace mojo { |
| 13 namespace { | 18 namespace { |
| 14 | 19 |
| 20 class BindingTestBase : public testing::Test { |
| 21 public: |
| 22 BindingTestBase() {} |
| 23 ~BindingTestBase() override {} |
| 24 |
| 25 RunLoop& loop() { return loop_; } |
| 26 |
| 27 private: |
| 28 Environment env_; |
| 29 RunLoop loop_; |
| 30 |
| 31 MOJO_DISALLOW_COPY_AND_ASSIGN(BindingTestBase); |
| 32 }; |
| 33 |
| 15 class ServiceImpl : public sample::Service { | 34 class ServiceImpl : public sample::Service { |
| 16 public: | 35 public: |
| 17 ServiceImpl() {} | 36 explicit ServiceImpl(bool* was_deleted = nullptr) |
| 18 ~ServiceImpl() override {} | 37 : was_deleted_(was_deleted) {} |
| 38 ~ServiceImpl() override { |
| 39 if (was_deleted_) |
| 40 *was_deleted_ = true; |
| 41 } |
| 19 | 42 |
| 20 private: | 43 private: |
| 21 // sample::Service implementation | 44 // sample::Service implementation |
| 22 void Frobinate(sample::FooPtr foo, | 45 void Frobinate(sample::FooPtr foo, |
| 23 BazOptions options, | 46 BazOptions options, |
| 24 sample::PortPtr port, | 47 sample::PortPtr port, |
| 25 const FrobinateCallback& callback) override { | 48 const FrobinateCallback& callback) override { |
| 26 callback.Run(1); | 49 callback.Run(1); |
| 27 } | 50 } |
| 28 void GetPort(InterfaceRequest<sample::Port> port) override {} | 51 void GetPort(InterfaceRequest<sample::Port> port) override {} |
| 52 |
| 53 bool* const was_deleted_; |
| 54 |
| 55 MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceImpl); |
| 29 }; | 56 }; |
| 30 | 57 |
| 31 class IntegerAccessorImpl : public sample::IntegerAccessor { | 58 // BindingTest ----------------------------------------------------------------- |
| 32 public: | |
| 33 IntegerAccessorImpl() {} | |
| 34 ~IntegerAccessorImpl() override {} | |
| 35 | 59 |
| 36 private: | 60 using BindingTest = BindingTestBase; |
| 37 // sample::IntegerAccessor implementation. | |
| 38 void GetInteger(const GetIntegerCallback& callback) override { | |
| 39 callback.Run(1, sample::ENUM_VALUE); | |
| 40 } | |
| 41 void SetInteger(int64_t data, sample::Enum type) override {} | |
| 42 }; | |
| 43 | 61 |
| 44 class BindingTest : public testing::Test { | 62 TEST_F(BindingTest, Close) { |
| 45 public: | 63 bool called = false; |
| 46 BindingTest() {} | 64 sample::ServicePtr ptr; |
| 47 ~BindingTest() override {} | 65 auto request = GetProxy(&ptr); |
| 66 ptr.set_connection_error_handler([&called]() { called = true; }); |
| 67 ServiceImpl impl; |
| 68 Binding<sample::Service> binding(&impl, request.Pass()); |
| 48 | 69 |
| 49 protected: | 70 binding.Close(); |
| 50 Environment env_; | 71 EXPECT_FALSE(called); |
| 51 RunLoop loop_; | 72 loop().RunUntilIdle(); |
| 52 }; | 73 EXPECT_TRUE(called); |
| 74 } |
| 53 | 75 |
| 54 // Tests that destroying a mojo::Binding closes the bound message pipe handle. | 76 // Tests that destroying a mojo::Binding closes the bound message pipe handle. |
| 55 TEST_F(BindingTest, DestroyClosesMessagePipe) { | 77 TEST_F(BindingTest, DestroyClosesMessagePipe) { |
| 56 bool encountered_error = false; | 78 bool encountered_error = false; |
| 57 ServiceImpl impl; | 79 ServiceImpl impl; |
| 58 sample::ServicePtr ptr; | 80 sample::ServicePtr ptr; |
| 59 auto request = GetProxy(&ptr); | 81 auto request = GetProxy(&ptr); |
| 60 ptr.set_connection_error_handler( | 82 ptr.set_connection_error_handler( |
| 61 [&encountered_error]() { encountered_error = true; }); | 83 [&encountered_error]() { encountered_error = true; }); |
| 62 bool called = false; | 84 bool called = false; |
| 63 auto called_cb = [&called](int32_t result) { called = true; }; | 85 auto called_cb = [&called](int32_t result) { called = true; }; |
| 64 { | 86 { |
| 65 Binding<sample::Service> binding(&impl, request.Pass()); | 87 Binding<sample::Service> binding(&impl, request.Pass()); |
| 66 ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, | 88 ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, |
| 67 called_cb); | 89 called_cb); |
| 68 loop_.RunUntilIdle(); | 90 loop().RunUntilIdle(); |
| 69 EXPECT_TRUE(called); | 91 EXPECT_TRUE(called); |
| 70 EXPECT_FALSE(encountered_error); | 92 EXPECT_FALSE(encountered_error); |
| 71 } | 93 } |
| 72 // Now that the Binding is out of scope we should detect an error on the other | 94 // Now that the Binding is out of scope we should detect an error on the other |
| 73 // end of the pipe. | 95 // end of the pipe. |
| 74 loop_.RunUntilIdle(); | 96 loop().RunUntilIdle(); |
| 75 EXPECT_TRUE(encountered_error); | 97 EXPECT_TRUE(encountered_error); |
| 76 | 98 |
| 77 // And calls should fail. | 99 // And calls should fail. |
| 78 called = false; | 100 called = false; |
| 79 ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, | 101 ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, |
| 80 called_cb); | 102 called_cb); |
| 81 loop_.RunUntilIdle(); | 103 loop().RunUntilIdle(); |
| 82 EXPECT_FALSE(called); | 104 EXPECT_FALSE(called); |
| 83 } | 105 } |
| 84 | 106 |
| 107 // Tests that the binding's connection error handler gets called when the other |
| 108 // end is closed. |
| 109 TEST_F(BindingTest, ConnectionError) { |
| 110 bool called = false; |
| 111 { |
| 112 ServiceImpl impl; |
| 113 sample::ServicePtr ptr; |
| 114 Binding<sample::Service> binding(&impl, GetProxy(&ptr)); |
| 115 binding.set_connection_error_handler([&called]() { called = true; }); |
| 116 ptr.reset(); |
| 117 EXPECT_FALSE(called); |
| 118 loop().RunUntilIdle(); |
| 119 EXPECT_TRUE(called); |
| 120 // We want to make sure that it isn't called again during destruction. |
| 121 called = false; |
| 122 } |
| 123 EXPECT_FALSE(called); |
| 124 } |
| 125 |
| 126 // Tests that calling Close doesn't result in the connection error handler being |
| 127 // called. |
| 128 TEST_F(BindingTest, CloseDoesntCallConnectionErrorHandler) { |
| 129 ServiceImpl impl; |
| 130 sample::ServicePtr ptr; |
| 131 Binding<sample::Service> binding(&impl, GetProxy(&ptr)); |
| 132 bool called = false; |
| 133 binding.set_connection_error_handler([&called]() { called = true; }); |
| 134 binding.Close(); |
| 135 loop().RunUntilIdle(); |
| 136 EXPECT_FALSE(called); |
| 137 |
| 138 // We can also close the other end, and the error handler still won't be |
| 139 // called. |
| 140 ptr.reset(); |
| 141 loop().RunUntilIdle(); |
| 142 EXPECT_FALSE(called); |
| 143 } |
| 144 |
| 145 class ServiceImplWithBinding : public ServiceImpl { |
| 146 public: |
| 147 ServiceImplWithBinding(bool* was_deleted, |
| 148 InterfaceRequest<sample::Service> request) |
| 149 : ServiceImpl(was_deleted), binding_(this, request.Pass()) { |
| 150 binding_.set_connection_error_handler([this]() { delete this; }); |
| 151 } |
| 152 |
| 153 private: |
| 154 Binding<sample::Service> binding_; |
| 155 |
| 156 MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceImplWithBinding); |
| 157 }; |
| 158 |
| 159 // Tests that the binding may be deleted in the connection error handler. |
| 160 TEST_F(BindingTest, SelfDeleteOnConnectionError) { |
| 161 bool was_deleted = false; |
| 162 sample::ServicePtr ptr; |
| 163 // This should delete itself on connection error. |
| 164 new ServiceImplWithBinding(&was_deleted, GetProxy(&ptr)); |
| 165 ptr.reset(); |
| 166 EXPECT_FALSE(was_deleted); |
| 167 loop().RunUntilIdle(); |
| 168 EXPECT_TRUE(was_deleted); |
| 169 } |
| 170 |
| 85 // Tests that explicitly calling Unbind followed by rebinding works. | 171 // Tests that explicitly calling Unbind followed by rebinding works. |
| 86 TEST_F(BindingTest, Unbind) { | 172 TEST_F(BindingTest, Unbind) { |
| 87 ServiceImpl impl; | 173 ServiceImpl impl; |
| 88 sample::ServicePtr ptr; | 174 sample::ServicePtr ptr; |
| 89 Binding<sample::Service> binding(&impl, GetProxy(&ptr)); | 175 Binding<sample::Service> binding(&impl, GetProxy(&ptr)); |
| 90 | 176 |
| 91 bool called = false; | 177 bool called = false; |
| 92 auto called_cb = [&called](int32_t result) { called = true; }; | 178 auto called_cb = [&called](int32_t result) { called = true; }; |
| 93 ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, | 179 ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, |
| 94 called_cb); | 180 called_cb); |
| 95 loop_.RunUntilIdle(); | 181 loop().RunUntilIdle(); |
| 96 EXPECT_TRUE(called); | 182 EXPECT_TRUE(called); |
| 97 | 183 |
| 98 called = false; | 184 called = false; |
| 99 auto request = binding.Unbind(); | 185 auto request = binding.Unbind(); |
| 100 EXPECT_FALSE(binding.is_bound()); | 186 EXPECT_FALSE(binding.is_bound()); |
| 101 // All calls should fail when not bound... | 187 // All calls should fail when not bound... |
| 102 ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, | 188 ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, |
| 103 called_cb); | 189 called_cb); |
| 104 loop_.RunUntilIdle(); | 190 loop().RunUntilIdle(); |
| 105 EXPECT_FALSE(called); | 191 EXPECT_FALSE(called); |
| 106 | 192 |
| 107 called = false; | 193 called = false; |
| 108 binding.Bind(request.Pass()); | 194 binding.Bind(request.Pass()); |
| 109 EXPECT_TRUE(binding.is_bound()); | 195 EXPECT_TRUE(binding.is_bound()); |
| 110 // ...and should succeed again when the rebound. | 196 // ...and should succeed again when the rebound. |
| 111 ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, | 197 ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, |
| 112 called_cb); | 198 called_cb); |
| 113 loop_.RunUntilIdle(); | 199 loop().RunUntilIdle(); |
| 114 EXPECT_TRUE(called); | 200 EXPECT_TRUE(called); |
| 115 } | 201 } |
| 116 | 202 |
| 203 class IntegerAccessorImpl : public sample::IntegerAccessor { |
| 204 public: |
| 205 IntegerAccessorImpl() {} |
| 206 ~IntegerAccessorImpl() override {} |
| 207 |
| 208 private: |
| 209 // sample::IntegerAccessor implementation. |
| 210 void GetInteger(const GetIntegerCallback& callback) override { |
| 211 callback.Run(1, sample::ENUM_VALUE); |
| 212 } |
| 213 void SetInteger(int64_t data, sample::Enum type) override {} |
| 214 |
| 215 MOJO_DISALLOW_COPY_AND_ASSIGN(IntegerAccessorImpl); |
| 216 }; |
| 217 |
| 117 TEST_F(BindingTest, SetInterfacePtrVersion) { | 218 TEST_F(BindingTest, SetInterfacePtrVersion) { |
| 118 IntegerAccessorImpl impl; | 219 IntegerAccessorImpl impl; |
| 119 sample::IntegerAccessorPtr ptr; | 220 sample::IntegerAccessorPtr ptr; |
| 120 Binding<sample::IntegerAccessor> binding(&impl, &ptr); | 221 Binding<sample::IntegerAccessor> binding(&impl, &ptr); |
| 121 EXPECT_EQ(3u, ptr.version()); | 222 EXPECT_EQ(3u, ptr.version()); |
| 122 } | 223 } |
| 123 | 224 |
| 225 // StrongBindingTest ----------------------------------------------------------- |
| 226 |
| 227 using StrongBindingTest = BindingTestBase; |
| 228 |
| 229 // Tests that destroying a mojo::StrongBinding closes the bound message pipe |
| 230 // handle but does *not* destroy the implementation object. |
| 231 TEST_F(StrongBindingTest, DestroyClosesMessagePipe) { |
| 232 bool encountered_error = false; |
| 233 bool was_deleted = false; |
| 234 ServiceImpl impl(&was_deleted); |
| 235 sample::ServicePtr ptr; |
| 236 auto request = GetProxy(&ptr); |
| 237 ptr.set_connection_error_handler( |
| 238 [&encountered_error]() { encountered_error = true; }); |
| 239 bool called = false; |
| 240 auto called_cb = [&called](int32_t result) { called = true; }; |
| 241 { |
| 242 StrongBinding<sample::Service> binding(&impl, request.Pass()); |
| 243 ptr->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr, |
| 244 called_cb); |
| 245 loop().RunUntilIdle(); |
| 246 EXPECT_TRUE(called); |
| 247 EXPECT_FALSE(encountered_error); |
| 248 } |
| 249 // Now that the StrongBinding is out of scope we should detect an error on the |
| 250 // other end of the pipe. |
| 251 loop().RunUntilIdle(); |
| 252 EXPECT_TRUE(encountered_error); |
| 253 // But destroying the StrongBinding doesn't destroy the object. |
| 254 ASSERT_FALSE(was_deleted); |
| 255 } |
| 256 |
| 257 class ServiceImplWithStrongBinding : public ServiceImpl { |
| 258 public: |
| 259 ServiceImplWithStrongBinding(bool* was_deleted, |
| 260 InterfaceRequest<sample::Service> request) |
| 261 : ServiceImpl(was_deleted), binding_(this, request.Pass()) {} |
| 262 |
| 263 StrongBinding<sample::Service>& binding() { return binding_; } |
| 264 |
| 265 private: |
| 266 StrongBinding<sample::Service> binding_; |
| 267 |
| 268 MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceImplWithStrongBinding); |
| 269 }; |
| 270 |
| 271 // Tests the typical case, where the implementation object owns the |
| 272 // StrongBinding (and should be destroyed on connection error). |
| 273 TEST_F(StrongBindingTest, ConnectionErrorDestroysImpl) { |
| 274 sample::ServicePtr ptr; |
| 275 bool was_deleted = false; |
| 276 // Will delete itself. |
| 277 new ServiceImplWithBinding(&was_deleted, GetProxy(&ptr)); |
| 278 |
| 279 loop().RunUntilIdle(); |
| 280 EXPECT_FALSE(was_deleted); |
| 281 |
| 282 ptr.reset(); |
| 283 EXPECT_FALSE(was_deleted); |
| 284 loop().RunUntilIdle(); |
| 285 EXPECT_TRUE(was_deleted); |
| 286 } |
| 287 |
| 288 // Tests that even when the implementation object owns the StrongBinding, that |
| 289 // the implementation can still be deleted (which should result in the message |
| 290 // pipe being closed). Also checks that the connection error handler doesn't get |
| 291 // called. |
| 292 TEST_F(StrongBindingTest, ExplicitDeleteImpl) { |
| 293 bool ptr_error_handler_called = false; |
| 294 sample::ServicePtr ptr; |
| 295 auto request = GetProxy(&ptr); |
| 296 ptr.set_connection_error_handler( |
| 297 [&ptr_error_handler_called]() { ptr_error_handler_called = true; }); |
| 298 bool was_deleted = false; |
| 299 ServiceImplWithStrongBinding* impl = |
| 300 new ServiceImplWithStrongBinding(&was_deleted, request.Pass()); |
| 301 bool binding_error_handler_called = false; |
| 302 impl->binding().set_connection_error_handler( |
| 303 [&binding_error_handler_called]() { |
| 304 binding_error_handler_called = true; |
| 305 }); |
| 306 |
| 307 loop().RunUntilIdle(); |
| 308 EXPECT_FALSE(ptr_error_handler_called); |
| 309 EXPECT_FALSE(was_deleted); |
| 310 |
| 311 delete impl; |
| 312 EXPECT_FALSE(ptr_error_handler_called); |
| 313 EXPECT_TRUE(was_deleted); |
| 314 was_deleted = false; // It shouldn't be double-deleted! |
| 315 loop().RunUntilIdle(); |
| 316 EXPECT_TRUE(ptr_error_handler_called); |
| 317 EXPECT_FALSE(was_deleted); |
| 318 |
| 319 EXPECT_FALSE(binding_error_handler_called); |
| 320 } |
| 321 |
| 124 } // namespace | 322 } // namespace |
| 125 } // mojo | 323 } // mojo |
| OLD | NEW |