| OLD | NEW |
| (Empty) |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "mojo/public/cpp/bindings/binding.h" | |
| 6 #include "mojo/public/cpp/bindings/error_handler.h" | |
| 7 #include "mojo/public/cpp/bindings/strong_binding.h" | |
| 8 #include "mojo/public/cpp/environment/environment.h" | |
| 9 #include "mojo/public/cpp/utility/run_loop.h" | |
| 10 #include "mojo/public/interfaces/bindings/tests/math_calculator.mojom.h" | |
| 11 #include "mojo/public/interfaces/bindings/tests/sample_service.mojom.h" | |
| 12 #include "testing/gtest/include/gtest/gtest.h" | |
| 13 | |
| 14 namespace mojo { | |
| 15 namespace test { | |
| 16 namespace { | |
| 17 | |
| 18 class ErrorObserver : public ErrorHandler { | |
| 19 public: | |
| 20 ErrorObserver() : encountered_error_(false) {} | |
| 21 | |
| 22 bool encountered_error() const { return encountered_error_; } | |
| 23 | |
| 24 void OnConnectionError() override { encountered_error_ = true; } | |
| 25 | |
| 26 private: | |
| 27 bool encountered_error_; | |
| 28 }; | |
| 29 | |
| 30 class MathCalculatorImpl : public InterfaceImpl<math::Calculator> { | |
| 31 public: | |
| 32 ~MathCalculatorImpl() override {} | |
| 33 | |
| 34 MathCalculatorImpl() : total_(0.0) {} | |
| 35 | |
| 36 void Clear() override { client()->Output(total_); } | |
| 37 | |
| 38 void Add(double value) override { | |
| 39 total_ += value; | |
| 40 client()->Output(total_); | |
| 41 } | |
| 42 | |
| 43 void Multiply(double value) override { | |
| 44 total_ *= value; | |
| 45 client()->Output(total_); | |
| 46 } | |
| 47 | |
| 48 private: | |
| 49 double total_; | |
| 50 }; | |
| 51 | |
| 52 class MathCalculatorUIImpl : public math::CalculatorUI { | |
| 53 public: | |
| 54 explicit MathCalculatorUIImpl(math::CalculatorPtr calculator) | |
| 55 : calculator_(calculator.Pass()), output_(0.0) { | |
| 56 calculator_.set_client(this); | |
| 57 } | |
| 58 | |
| 59 bool WaitForIncomingMethodCall() { | |
| 60 return calculator_.WaitForIncomingMethodCall(); | |
| 61 } | |
| 62 | |
| 63 bool encountered_error() const { return calculator_.encountered_error(); } | |
| 64 | |
| 65 void Add(double value) { calculator_->Add(value); } | |
| 66 | |
| 67 void Subtract(double value) { calculator_->Add(-value); } | |
| 68 | |
| 69 void Multiply(double value) { calculator_->Multiply(value); } | |
| 70 | |
| 71 void Divide(double value) { calculator_->Multiply(1.0 / value); } | |
| 72 | |
| 73 double GetOutput() const { return output_; } | |
| 74 | |
| 75 private: | |
| 76 // math::CalculatorUI implementation: | |
| 77 void Output(double value) override { output_ = value; } | |
| 78 | |
| 79 math::CalculatorPtr calculator_; | |
| 80 double output_; | |
| 81 }; | |
| 82 | |
| 83 class SelfDestructingMathCalculatorUIImpl : public math::CalculatorUI { | |
| 84 public: | |
| 85 explicit SelfDestructingMathCalculatorUIImpl(math::CalculatorPtr calculator) | |
| 86 : calculator_(calculator.Pass()), nesting_level_(0) { | |
| 87 ++num_instances_; | |
| 88 calculator_.set_client(this); | |
| 89 } | |
| 90 | |
| 91 void BeginTest(bool nested) { | |
| 92 nesting_level_ = nested ? 2 : 1; | |
| 93 calculator_->Add(1.0); | |
| 94 } | |
| 95 | |
| 96 static int num_instances() { return num_instances_; } | |
| 97 | |
| 98 private: | |
| 99 ~SelfDestructingMathCalculatorUIImpl() override { --num_instances_; } | |
| 100 | |
| 101 void Output(double value) override { | |
| 102 if (--nesting_level_ > 0) { | |
| 103 // Add some more and wait for re-entrant call to Output! | |
| 104 calculator_->Add(1.0); | |
| 105 RunLoop::current()->RunUntilIdle(); | |
| 106 } else { | |
| 107 delete this; | |
| 108 } | |
| 109 } | |
| 110 | |
| 111 math::CalculatorPtr calculator_; | |
| 112 int nesting_level_; | |
| 113 static int num_instances_; | |
| 114 }; | |
| 115 | |
| 116 // static | |
| 117 int SelfDestructingMathCalculatorUIImpl::num_instances_ = 0; | |
| 118 | |
| 119 class ReentrantServiceImpl : public InterfaceImpl<sample::Service> { | |
| 120 public: | |
| 121 ~ReentrantServiceImpl() override {} | |
| 122 | |
| 123 ReentrantServiceImpl() : call_depth_(0), max_call_depth_(0) {} | |
| 124 | |
| 125 int max_call_depth() { return max_call_depth_; } | |
| 126 | |
| 127 void Frobinate(sample::FooPtr foo, | |
| 128 sample::Service::BazOptions baz, | |
| 129 sample::PortPtr port) override { | |
| 130 max_call_depth_ = std::max(++call_depth_, max_call_depth_); | |
| 131 if (call_depth_ == 1) { | |
| 132 EXPECT_TRUE(WaitForIncomingMethodCall()); | |
| 133 } | |
| 134 call_depth_--; | |
| 135 } | |
| 136 | |
| 137 void GetPort(mojo::InterfaceRequest<sample::Port> port) override {} | |
| 138 | |
| 139 private: | |
| 140 int call_depth_; | |
| 141 int max_call_depth_; | |
| 142 }; | |
| 143 | |
| 144 class InterfacePtrTest : public testing::Test { | |
| 145 public: | |
| 146 ~InterfacePtrTest() override { loop_.RunUntilIdle(); } | |
| 147 | |
| 148 void PumpMessages() { loop_.RunUntilIdle(); } | |
| 149 | |
| 150 private: | |
| 151 Environment env_; | |
| 152 RunLoop loop_; | |
| 153 }; | |
| 154 | |
| 155 TEST_F(InterfacePtrTest, EndToEnd) { | |
| 156 math::CalculatorPtr calc; | |
| 157 BindToProxy(new MathCalculatorImpl(), &calc); | |
| 158 | |
| 159 // Suppose this is instantiated in a process that has pipe1_. | |
| 160 MathCalculatorUIImpl calculator_ui(calc.Pass()); | |
| 161 | |
| 162 calculator_ui.Add(2.0); | |
| 163 calculator_ui.Multiply(5.0); | |
| 164 | |
| 165 PumpMessages(); | |
| 166 | |
| 167 EXPECT_EQ(10.0, calculator_ui.GetOutput()); | |
| 168 } | |
| 169 | |
| 170 TEST_F(InterfacePtrTest, EndToEnd_Synchronous) { | |
| 171 math::CalculatorPtr calc; | |
| 172 MathCalculatorImpl* impl = BindToProxy(new MathCalculatorImpl(), &calc); | |
| 173 | |
| 174 // Suppose this is instantiated in a process that has pipe1_. | |
| 175 MathCalculatorUIImpl calculator_ui(calc.Pass()); | |
| 176 | |
| 177 EXPECT_EQ(0.0, calculator_ui.GetOutput()); | |
| 178 | |
| 179 calculator_ui.Add(2.0); | |
| 180 EXPECT_EQ(0.0, calculator_ui.GetOutput()); | |
| 181 impl->WaitForIncomingMethodCall(); | |
| 182 calculator_ui.WaitForIncomingMethodCall(); | |
| 183 EXPECT_EQ(2.0, calculator_ui.GetOutput()); | |
| 184 | |
| 185 calculator_ui.Multiply(5.0); | |
| 186 EXPECT_EQ(2.0, calculator_ui.GetOutput()); | |
| 187 impl->WaitForIncomingMethodCall(); | |
| 188 calculator_ui.WaitForIncomingMethodCall(); | |
| 189 EXPECT_EQ(10.0, calculator_ui.GetOutput()); | |
| 190 } | |
| 191 | |
| 192 TEST_F(InterfacePtrTest, Movable) { | |
| 193 math::CalculatorPtr a; | |
| 194 math::CalculatorPtr b; | |
| 195 BindToProxy(new MathCalculatorImpl(), &b); | |
| 196 | |
| 197 EXPECT_TRUE(!a); | |
| 198 EXPECT_FALSE(!b); | |
| 199 | |
| 200 a = b.Pass(); | |
| 201 | |
| 202 EXPECT_FALSE(!a); | |
| 203 EXPECT_TRUE(!b); | |
| 204 } | |
| 205 | |
| 206 TEST_F(InterfacePtrTest, Resettable) { | |
| 207 math::CalculatorPtr a; | |
| 208 | |
| 209 EXPECT_TRUE(!a); | |
| 210 | |
| 211 MessagePipe pipe; | |
| 212 | |
| 213 // Save this so we can test it later. | |
| 214 Handle handle = pipe.handle0.get(); | |
| 215 | |
| 216 a = MakeProxy<math::Calculator>(pipe.handle0.Pass()); | |
| 217 | |
| 218 EXPECT_FALSE(!a); | |
| 219 | |
| 220 a.reset(); | |
| 221 | |
| 222 EXPECT_TRUE(!a); | |
| 223 EXPECT_FALSE(a.internal_state()->router_for_testing()); | |
| 224 | |
| 225 // Test that handle was closed. | |
| 226 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, CloseRaw(handle)); | |
| 227 } | |
| 228 | |
| 229 TEST_F(InterfacePtrTest, EncounteredError) { | |
| 230 math::CalculatorPtr proxy; | |
| 231 MathCalculatorImpl* server = BindToProxy(new MathCalculatorImpl(), &proxy); | |
| 232 | |
| 233 MathCalculatorUIImpl calculator_ui(proxy.Pass()); | |
| 234 | |
| 235 calculator_ui.Add(2.0); | |
| 236 PumpMessages(); | |
| 237 EXPECT_EQ(2.0, calculator_ui.GetOutput()); | |
| 238 EXPECT_FALSE(calculator_ui.encountered_error()); | |
| 239 | |
| 240 calculator_ui.Multiply(5.0); | |
| 241 EXPECT_FALSE(calculator_ui.encountered_error()); | |
| 242 | |
| 243 // Close the server. | |
| 244 server->internal_router()->CloseMessagePipe(); | |
| 245 | |
| 246 // The state change isn't picked up locally yet. | |
| 247 EXPECT_FALSE(calculator_ui.encountered_error()); | |
| 248 | |
| 249 PumpMessages(); | |
| 250 | |
| 251 // OK, now we see the error. | |
| 252 EXPECT_TRUE(calculator_ui.encountered_error()); | |
| 253 } | |
| 254 | |
| 255 TEST_F(InterfacePtrTest, EncounteredErrorCallback) { | |
| 256 math::CalculatorPtr proxy; | |
| 257 MathCalculatorImpl* server = BindToProxy(new MathCalculatorImpl(), &proxy); | |
| 258 | |
| 259 ErrorObserver error_observer; | |
| 260 proxy.set_error_handler(&error_observer); | |
| 261 | |
| 262 MathCalculatorUIImpl calculator_ui(proxy.Pass()); | |
| 263 | |
| 264 calculator_ui.Add(2.0); | |
| 265 PumpMessages(); | |
| 266 EXPECT_EQ(2.0, calculator_ui.GetOutput()); | |
| 267 EXPECT_FALSE(calculator_ui.encountered_error()); | |
| 268 | |
| 269 calculator_ui.Multiply(5.0); | |
| 270 EXPECT_FALSE(calculator_ui.encountered_error()); | |
| 271 | |
| 272 // Close the server. | |
| 273 server->internal_router()->CloseMessagePipe(); | |
| 274 | |
| 275 // The state change isn't picked up locally yet. | |
| 276 EXPECT_FALSE(calculator_ui.encountered_error()); | |
| 277 | |
| 278 PumpMessages(); | |
| 279 | |
| 280 // OK, now we see the error. | |
| 281 EXPECT_TRUE(calculator_ui.encountered_error()); | |
| 282 | |
| 283 // We should have also been able to observe the error through the | |
| 284 // ErrorHandler interface. | |
| 285 EXPECT_TRUE(error_observer.encountered_error()); | |
| 286 } | |
| 287 | |
| 288 TEST_F(InterfacePtrTest, NoClientAttribute) { | |
| 289 // This is a test to ensure the following compiles. The sample::Port interface | |
| 290 // does not have an explicit Client attribute. | |
| 291 sample::PortPtr port; | |
| 292 MessagePipe pipe; | |
| 293 port.Bind(pipe.handle0.Pass()); | |
| 294 } | |
| 295 | |
| 296 TEST_F(InterfacePtrTest, DestroyInterfacePtrOnClientMethod) { | |
| 297 math::CalculatorPtr proxy; | |
| 298 BindToProxy(new MathCalculatorImpl(), &proxy); | |
| 299 | |
| 300 EXPECT_EQ(0, SelfDestructingMathCalculatorUIImpl::num_instances()); | |
| 301 | |
| 302 SelfDestructingMathCalculatorUIImpl* impl = | |
| 303 new SelfDestructingMathCalculatorUIImpl(proxy.Pass()); | |
| 304 impl->BeginTest(false); | |
| 305 | |
| 306 PumpMessages(); | |
| 307 | |
| 308 EXPECT_EQ(0, SelfDestructingMathCalculatorUIImpl::num_instances()); | |
| 309 } | |
| 310 | |
| 311 TEST_F(InterfacePtrTest, NestedDestroyInterfacePtrOnClientMethod) { | |
| 312 math::CalculatorPtr proxy; | |
| 313 BindToProxy(new MathCalculatorImpl(), &proxy); | |
| 314 | |
| 315 EXPECT_EQ(0, SelfDestructingMathCalculatorUIImpl::num_instances()); | |
| 316 | |
| 317 SelfDestructingMathCalculatorUIImpl* impl = | |
| 318 new SelfDestructingMathCalculatorUIImpl(proxy.Pass()); | |
| 319 impl->BeginTest(true); | |
| 320 | |
| 321 PumpMessages(); | |
| 322 | |
| 323 EXPECT_EQ(0, SelfDestructingMathCalculatorUIImpl::num_instances()); | |
| 324 } | |
| 325 | |
| 326 TEST_F(InterfacePtrTest, ReentrantWaitForIncomingMethodCall) { | |
| 327 sample::ServicePtr proxy; | |
| 328 ReentrantServiceImpl* impl = BindToProxy(new ReentrantServiceImpl(), &proxy); | |
| 329 | |
| 330 proxy->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr); | |
| 331 proxy->Frobinate(nullptr, sample::Service::BAZ_OPTIONS_REGULAR, nullptr); | |
| 332 | |
| 333 PumpMessages(); | |
| 334 | |
| 335 EXPECT_EQ(2, impl->max_call_depth()); | |
| 336 } | |
| 337 | |
| 338 class StrongMathCalculatorImpl : public math::Calculator, public ErrorHandler { | |
| 339 public: | |
| 340 StrongMathCalculatorImpl(ScopedMessagePipeHandle handle, | |
| 341 bool* error_received, | |
| 342 bool* destroyed) | |
| 343 : error_received_(error_received), | |
| 344 destroyed_(destroyed), | |
| 345 binding_(this, handle.Pass()) { | |
| 346 binding_.set_error_handler(this); | |
| 347 } | |
| 348 ~StrongMathCalculatorImpl() override { *destroyed_ = true; } | |
| 349 | |
| 350 // math::Calculator implementation. | |
| 351 void Clear() override { binding_.client()->Output(total_); } | |
| 352 | |
| 353 void Add(double value) override { | |
| 354 total_ += value; | |
| 355 binding_.client()->Output(total_); | |
| 356 } | |
| 357 | |
| 358 void Multiply(double value) override { | |
| 359 total_ *= value; | |
| 360 binding_.client()->Output(total_); | |
| 361 } | |
| 362 | |
| 363 // ErrorHandler implementation. | |
| 364 void OnConnectionError() override { *error_received_ = true; } | |
| 365 | |
| 366 private: | |
| 367 double total_ = 0.0; | |
| 368 bool* error_received_; | |
| 369 bool* destroyed_; | |
| 370 | |
| 371 StrongBinding<math::Calculator> binding_; | |
| 372 }; | |
| 373 | |
| 374 TEST(StrongConnectorTest, Math) { | |
| 375 Environment env; | |
| 376 RunLoop loop; | |
| 377 | |
| 378 bool error_received = false; | |
| 379 bool destroyed = false; | |
| 380 MessagePipe pipe; | |
| 381 new StrongMathCalculatorImpl(pipe.handle0.Pass(), &error_received, | |
| 382 &destroyed); | |
| 383 | |
| 384 math::CalculatorPtr calc; | |
| 385 calc.Bind(pipe.handle1.Pass()); | |
| 386 | |
| 387 { | |
| 388 // Suppose this is instantiated in a process that has the other end of the | |
| 389 // message pipe. | |
| 390 MathCalculatorUIImpl calculator_ui(calc.Pass()); | |
| 391 | |
| 392 calculator_ui.Add(2.0); | |
| 393 calculator_ui.Multiply(5.0); | |
| 394 | |
| 395 loop.RunUntilIdle(); | |
| 396 | |
| 397 EXPECT_EQ(10.0, calculator_ui.GetOutput()); | |
| 398 EXPECT_FALSE(error_received); | |
| 399 EXPECT_FALSE(destroyed); | |
| 400 } | |
| 401 // Destroying calculator_ui should close the pipe and generate an error on the | |
| 402 // other | |
| 403 // end which will destroy the instance since it is strongly bound. | |
| 404 | |
| 405 loop.RunUntilIdle(); | |
| 406 EXPECT_TRUE(error_received); | |
| 407 EXPECT_TRUE(destroyed); | |
| 408 } | |
| 409 | |
| 410 class WeakMathCalculatorImpl : public math::Calculator, public ErrorHandler { | |
| 411 public: | |
| 412 WeakMathCalculatorImpl(ScopedMessagePipeHandle handle, | |
| 413 bool* error_received, | |
| 414 bool* destroyed) | |
| 415 : error_received_(error_received), | |
| 416 destroyed_(destroyed), | |
| 417 binding_(this, handle.Pass()) { | |
| 418 binding_.set_error_handler(this); | |
| 419 } | |
| 420 ~WeakMathCalculatorImpl() override { *destroyed_ = true; } | |
| 421 | |
| 422 void Clear() override { binding_.client()->Output(total_); } | |
| 423 | |
| 424 void Add(double value) override { | |
| 425 total_ += value; | |
| 426 binding_.client()->Output(total_); | |
| 427 } | |
| 428 | |
| 429 void Multiply(double value) override { | |
| 430 total_ *= value; | |
| 431 binding_.client()->Output(total_); | |
| 432 } | |
| 433 | |
| 434 // ErrorHandler implementation. | |
| 435 void OnConnectionError() override { *error_received_ = true; } | |
| 436 | |
| 437 private: | |
| 438 double total_ = 0.0; | |
| 439 bool* error_received_; | |
| 440 bool* destroyed_; | |
| 441 | |
| 442 Binding<math::Calculator> binding_; | |
| 443 }; | |
| 444 | |
| 445 TEST(WeakConnectorTest, Math) { | |
| 446 Environment env; | |
| 447 RunLoop loop; | |
| 448 | |
| 449 bool error_received = false; | |
| 450 bool destroyed = false; | |
| 451 MessagePipe pipe; | |
| 452 WeakMathCalculatorImpl impl(pipe.handle0.Pass(), &error_received, &destroyed); | |
| 453 | |
| 454 math::CalculatorPtr calc; | |
| 455 calc.Bind(pipe.handle1.Pass()); | |
| 456 | |
| 457 { | |
| 458 // Suppose this is instantiated in a process that has the other end of the | |
| 459 // message pipe. | |
| 460 MathCalculatorUIImpl calculator_ui(calc.Pass()); | |
| 461 | |
| 462 calculator_ui.Add(2.0); | |
| 463 calculator_ui.Multiply(5.0); | |
| 464 | |
| 465 loop.RunUntilIdle(); | |
| 466 | |
| 467 EXPECT_EQ(10.0, calculator_ui.GetOutput()); | |
| 468 EXPECT_FALSE(error_received); | |
| 469 EXPECT_FALSE(destroyed); | |
| 470 // Destroying calculator_ui should close the pipe and generate an error on | |
| 471 // the other | |
| 472 // end which will destroy the instance since it is strongly bound. | |
| 473 } | |
| 474 | |
| 475 loop.RunUntilIdle(); | |
| 476 EXPECT_TRUE(error_received); | |
| 477 EXPECT_FALSE(destroyed); | |
| 478 } | |
| 479 | |
| 480 } // namespace | |
| 481 } // namespace test | |
| 482 } // namespace mojo | |
| OLD | NEW |