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/error_handler.h" 6#include "mojo/public/cpp/environment/environment.h" 7#include "mojo/public/cpp/utility/run_loop.h" 8#include "mojo/public/interfaces/bindings/tests/math_calculator.mojom.h" 9#include "mojo/public/interfaces/bindings/tests/sample_service.mojom.h" 10#include "testing/gtest/include/gtest/gtest.h" 11 12namespace mojo { 13namespace test { 14namespace { 15 16class ErrorObserver : public ErrorHandler { 17 public: 18 ErrorObserver() : encountered_error_(false) { 19 } 20 21 bool encountered_error() const { return encountered_error_; } 22 23 virtual void OnConnectionError() MOJO_OVERRIDE { 24 encountered_error_ = true; 25 } 26 27 private: 28 bool encountered_error_; 29}; 30 31class MathCalculatorImpl : public InterfaceImpl<math::Calculator> { 32 public: 33 virtual ~MathCalculatorImpl() {} 34 35 MathCalculatorImpl() 36 : total_(0.0), 37 got_connection_(false) { 38 } 39 40 virtual void OnConnectionEstablished() MOJO_OVERRIDE { 41 got_connection_ = true; 42 } 43 44 virtual void Clear() MOJO_OVERRIDE { 45 client()->Output(total_); 46 } 47 48 virtual void Add(double value) MOJO_OVERRIDE { 49 total_ += value; 50 client()->Output(total_); 51 } 52 53 virtual void Multiply(double value) MOJO_OVERRIDE { 54 total_ *= value; 55 client()->Output(total_); 56 } 57 58 bool got_connection() const { 59 return got_connection_; 60 } 61 62 private: 63 double total_; 64 bool got_connection_; 65}; 66 67class MathCalculatorUIImpl : public math::CalculatorUI { 68 public: 69 explicit MathCalculatorUIImpl(math::CalculatorPtr calculator) 70 : calculator_(calculator.Pass()), 71 output_(0.0) { 72 calculator_.set_client(this); 73 } 74 75 bool WaitForIncomingMethodCall() { 76 return calculator_.WaitForIncomingMethodCall(); 77 } 78 79 bool encountered_error() const { 80 return calculator_.encountered_error(); 81 } 82 83 void Add(double value) { 84 calculator_->Add(value); 85 } 86 87 void Subtract(double value) { 88 calculator_->Add(-value); 89 } 90 91 void Multiply(double value) { 92 calculator_->Multiply(value); 93 } 94 95 void Divide(double value) { 96 calculator_->Multiply(1.0 / value); 97 } 98 99 double GetOutput() const { 100 return output_; 101 } 102 103 private: 104 // math::CalculatorUI implementation: 105 virtual void Output(double value) MOJO_OVERRIDE { 106 output_ = value; 107 } 108 109 math::CalculatorPtr calculator_; 110 double output_; 111}; 112 113class SelfDestructingMathCalculatorUIImpl : public math::CalculatorUI { 114 public: 115 explicit SelfDestructingMathCalculatorUIImpl(math::CalculatorPtr calculator) 116 : calculator_(calculator.Pass()), 117 nesting_level_(0) { 118 ++num_instances_; 119 calculator_.set_client(this); 120 } 121 122 void BeginTest(bool nested) { 123 nesting_level_ = nested ? 2 : 1; 124 calculator_->Add(1.0); 125 } 126 127 static int num_instances() { return num_instances_; } 128 129 private: 130 virtual ~SelfDestructingMathCalculatorUIImpl() { 131 --num_instances_; 132 } 133 134 virtual void Output(double value) MOJO_OVERRIDE { 135 if (--nesting_level_ > 0) { 136 // Add some more and wait for re-entrant call to Output! 137 calculator_->Add(1.0); 138 RunLoop::current()->RunUntilIdle(); 139 } else { 140 delete this; 141 } 142 } 143 144 math::CalculatorPtr calculator_; 145 int nesting_level_; 146 static int num_instances_; 147}; 148 149// static 150int SelfDestructingMathCalculatorUIImpl::num_instances_ = 0; 151 152class ReentrantServiceImpl : public InterfaceImpl<sample::Service> { 153 public: 154 virtual ~ReentrantServiceImpl() {} 155 156 ReentrantServiceImpl() 157 : got_connection_(false), call_depth_(0), max_call_depth_(0) {} 158 159 virtual void OnConnectionEstablished() MOJO_OVERRIDE { 160 got_connection_ = true; 161 } 162 163 bool got_connection() const { 164 return got_connection_; 165 } 166 167 int max_call_depth() { return max_call_depth_; } 168 169 virtual void Frobinate(sample::FooPtr foo, 170 sample::Service::BazOptions baz, 171 sample::PortPtr port) MOJO_OVERRIDE { 172 max_call_depth_ = std::max(++call_depth_, max_call_depth_); 173 if (call_depth_ == 1) { 174 EXPECT_TRUE(WaitForIncomingMethodCall()); 175 } 176 call_depth_--; 177 } 178 179 virtual void GetPort(mojo::InterfaceRequest<sample::Port> port) 180 MOJO_OVERRIDE {} 181 182 private: 183 bool got_connection_; 184 int call_depth_; 185 int max_call_depth_; 186}; 187 188class InterfacePtrTest : public testing::Test { 189 public: 190 virtual ~InterfacePtrTest() { 191 loop_.RunUntilIdle(); 192 } 193 194 void PumpMessages() { 195 loop_.RunUntilIdle(); 196 } 197 198 private: 199 Environment env_; 200 RunLoop loop_; 201}; 202 203TEST_F(InterfacePtrTest, EndToEnd) { 204 math::CalculatorPtr calc; 205 MathCalculatorImpl* impl = BindToProxy(new MathCalculatorImpl(), &calc); 206 EXPECT_TRUE(impl->got_connection()); 207 208 // Suppose this is instantiated in a process that has pipe1_. 209 MathCalculatorUIImpl calculator_ui(calc.Pass()); 210 211 calculator_ui.Add(2.0); 212 calculator_ui.Multiply(5.0); 213 214 PumpMessages(); 215 216 EXPECT_EQ(10.0, calculator_ui.GetOutput()); 217} 218 219TEST_F(InterfacePtrTest, EndToEnd_Synchronous) { 220 math::CalculatorPtr calc; 221 MathCalculatorImpl* impl = BindToProxy(new MathCalculatorImpl(), &calc); 222 EXPECT_TRUE(impl->got_connection()); 223 224 // Suppose this is instantiated in a process that has pipe1_. 225 MathCalculatorUIImpl calculator_ui(calc.Pass()); 226 227 EXPECT_EQ(0.0, calculator_ui.GetOutput()); 228 229 calculator_ui.Add(2.0); 230 EXPECT_EQ(0.0, calculator_ui.GetOutput()); 231 impl->WaitForIncomingMethodCall(); 232 calculator_ui.WaitForIncomingMethodCall(); 233 EXPECT_EQ(2.0, calculator_ui.GetOutput()); 234 235 calculator_ui.Multiply(5.0); 236 EXPECT_EQ(2.0, calculator_ui.GetOutput()); 237 impl->WaitForIncomingMethodCall(); 238 calculator_ui.WaitForIncomingMethodCall(); 239 EXPECT_EQ(10.0, calculator_ui.GetOutput()); 240} 241 242TEST_F(InterfacePtrTest, Movable) { 243 math::CalculatorPtr a; 244 math::CalculatorPtr b; 245 BindToProxy(new MathCalculatorImpl(), &b); 246 247 EXPECT_TRUE(!a); 248 EXPECT_FALSE(!b); 249 250 a = b.Pass(); 251 252 EXPECT_FALSE(!a); 253 EXPECT_TRUE(!b); 254} 255 256TEST_F(InterfacePtrTest, Resettable) { 257 math::CalculatorPtr a; 258 259 EXPECT_TRUE(!a); 260 261 MessagePipe pipe; 262 263 // Save this so we can test it later. 264 Handle handle = pipe.handle0.get(); 265 266 a = MakeProxy<math::Calculator>(pipe.handle0.Pass()); 267 268 EXPECT_FALSE(!a); 269 270 a.reset(); 271 272 EXPECT_TRUE(!a); 273 EXPECT_FALSE(a.internal_state()->router_for_testing()); 274 275 // Test that handle was closed. 276 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, CloseRaw(handle)); 277} 278 279TEST_F(InterfacePtrTest, EncounteredError) { 280 math::CalculatorPtr proxy; 281 MathCalculatorImpl* server = BindToProxy(new MathCalculatorImpl(), &proxy); 282 283 MathCalculatorUIImpl calculator_ui(proxy.Pass()); 284 285 calculator_ui.Add(2.0); 286 PumpMessages(); 287 EXPECT_EQ(2.0, calculator_ui.GetOutput()); 288 EXPECT_FALSE(calculator_ui.encountered_error()); 289 290 calculator_ui.Multiply(5.0); 291 EXPECT_FALSE(calculator_ui.encountered_error()); 292 293 // Close the server. 294 server->internal_state()->router()->CloseMessagePipe(); 295 296 // The state change isn't picked up locally yet. 297 EXPECT_FALSE(calculator_ui.encountered_error()); 298 299 PumpMessages(); 300 301 // OK, now we see the error. 302 EXPECT_TRUE(calculator_ui.encountered_error()); 303} 304 305TEST_F(InterfacePtrTest, EncounteredErrorCallback) { 306 math::CalculatorPtr proxy; 307 MathCalculatorImpl* server = BindToProxy(new MathCalculatorImpl(), &proxy); 308 309 ErrorObserver error_observer; 310 proxy.set_error_handler(&error_observer); 311 312 MathCalculatorUIImpl calculator_ui(proxy.Pass()); 313 314 calculator_ui.Add(2.0); 315 PumpMessages(); 316 EXPECT_EQ(2.0, calculator_ui.GetOutput()); 317 EXPECT_FALSE(calculator_ui.encountered_error()); 318 319 calculator_ui.Multiply(5.0); 320 EXPECT_FALSE(calculator_ui.encountered_error()); 321 322 // Close the server. 323 server->internal_state()->router()->CloseMessagePipe(); 324 325 // The state change isn't picked up locally yet. 326 EXPECT_FALSE(calculator_ui.encountered_error()); 327 328 PumpMessages(); 329 330 // OK, now we see the error. 331 EXPECT_TRUE(calculator_ui.encountered_error()); 332 333 // We should have also been able to observe the error through the 334 // ErrorHandler interface. 335 EXPECT_TRUE(error_observer.encountered_error()); 336} 337 338TEST_F(InterfacePtrTest, NoClientAttribute) { 339 // This is a test to ensure the following compiles. The sample::Port interface 340 // does not have an explicit Client attribute. 341 sample::PortPtr port; 342 MessagePipe pipe; 343 port.Bind(pipe.handle0.Pass()); 344} 345 346TEST_F(InterfacePtrTest, DestroyInterfacePtrOnClientMethod) { 347 math::CalculatorPtr proxy; 348 BindToProxy(new MathCalculatorImpl(), &proxy); 349 350 EXPECT_EQ(0, SelfDestructingMathCalculatorUIImpl::num_instances()); 351 352 SelfDestructingMathCalculatorUIImpl* impl = 353 new SelfDestructingMathCalculatorUIImpl(proxy.Pass()); 354 impl->BeginTest(false); 355 356 PumpMessages(); 357 358 EXPECT_EQ(0, SelfDestructingMathCalculatorUIImpl::num_instances()); 359} 360 361TEST_F(InterfacePtrTest, NestedDestroyInterfacePtrOnClientMethod) { 362 math::CalculatorPtr proxy; 363 BindToProxy(new MathCalculatorImpl(), &proxy); 364 365 EXPECT_EQ(0, SelfDestructingMathCalculatorUIImpl::num_instances()); 366 367 SelfDestructingMathCalculatorUIImpl* impl = 368 new SelfDestructingMathCalculatorUIImpl(proxy.Pass()); 369 impl->BeginTest(true); 370 371 PumpMessages(); 372 373 EXPECT_EQ(0, SelfDestructingMathCalculatorUIImpl::num_instances()); 374} 375 376TEST_F(InterfacePtrTest, ReentrantWaitForIncomingMethodCall) { 377 sample::ServicePtr proxy; 378 ReentrantServiceImpl* impl = BindToProxy(new ReentrantServiceImpl(), &proxy); 379 EXPECT_TRUE(impl->got_connection()); 380 381 proxy->Frobinate(sample::FooPtr(), 382 sample::Service::BAZ_OPTIONS_REGULAR, 383 sample::PortPtr()); 384 proxy->Frobinate(sample::FooPtr(), 385 sample::Service::BAZ_OPTIONS_REGULAR, 386 sample::PortPtr()); 387 388 PumpMessages(); 389 390 EXPECT_EQ(2, impl->max_call_depth()); 391} 392 393} // namespace 394} // namespace test 395} // namespace mojo 396