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
12 namespace mojo {
13 namespace test {
14 namespace {
15
16 class ErrorObserver : public ErrorHandler {
17 public:
ErrorObserver()18 ErrorObserver() : encountered_error_(false) {
19 }
20
encountered_error() const21 bool encountered_error() const { return encountered_error_; }
22
OnConnectionError()23 virtual void OnConnectionError() MOJO_OVERRIDE {
24 encountered_error_ = true;
25 }
26
27 private:
28 bool encountered_error_;
29 };
30
31 class MathCalculatorImpl : public InterfaceImpl<math::Calculator> {
32 public:
~MathCalculatorImpl()33 virtual ~MathCalculatorImpl() {}
34
MathCalculatorImpl()35 MathCalculatorImpl()
36 : total_(0.0),
37 got_connection_(false) {
38 }
39
OnConnectionEstablished()40 virtual void OnConnectionEstablished() MOJO_OVERRIDE {
41 got_connection_ = true;
42 }
43
OnConnectionError()44 virtual void OnConnectionError() MOJO_OVERRIDE {
45 delete this;
46 }
47
Clear()48 virtual void Clear() MOJO_OVERRIDE {
49 client()->Output(total_);
50 }
51
Add(double value)52 virtual void Add(double value) MOJO_OVERRIDE {
53 total_ += value;
54 client()->Output(total_);
55 }
56
Multiply(double value)57 virtual void Multiply(double value) MOJO_OVERRIDE {
58 total_ *= value;
59 client()->Output(total_);
60 }
61
got_connection() const62 bool got_connection() const {
63 return got_connection_;
64 }
65
66 private:
67 double total_;
68 bool got_connection_;
69 };
70
71 class MathCalculatorUIImpl : public math::CalculatorUI {
72 public:
MathCalculatorUIImpl(math::CalculatorPtr calculator)73 explicit MathCalculatorUIImpl(math::CalculatorPtr calculator)
74 : calculator_(calculator.Pass()),
75 output_(0.0) {
76 calculator_.set_client(this);
77 }
78
encountered_error() const79 bool encountered_error() const {
80 return calculator_.encountered_error();
81 }
82
Add(double value)83 void Add(double value) {
84 calculator_->Add(value);
85 }
86
Subtract(double value)87 void Subtract(double value) {
88 calculator_->Add(-value);
89 }
90
Multiply(double value)91 void Multiply(double value) {
92 calculator_->Multiply(value);
93 }
94
Divide(double value)95 void Divide(double value) {
96 calculator_->Multiply(1.0 / value);
97 }
98
GetOutput() const99 double GetOutput() const {
100 return output_;
101 }
102
103 private:
104 // math::CalculatorUI implementation:
Output(double value)105 virtual void Output(double value) MOJO_OVERRIDE {
106 output_ = value;
107 }
108
109 math::CalculatorPtr calculator_;
110 double output_;
111 };
112
113 class SelfDestructingMathCalculatorUIImpl : public math::CalculatorUI {
114 public:
SelfDestructingMathCalculatorUIImpl(math::CalculatorPtr calculator)115 explicit SelfDestructingMathCalculatorUIImpl(math::CalculatorPtr calculator)
116 : calculator_(calculator.Pass()),
117 nesting_level_(0) {
118 ++num_instances_;
119 calculator_.set_client(this);
120 }
121
BeginTest(bool nested)122 void BeginTest(bool nested) {
123 nesting_level_ = nested ? 2 : 1;
124 calculator_->Add(1.0);
125 }
126
num_instances()127 static int num_instances() { return num_instances_; }
128
129 private:
~SelfDestructingMathCalculatorUIImpl()130 virtual ~SelfDestructingMathCalculatorUIImpl() {
131 --num_instances_;
132 }
133
Output(double value)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
150 int SelfDestructingMathCalculatorUIImpl::num_instances_ = 0;
151
152 class InterfacePtrTest : public testing::Test {
153 public:
~InterfacePtrTest()154 virtual ~InterfacePtrTest() {
155 loop_.RunUntilIdle();
156 }
157
PumpMessages()158 void PumpMessages() {
159 loop_.RunUntilIdle();
160 }
161
162 private:
163 Environment env_;
164 RunLoop loop_;
165 };
166
TEST_F(InterfacePtrTest,EndToEnd)167 TEST_F(InterfacePtrTest, EndToEnd) {
168 math::CalculatorPtr calc;
169 MathCalculatorImpl* impl = BindToProxy(new MathCalculatorImpl(), &calc);
170 EXPECT_TRUE(impl->got_connection());
171
172 // Suppose this is instantiated in a process that has pipe1_.
173 MathCalculatorUIImpl calculator_ui(calc.Pass());
174
175 calculator_ui.Add(2.0);
176 calculator_ui.Multiply(5.0);
177
178 PumpMessages();
179
180 EXPECT_EQ(10.0, calculator_ui.GetOutput());
181 }
182
TEST_F(InterfacePtrTest,Movable)183 TEST_F(InterfacePtrTest, Movable) {
184 math::CalculatorPtr a;
185 math::CalculatorPtr b;
186 BindToProxy(new MathCalculatorImpl(), &b);
187
188 EXPECT_TRUE(!a.get());
189 EXPECT_FALSE(!b.get());
190
191 a = b.Pass();
192
193 EXPECT_FALSE(!a.get());
194 EXPECT_TRUE(!b.get());
195 }
196
TEST_F(InterfacePtrTest,Resettable)197 TEST_F(InterfacePtrTest, Resettable) {
198 math::CalculatorPtr a;
199
200 EXPECT_TRUE(!a.get());
201
202 MessagePipe pipe;
203
204 // Save this so we can test it later.
205 Handle handle = pipe.handle0.get();
206
207 a = MakeProxy<math::Calculator>(pipe.handle0.Pass());
208
209 EXPECT_FALSE(!a.get());
210
211 a.reset();
212
213 EXPECT_TRUE(!a.get());
214 EXPECT_FALSE(a.internal_state()->router());
215
216 // Test that handle was closed.
217 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, CloseRaw(handle));
218 }
219
TEST_F(InterfacePtrTest,EncounteredError)220 TEST_F(InterfacePtrTest, EncounteredError) {
221 math::CalculatorPtr proxy;
222 MathCalculatorImpl* server = BindToProxy(new MathCalculatorImpl(), &proxy);
223
224 MathCalculatorUIImpl calculator_ui(proxy.Pass());
225
226 calculator_ui.Add(2.0);
227 PumpMessages();
228 EXPECT_EQ(2.0, calculator_ui.GetOutput());
229 EXPECT_FALSE(calculator_ui.encountered_error());
230
231 calculator_ui.Multiply(5.0);
232 EXPECT_FALSE(calculator_ui.encountered_error());
233
234 // Close the server.
235 server->internal_state()->router()->CloseMessagePipe();
236
237 // The state change isn't picked up locally yet.
238 EXPECT_FALSE(calculator_ui.encountered_error());
239
240 PumpMessages();
241
242 // OK, now we see the error.
243 EXPECT_TRUE(calculator_ui.encountered_error());
244 }
245
TEST_F(InterfacePtrTest,EncounteredErrorCallback)246 TEST_F(InterfacePtrTest, EncounteredErrorCallback) {
247 math::CalculatorPtr proxy;
248 MathCalculatorImpl* server = BindToProxy(new MathCalculatorImpl(), &proxy);
249
250 ErrorObserver error_observer;
251 proxy.set_error_handler(&error_observer);
252
253 MathCalculatorUIImpl calculator_ui(proxy.Pass());
254
255 calculator_ui.Add(2.0);
256 PumpMessages();
257 EXPECT_EQ(2.0, calculator_ui.GetOutput());
258 EXPECT_FALSE(calculator_ui.encountered_error());
259
260 calculator_ui.Multiply(5.0);
261 EXPECT_FALSE(calculator_ui.encountered_error());
262
263 // Close the server.
264 server->internal_state()->router()->CloseMessagePipe();
265
266 // The state change isn't picked up locally yet.
267 EXPECT_FALSE(calculator_ui.encountered_error());
268
269 PumpMessages();
270
271 // OK, now we see the error.
272 EXPECT_TRUE(calculator_ui.encountered_error());
273
274 // We should have also been able to observe the error through the
275 // ErrorHandler interface.
276 EXPECT_TRUE(error_observer.encountered_error());
277 }
278
TEST_F(InterfacePtrTest,NoClientAttribute)279 TEST_F(InterfacePtrTest, NoClientAttribute) {
280 // This is a test to ensure the following compiles. The sample::Port interface
281 // does not have an explicit Client attribute.
282 sample::PortPtr port;
283 MessagePipe pipe;
284 port.Bind(pipe.handle0.Pass());
285 }
286
TEST_F(InterfacePtrTest,DestroyInterfacePtrOnClientMethod)287 TEST_F(InterfacePtrTest, DestroyInterfacePtrOnClientMethod) {
288 math::CalculatorPtr proxy;
289 BindToProxy(new MathCalculatorImpl(), &proxy);
290
291 EXPECT_EQ(0, SelfDestructingMathCalculatorUIImpl::num_instances());
292
293 SelfDestructingMathCalculatorUIImpl* impl =
294 new SelfDestructingMathCalculatorUIImpl(proxy.Pass());
295 impl->BeginTest(false);
296
297 PumpMessages();
298
299 EXPECT_EQ(0, SelfDestructingMathCalculatorUIImpl::num_instances());
300 }
301
TEST_F(InterfacePtrTest,NestedDestroyInterfacePtrOnClientMethod)302 TEST_F(InterfacePtrTest, NestedDestroyInterfacePtrOnClientMethod) {
303 math::CalculatorPtr proxy;
304 BindToProxy(new MathCalculatorImpl(), &proxy);
305
306 EXPECT_EQ(0, SelfDestructingMathCalculatorUIImpl::num_instances());
307
308 SelfDestructingMathCalculatorUIImpl* impl =
309 new SelfDestructingMathCalculatorUIImpl(proxy.Pass());
310 impl->BeginTest(true);
311
312 PumpMessages();
313
314 EXPECT_EQ(0, SelfDestructingMathCalculatorUIImpl::num_instances());
315 }
316
317 } // namespace
318 } // namespace test
319 } // namespace mojo
320