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 |