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.
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"
16 class ErrorObserver
: public ErrorHandler
{
18 ErrorObserver() : encountered_error_(false) {
21 bool encountered_error() const { return encountered_error_
; }
23 virtual void OnConnectionError() MOJO_OVERRIDE
{
24 encountered_error_
= true;
28 bool encountered_error_
;
31 class MathCalculatorImpl
: public InterfaceImpl
<math::Calculator
> {
33 virtual ~MathCalculatorImpl() {}
37 got_connection_(false) {
40 virtual void OnConnectionEstablished() MOJO_OVERRIDE
{
41 got_connection_
= true;
44 virtual void Clear() MOJO_OVERRIDE
{
45 client()->Output(total_
);
48 virtual void Add(double value
) MOJO_OVERRIDE
{
50 client()->Output(total_
);
53 virtual void Multiply(double value
) MOJO_OVERRIDE
{
55 client()->Output(total_
);
58 bool got_connection() const {
59 return got_connection_
;
67 class MathCalculatorUIImpl
: public math::CalculatorUI
{
69 explicit MathCalculatorUIImpl(math::CalculatorPtr calculator
)
70 : calculator_(calculator
.Pass()),
72 calculator_
.set_client(this);
75 bool WaitForIncomingMethodCall() {
76 return calculator_
.WaitForIncomingMethodCall();
79 bool encountered_error() const {
80 return calculator_
.encountered_error();
83 void Add(double value
) {
84 calculator_
->Add(value
);
87 void Subtract(double value
) {
88 calculator_
->Add(-value
);
91 void Multiply(double value
) {
92 calculator_
->Multiply(value
);
95 void Divide(double value
) {
96 calculator_
->Multiply(1.0 / value
);
99 double GetOutput() const {
104 // math::CalculatorUI implementation:
105 virtual void Output(double value
) MOJO_OVERRIDE
{
109 math::CalculatorPtr calculator_
;
113 class SelfDestructingMathCalculatorUIImpl
: public math::CalculatorUI
{
115 explicit SelfDestructingMathCalculatorUIImpl(math::CalculatorPtr calculator
)
116 : calculator_(calculator
.Pass()),
119 calculator_
.set_client(this);
122 void BeginTest(bool nested
) {
123 nesting_level_
= nested
? 2 : 1;
124 calculator_
->Add(1.0);
127 static int num_instances() { return num_instances_
; }
130 virtual ~SelfDestructingMathCalculatorUIImpl() {
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();
144 math::CalculatorPtr calculator_
;
146 static int num_instances_
;
150 int SelfDestructingMathCalculatorUIImpl::num_instances_
= 0;
152 class ReentrantServiceImpl
: public InterfaceImpl
<sample::Service
> {
154 virtual ~ReentrantServiceImpl() {}
156 ReentrantServiceImpl()
157 : got_connection_(false), call_depth_(0), max_call_depth_(0) {}
159 virtual void OnConnectionEstablished() MOJO_OVERRIDE
{
160 got_connection_
= true;
163 bool got_connection() const {
164 return got_connection_
;
167 int max_call_depth() { return max_call_depth_
; }
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());
179 virtual void GetPort(mojo::InterfaceRequest
<sample::Port
> port
)
183 bool got_connection_
;
188 class InterfacePtrTest
: public testing::Test
{
190 virtual ~InterfacePtrTest() {
191 loop_
.RunUntilIdle();
194 void PumpMessages() {
195 loop_
.RunUntilIdle();
203 TEST_F(InterfacePtrTest
, EndToEnd
) {
204 math::CalculatorPtr calc
;
205 MathCalculatorImpl
* impl
= BindToProxy(new MathCalculatorImpl(), &calc
);
206 EXPECT_TRUE(impl
->got_connection());
208 // Suppose this is instantiated in a process that has pipe1_.
209 MathCalculatorUIImpl
calculator_ui(calc
.Pass());
211 calculator_ui
.Add(2.0);
212 calculator_ui
.Multiply(5.0);
216 EXPECT_EQ(10.0, calculator_ui
.GetOutput());
219 TEST_F(InterfacePtrTest
, EndToEnd_Synchronous
) {
220 math::CalculatorPtr calc
;
221 MathCalculatorImpl
* impl
= BindToProxy(new MathCalculatorImpl(), &calc
);
222 EXPECT_TRUE(impl
->got_connection());
224 // Suppose this is instantiated in a process that has pipe1_.
225 MathCalculatorUIImpl
calculator_ui(calc
.Pass());
227 EXPECT_EQ(0.0, calculator_ui
.GetOutput());
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());
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());
242 TEST_F(InterfacePtrTest
, Movable
) {
243 math::CalculatorPtr a
;
244 math::CalculatorPtr b
;
245 BindToProxy(new MathCalculatorImpl(), &b
);
256 TEST_F(InterfacePtrTest
, Resettable
) {
257 math::CalculatorPtr a
;
263 // Save this so we can test it later.
264 Handle handle
= pipe
.handle0
.get();
266 a
= MakeProxy
<math::Calculator
>(pipe
.handle0
.Pass());
273 EXPECT_FALSE(a
.internal_state()->router_for_testing());
275 // Test that handle was closed.
276 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT
, CloseRaw(handle
));
279 TEST_F(InterfacePtrTest
, EncounteredError
) {
280 math::CalculatorPtr proxy
;
281 MathCalculatorImpl
* server
= BindToProxy(new MathCalculatorImpl(), &proxy
);
283 MathCalculatorUIImpl
calculator_ui(proxy
.Pass());
285 calculator_ui
.Add(2.0);
287 EXPECT_EQ(2.0, calculator_ui
.GetOutput());
288 EXPECT_FALSE(calculator_ui
.encountered_error());
290 calculator_ui
.Multiply(5.0);
291 EXPECT_FALSE(calculator_ui
.encountered_error());
294 server
->internal_state()->router()->CloseMessagePipe();
296 // The state change isn't picked up locally yet.
297 EXPECT_FALSE(calculator_ui
.encountered_error());
301 // OK, now we see the error.
302 EXPECT_TRUE(calculator_ui
.encountered_error());
305 TEST_F(InterfacePtrTest
, EncounteredErrorCallback
) {
306 math::CalculatorPtr proxy
;
307 MathCalculatorImpl
* server
= BindToProxy(new MathCalculatorImpl(), &proxy
);
309 ErrorObserver error_observer
;
310 proxy
.set_error_handler(&error_observer
);
312 MathCalculatorUIImpl
calculator_ui(proxy
.Pass());
314 calculator_ui
.Add(2.0);
316 EXPECT_EQ(2.0, calculator_ui
.GetOutput());
317 EXPECT_FALSE(calculator_ui
.encountered_error());
319 calculator_ui
.Multiply(5.0);
320 EXPECT_FALSE(calculator_ui
.encountered_error());
323 server
->internal_state()->router()->CloseMessagePipe();
325 // The state change isn't picked up locally yet.
326 EXPECT_FALSE(calculator_ui
.encountered_error());
330 // OK, now we see the error.
331 EXPECT_TRUE(calculator_ui
.encountered_error());
333 // We should have also been able to observe the error through the
334 // ErrorHandler interface.
335 EXPECT_TRUE(error_observer
.encountered_error());
338 TEST_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
;
343 port
.Bind(pipe
.handle0
.Pass());
346 TEST_F(InterfacePtrTest
, DestroyInterfacePtrOnClientMethod
) {
347 math::CalculatorPtr proxy
;
348 BindToProxy(new MathCalculatorImpl(), &proxy
);
350 EXPECT_EQ(0, SelfDestructingMathCalculatorUIImpl::num_instances());
352 SelfDestructingMathCalculatorUIImpl
* impl
=
353 new SelfDestructingMathCalculatorUIImpl(proxy
.Pass());
354 impl
->BeginTest(false);
358 EXPECT_EQ(0, SelfDestructingMathCalculatorUIImpl::num_instances());
361 TEST_F(InterfacePtrTest
, NestedDestroyInterfacePtrOnClientMethod
) {
362 math::CalculatorPtr proxy
;
363 BindToProxy(new MathCalculatorImpl(), &proxy
);
365 EXPECT_EQ(0, SelfDestructingMathCalculatorUIImpl::num_instances());
367 SelfDestructingMathCalculatorUIImpl
* impl
=
368 new SelfDestructingMathCalculatorUIImpl(proxy
.Pass());
369 impl
->BeginTest(true);
373 EXPECT_EQ(0, SelfDestructingMathCalculatorUIImpl::num_instances());
376 TEST_F(InterfacePtrTest
, ReentrantWaitForIncomingMethodCall
) {
377 sample::ServicePtr proxy
;
378 ReentrantServiceImpl
* impl
= BindToProxy(new ReentrantServiceImpl(), &proxy
);
379 EXPECT_TRUE(impl
->got_connection());
381 proxy
->Frobinate(sample::FooPtr(),
382 sample::Service::BAZ_OPTIONS_REGULAR
,
384 proxy
->Frobinate(sample::FooPtr(),
385 sample::Service::BAZ_OPTIONS_REGULAR
,
390 EXPECT_EQ(2, impl
->max_call_depth());