OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "mojo/public/cpp/bindings/lib/connector.h" | 5 #include "mojo/public/cpp/bindings/lib/connector.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 #include <stdlib.h> | 8 #include <stdlib.h> |
9 #include <string.h> | 9 #include <string.h> |
10 #include <utility> | 10 #include <utility> |
11 | 11 |
12 #include "base/message_loop/message_loop.h" | 12 #include "base/message_loop/message_loop.h" |
| 13 #include "base/run_loop.h" |
13 #include "mojo/message_pump/message_pump_mojo.h" | 14 #include "mojo/message_pump/message_pump_mojo.h" |
14 #include "mojo/public/cpp/bindings/lib/message_builder.h" | 15 #include "mojo/public/cpp/bindings/lib/message_builder.h" |
15 #include "mojo/public/cpp/bindings/tests/message_queue.h" | 16 #include "mojo/public/cpp/bindings/tests/message_queue.h" |
16 #include "mojo/public/cpp/system/macros.h" | 17 #include "mojo/public/cpp/system/macros.h" |
17 #include "testing/gtest/include/gtest/gtest.h" | 18 #include "testing/gtest/include/gtest/gtest.h" |
18 | 19 |
19 namespace mojo { | 20 namespace mojo { |
20 namespace test { | 21 namespace test { |
21 namespace { | 22 namespace { |
22 | 23 |
23 class MessageAccumulator : public MessageReceiver { | 24 class MessageAccumulator : public MessageReceiver { |
24 public: | 25 public: |
25 MessageAccumulator() {} | 26 MessageAccumulator() {} |
| 27 explicit MessageAccumulator(const base::Closure& closure) |
| 28 : closure_(closure) {} |
26 | 29 |
27 bool Accept(Message* message) override { | 30 bool Accept(Message* message) override { |
28 queue_.Push(message); | 31 queue_.Push(message); |
| 32 if (!closure_.is_null()) |
| 33 closure_.Run(); |
| 34 closure_.Reset(); |
29 return true; | 35 return true; |
30 } | 36 } |
31 | 37 |
32 bool IsEmpty() const { return queue_.IsEmpty(); } | 38 bool IsEmpty() const { return queue_.IsEmpty(); } |
33 | 39 |
34 void Pop(Message* message) { queue_.Pop(message); } | 40 void Pop(Message* message) { queue_.Pop(message); } |
35 | 41 |
| 42 void set_closure(const base::Closure& closure) { |
| 43 closure_ = closure; |
| 44 } |
| 45 |
36 private: | 46 private: |
37 MessageQueue queue_; | 47 MessageQueue queue_; |
| 48 base::Closure closure_; |
38 }; | 49 }; |
39 | 50 |
40 class ConnectorDeletingMessageAccumulator : public MessageAccumulator { | 51 class ConnectorDeletingMessageAccumulator : public MessageAccumulator { |
41 public: | 52 public: |
42 ConnectorDeletingMessageAccumulator(internal::Connector** connector) | 53 ConnectorDeletingMessageAccumulator(internal::Connector** connector) |
43 : connector_(connector) {} | 54 : connector_(connector) {} |
44 | 55 |
45 bool Accept(Message* message) override { | 56 bool Accept(Message* message) override { |
46 delete *connector_; | 57 delete *connector_; |
47 *connector_ = nullptr; | 58 *connector_ = nullptr; |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
85 void TearDown() override {} | 96 void TearDown() override {} |
86 | 97 |
87 void AllocMessage(const char* text, Message* message) { | 98 void AllocMessage(const char* text, Message* message) { |
88 size_t payload_size = strlen(text) + 1; // Plus null terminator. | 99 size_t payload_size = strlen(text) + 1; // Plus null terminator. |
89 internal::MessageBuilder builder(1, payload_size); | 100 internal::MessageBuilder builder(1, payload_size); |
90 memcpy(builder.buffer()->Allocate(payload_size), text, payload_size); | 101 memcpy(builder.buffer()->Allocate(payload_size), text, payload_size); |
91 | 102 |
92 builder.message()->MoveTo(message); | 103 builder.message()->MoveTo(message); |
93 } | 104 } |
94 | 105 |
95 void PumpMessages() { loop_.RunUntilIdle(); } | |
96 | |
97 protected: | 106 protected: |
98 ScopedMessagePipeHandle handle0_; | 107 ScopedMessagePipeHandle handle0_; |
99 ScopedMessagePipeHandle handle1_; | 108 ScopedMessagePipeHandle handle1_; |
100 | 109 |
101 private: | 110 private: |
102 base::MessageLoop loop_; | 111 base::MessageLoop loop_; |
103 }; | 112 }; |
104 | 113 |
105 TEST_F(ConnectorTest, Basic) { | 114 TEST_F(ConnectorTest, Basic) { |
106 internal::Connector connector0(std::move(handle0_), | 115 internal::Connector connector0(std::move(handle0_), |
107 internal::Connector::SINGLE_THREADED_SEND); | 116 internal::Connector::SINGLE_THREADED_SEND); |
108 internal::Connector connector1(std::move(handle1_), | 117 internal::Connector connector1(std::move(handle1_), |
109 internal::Connector::SINGLE_THREADED_SEND); | 118 internal::Connector::SINGLE_THREADED_SEND); |
110 | 119 |
111 const char kText[] = "hello world"; | 120 const char kText[] = "hello world"; |
112 | 121 |
113 Message message; | 122 Message message; |
114 AllocMessage(kText, &message); | 123 AllocMessage(kText, &message); |
115 | 124 |
116 connector0.Accept(&message); | 125 connector0.Accept(&message); |
117 | 126 |
118 MessageAccumulator accumulator; | 127 base::RunLoop run_loop; |
| 128 MessageAccumulator accumulator(run_loop.QuitClosure()); |
119 connector1.set_incoming_receiver(&accumulator); | 129 connector1.set_incoming_receiver(&accumulator); |
120 | 130 |
121 PumpMessages(); | 131 run_loop.Run(); |
122 | 132 |
123 ASSERT_FALSE(accumulator.IsEmpty()); | 133 ASSERT_FALSE(accumulator.IsEmpty()); |
124 | 134 |
125 Message message_received; | 135 Message message_received; |
126 accumulator.Pop(&message_received); | 136 accumulator.Pop(&message_received); |
127 | 137 |
128 EXPECT_EQ( | 138 EXPECT_EQ( |
129 std::string(kText), | 139 std::string(kText), |
130 std::string(reinterpret_cast<const char*>(message_received.payload()))); | 140 std::string(reinterpret_cast<const char*>(message_received.payload()))); |
131 } | 141 } |
(...skipping 25 matching lines...) Expand all Loading... |
157 std::string(kText), | 167 std::string(kText), |
158 std::string(reinterpret_cast<const char*>(message_received.payload()))); | 168 std::string(reinterpret_cast<const char*>(message_received.payload()))); |
159 } | 169 } |
160 | 170 |
161 TEST_F(ConnectorTest, Basic_EarlyIncomingReceiver) { | 171 TEST_F(ConnectorTest, Basic_EarlyIncomingReceiver) { |
162 internal::Connector connector0(std::move(handle0_), | 172 internal::Connector connector0(std::move(handle0_), |
163 internal::Connector::SINGLE_THREADED_SEND); | 173 internal::Connector::SINGLE_THREADED_SEND); |
164 internal::Connector connector1(std::move(handle1_), | 174 internal::Connector connector1(std::move(handle1_), |
165 internal::Connector::SINGLE_THREADED_SEND); | 175 internal::Connector::SINGLE_THREADED_SEND); |
166 | 176 |
167 MessageAccumulator accumulator; | 177 base::RunLoop run_loop; |
| 178 MessageAccumulator accumulator(run_loop.QuitClosure()); |
168 connector1.set_incoming_receiver(&accumulator); | 179 connector1.set_incoming_receiver(&accumulator); |
169 | 180 |
170 const char kText[] = "hello world"; | 181 const char kText[] = "hello world"; |
171 | 182 |
172 Message message; | 183 Message message; |
173 AllocMessage(kText, &message); | 184 AllocMessage(kText, &message); |
174 | 185 |
175 connector0.Accept(&message); | 186 connector0.Accept(&message); |
176 | 187 |
177 PumpMessages(); | 188 run_loop.Run(); |
178 | 189 |
179 ASSERT_FALSE(accumulator.IsEmpty()); | 190 ASSERT_FALSE(accumulator.IsEmpty()); |
180 | 191 |
181 Message message_received; | 192 Message message_received; |
182 accumulator.Pop(&message_received); | 193 accumulator.Pop(&message_received); |
183 | 194 |
184 EXPECT_EQ( | 195 EXPECT_EQ( |
185 std::string(kText), | 196 std::string(kText), |
186 std::string(reinterpret_cast<const char*>(message_received.payload()))); | 197 std::string(reinterpret_cast<const char*>(message_received.payload()))); |
187 } | 198 } |
188 | 199 |
189 TEST_F(ConnectorTest, Basic_TwoMessages) { | 200 TEST_F(ConnectorTest, Basic_TwoMessages) { |
190 internal::Connector connector0(std::move(handle0_), | 201 internal::Connector connector0(std::move(handle0_), |
191 internal::Connector::SINGLE_THREADED_SEND); | 202 internal::Connector::SINGLE_THREADED_SEND); |
192 internal::Connector connector1(std::move(handle1_), | 203 internal::Connector connector1(std::move(handle1_), |
193 internal::Connector::SINGLE_THREADED_SEND); | 204 internal::Connector::SINGLE_THREADED_SEND); |
194 | 205 |
195 const char* kText[] = {"hello", "world"}; | 206 const char* kText[] = {"hello", "world"}; |
196 | 207 |
197 for (size_t i = 0; i < MOJO_ARRAYSIZE(kText); ++i) { | 208 for (size_t i = 0; i < MOJO_ARRAYSIZE(kText); ++i) { |
198 Message message; | 209 Message message; |
199 AllocMessage(kText[i], &message); | 210 AllocMessage(kText[i], &message); |
200 | 211 |
201 connector0.Accept(&message); | 212 connector0.Accept(&message); |
202 } | 213 } |
203 | 214 |
204 MessageAccumulator accumulator; | 215 MessageAccumulator accumulator; |
205 connector1.set_incoming_receiver(&accumulator); | 216 connector1.set_incoming_receiver(&accumulator); |
206 | 217 |
207 PumpMessages(); | |
208 | |
209 for (size_t i = 0; i < MOJO_ARRAYSIZE(kText); ++i) { | 218 for (size_t i = 0; i < MOJO_ARRAYSIZE(kText); ++i) { |
| 219 if (accumulator.IsEmpty()) { |
| 220 base::RunLoop run_loop; |
| 221 accumulator.set_closure(run_loop.QuitClosure()); |
| 222 run_loop.Run(); |
| 223 } |
210 ASSERT_FALSE(accumulator.IsEmpty()); | 224 ASSERT_FALSE(accumulator.IsEmpty()); |
211 | 225 |
212 Message message_received; | 226 Message message_received; |
213 accumulator.Pop(&message_received); | 227 accumulator.Pop(&message_received); |
214 | 228 |
215 EXPECT_EQ( | 229 EXPECT_EQ( |
216 std::string(kText[i]), | 230 std::string(kText[i]), |
217 std::string(reinterpret_cast<const char*>(message_received.payload()))); | 231 std::string(reinterpret_cast<const char*>(message_received.payload()))); |
218 } | 232 } |
219 } | 233 } |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
266 EXPECT_FALSE(connector0.encountered_error()); | 280 EXPECT_FALSE(connector0.encountered_error()); |
267 | 281 |
268 // Write failures are not reported. | 282 // Write failures are not reported. |
269 bool ok = connector0.Accept(&message); | 283 bool ok = connector0.Accept(&message); |
270 EXPECT_TRUE(ok); | 284 EXPECT_TRUE(ok); |
271 | 285 |
272 // Still not observed. | 286 // Still not observed. |
273 EXPECT_FALSE(connector0.encountered_error()); | 287 EXPECT_FALSE(connector0.encountered_error()); |
274 | 288 |
275 // Spin the message loop, and then we should start observing the closed pipe. | 289 // Spin the message loop, and then we should start observing the closed pipe. |
276 PumpMessages(); | 290 base::RunLoop run_loop; |
| 291 connector0.set_connection_error_handler(run_loop.QuitClosure()); |
| 292 run_loop.Run(); |
277 | 293 |
278 EXPECT_TRUE(connector0.encountered_error()); | 294 EXPECT_TRUE(connector0.encountered_error()); |
279 } | 295 } |
280 | 296 |
281 TEST_F(ConnectorTest, MessageWithHandles) { | 297 TEST_F(ConnectorTest, MessageWithHandles) { |
282 internal::Connector connector0(std::move(handle0_), | 298 internal::Connector connector0(std::move(handle0_), |
283 internal::Connector::SINGLE_THREADED_SEND); | 299 internal::Connector::SINGLE_THREADED_SEND); |
284 internal::Connector connector1(std::move(handle1_), | 300 internal::Connector connector1(std::move(handle1_), |
285 internal::Connector::SINGLE_THREADED_SEND); | 301 internal::Connector::SINGLE_THREADED_SEND); |
286 | 302 |
287 const char kText[] = "hello world"; | 303 const char kText[] = "hello world"; |
288 | 304 |
289 Message message1; | 305 Message message1; |
290 AllocMessage(kText, &message1); | 306 AllocMessage(kText, &message1); |
291 | 307 |
292 MessagePipe pipe; | 308 MessagePipe pipe; |
293 message1.mutable_handles()->push_back(pipe.handle0.release()); | 309 message1.mutable_handles()->push_back(pipe.handle0.release()); |
294 | 310 |
295 connector0.Accept(&message1); | 311 connector0.Accept(&message1); |
296 | 312 |
297 // The message should have been transferred, releasing the handles. | 313 // The message should have been transferred, releasing the handles. |
298 EXPECT_TRUE(message1.handles()->empty()); | 314 EXPECT_TRUE(message1.handles()->empty()); |
299 | 315 |
300 MessageAccumulator accumulator; | 316 base::RunLoop run_loop; |
| 317 MessageAccumulator accumulator(run_loop.QuitClosure()); |
301 connector1.set_incoming_receiver(&accumulator); | 318 connector1.set_incoming_receiver(&accumulator); |
302 | 319 |
303 PumpMessages(); | 320 run_loop.Run(); |
304 | 321 |
305 ASSERT_FALSE(accumulator.IsEmpty()); | 322 ASSERT_FALSE(accumulator.IsEmpty()); |
306 | 323 |
307 Message message_received; | 324 Message message_received; |
308 accumulator.Pop(&message_received); | 325 accumulator.Pop(&message_received); |
309 | 326 |
310 EXPECT_EQ( | 327 EXPECT_EQ( |
311 std::string(kText), | 328 std::string(kText), |
312 std::string(reinterpret_cast<const char*>(message_received.payload()))); | 329 std::string(reinterpret_cast<const char*>(message_received.payload()))); |
313 ASSERT_EQ(1U, message_received.handles()->size()); | 330 ASSERT_EQ(1U, message_received.handles()->size()); |
314 | 331 |
315 // Now send a message to the transferred handle and confirm it's sent through | 332 // Now send a message to the transferred handle and confirm it's sent through |
316 // to the orginal pipe. | 333 // to the orginal pipe. |
317 // TODO(vtl): Do we need a better way of "downcasting" the handle types? | 334 // TODO(vtl): Do we need a better way of "downcasting" the handle types? |
318 ScopedMessagePipeHandle smph; | 335 ScopedMessagePipeHandle smph; |
319 smph.reset(MessagePipeHandle(message_received.handles()->front().value())); | 336 smph.reset(MessagePipeHandle(message_received.handles()->front().value())); |
320 message_received.mutable_handles()->front() = Handle(); | 337 message_received.mutable_handles()->front() = Handle(); |
321 // |smph| now owns this handle. | 338 // |smph| now owns this handle. |
322 | 339 |
323 internal::Connector connector_received( | 340 internal::Connector connector_received( |
324 std::move(smph), internal::Connector::SINGLE_THREADED_SEND); | 341 std::move(smph), internal::Connector::SINGLE_THREADED_SEND); |
325 internal::Connector connector_original( | 342 internal::Connector connector_original( |
326 std::move(pipe.handle1), internal::Connector::SINGLE_THREADED_SEND); | 343 std::move(pipe.handle1), internal::Connector::SINGLE_THREADED_SEND); |
327 | 344 |
328 Message message2; | 345 Message message2; |
329 AllocMessage(kText, &message2); | 346 AllocMessage(kText, &message2); |
330 | 347 |
331 connector_received.Accept(&message2); | 348 connector_received.Accept(&message2); |
332 connector_original.set_incoming_receiver(&accumulator); | 349 base::RunLoop run_loop2; |
333 PumpMessages(); | 350 MessageAccumulator accumulator2(run_loop2.QuitClosure()); |
| 351 connector_original.set_incoming_receiver(&accumulator2); |
| 352 run_loop2.Run(); |
334 | 353 |
335 ASSERT_FALSE(accumulator.IsEmpty()); | 354 ASSERT_FALSE(accumulator2.IsEmpty()); |
336 | 355 |
337 accumulator.Pop(&message_received); | 356 accumulator2.Pop(&message_received); |
338 | 357 |
339 EXPECT_EQ( | 358 EXPECT_EQ( |
340 std::string(kText), | 359 std::string(kText), |
341 std::string(reinterpret_cast<const char*>(message_received.payload()))); | 360 std::string(reinterpret_cast<const char*>(message_received.payload()))); |
342 } | 361 } |
343 | 362 |
344 TEST_F(ConnectorTest, WaitForIncomingMessageWithError) { | 363 TEST_F(ConnectorTest, WaitForIncomingMessageWithError) { |
345 internal::Connector connector0(std::move(handle0_), | 364 internal::Connector connector0(std::move(handle0_), |
346 internal::Connector::SINGLE_THREADED_SEND); | 365 internal::Connector::SINGLE_THREADED_SEND); |
347 // Close the other end of the pipe. | 366 // Close the other end of the pipe. |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
389 for (size_t i = 0; i < MOJO_ARRAYSIZE(kText); ++i) { | 408 for (size_t i = 0; i < MOJO_ARRAYSIZE(kText); ++i) { |
390 Message message; | 409 Message message; |
391 AllocMessage(kText[i], &message); | 410 AllocMessage(kText[i], &message); |
392 | 411 |
393 connector0.Accept(&message); | 412 connector0.Accept(&message); |
394 } | 413 } |
395 | 414 |
396 ReentrantMessageAccumulator accumulator(&connector1); | 415 ReentrantMessageAccumulator accumulator(&connector1); |
397 connector1.set_incoming_receiver(&accumulator); | 416 connector1.set_incoming_receiver(&accumulator); |
398 | 417 |
399 PumpMessages(); | |
400 | |
401 for (size_t i = 0; i < MOJO_ARRAYSIZE(kText); ++i) { | 418 for (size_t i = 0; i < MOJO_ARRAYSIZE(kText); ++i) { |
| 419 if (accumulator.IsEmpty()) { |
| 420 base::RunLoop run_loop; |
| 421 accumulator.set_closure(run_loop.QuitClosure()); |
| 422 run_loop.Run(); |
| 423 } |
402 ASSERT_FALSE(accumulator.IsEmpty()); | 424 ASSERT_FALSE(accumulator.IsEmpty()); |
403 | 425 |
404 Message message_received; | 426 Message message_received; |
405 accumulator.Pop(&message_received); | 427 accumulator.Pop(&message_received); |
406 | 428 |
407 EXPECT_EQ( | 429 EXPECT_EQ( |
408 std::string(kText[i]), | 430 std::string(kText[i]), |
409 std::string(reinterpret_cast<const char*>(message_received.payload()))); | 431 std::string(reinterpret_cast<const char*>(message_received.payload()))); |
410 } | 432 } |
411 | 433 |
412 ASSERT_EQ(2, accumulator.number_of_calls()); | 434 ASSERT_EQ(2, accumulator.number_of_calls()); |
413 } | 435 } |
414 | 436 |
415 TEST_F(ConnectorTest, RaiseError) { | 437 TEST_F(ConnectorTest, RaiseError) { |
| 438 base::RunLoop run_loop, run_loop2; |
416 internal::Connector connector0(std::move(handle0_), | 439 internal::Connector connector0(std::move(handle0_), |
417 internal::Connector::SINGLE_THREADED_SEND); | 440 internal::Connector::SINGLE_THREADED_SEND); |
418 bool error_handler_called0 = false; | 441 bool error_handler_called0 = false; |
419 connector0.set_connection_error_handler( | 442 connector0.set_connection_error_handler( |
420 [&error_handler_called0]() { error_handler_called0 = true; }); | 443 [&error_handler_called0, &run_loop]() { |
| 444 error_handler_called0 = true; |
| 445 run_loop.Quit(); |
| 446 }); |
421 | 447 |
422 internal::Connector connector1(std::move(handle1_), | 448 internal::Connector connector1(std::move(handle1_), |
423 internal::Connector::SINGLE_THREADED_SEND); | 449 internal::Connector::SINGLE_THREADED_SEND); |
424 bool error_handler_called1 = false; | 450 bool error_handler_called1 = false; |
425 connector1.set_connection_error_handler( | 451 connector1.set_connection_error_handler( |
426 [&error_handler_called1]() { error_handler_called1 = true; }); | 452 [&error_handler_called1, &run_loop2]() { |
| 453 error_handler_called1 = true; |
| 454 run_loop2.Quit(); |
| 455 }); |
427 | 456 |
428 const char kText[] = "hello world"; | 457 const char kText[] = "hello world"; |
429 | 458 |
430 Message message; | 459 Message message; |
431 AllocMessage(kText, &message); | 460 AllocMessage(kText, &message); |
432 | 461 |
433 connector0.Accept(&message); | 462 connector0.Accept(&message); |
434 connector0.RaiseError(); | 463 connector0.RaiseError(); |
435 | 464 |
436 MessageAccumulator accumulator; | 465 base::RunLoop run_loop3; |
| 466 MessageAccumulator accumulator(run_loop3.QuitClosure()); |
437 connector1.set_incoming_receiver(&accumulator); | 467 connector1.set_incoming_receiver(&accumulator); |
438 | 468 |
439 PumpMessages(); | 469 run_loop3.Run(); |
440 | 470 |
441 // Messages sent prior to RaiseError() still arrive at the other end. | 471 // Messages sent prior to RaiseError() still arrive at the other end. |
442 ASSERT_FALSE(accumulator.IsEmpty()); | 472 ASSERT_FALSE(accumulator.IsEmpty()); |
443 | 473 |
444 Message message_received; | 474 Message message_received; |
445 accumulator.Pop(&message_received); | 475 accumulator.Pop(&message_received); |
446 | 476 |
447 EXPECT_EQ( | 477 EXPECT_EQ( |
448 std::string(kText), | 478 std::string(kText), |
449 std::string(reinterpret_cast<const char*>(message_received.payload()))); | 479 std::string(reinterpret_cast<const char*>(message_received.payload()))); |
450 | 480 |
451 PumpMessages(); | 481 run_loop.Run(); |
| 482 run_loop2.Run(); |
452 | 483 |
453 // Connection error handler is called at both sides. | 484 // Connection error handler is called at both sides. |
454 EXPECT_TRUE(error_handler_called0); | 485 EXPECT_TRUE(error_handler_called0); |
455 EXPECT_TRUE(error_handler_called1); | 486 EXPECT_TRUE(error_handler_called1); |
456 | 487 |
457 // The error flag is set at both sides. | 488 // The error flag is set at both sides. |
458 EXPECT_TRUE(connector0.encountered_error()); | 489 EXPECT_TRUE(connector0.encountered_error()); |
459 EXPECT_TRUE(connector1.encountered_error()); | 490 EXPECT_TRUE(connector1.encountered_error()); |
460 | 491 |
461 // The message pipe handle is valid at both sides. | 492 // The message pipe handle is valid at both sides. |
462 EXPECT_TRUE(connector0.is_valid()); | 493 EXPECT_TRUE(connector0.is_valid()); |
463 EXPECT_TRUE(connector1.is_valid()); | 494 EXPECT_TRUE(connector1.is_valid()); |
464 } | 495 } |
465 | 496 |
466 } // namespace | 497 } // namespace |
467 } // namespace test | 498 } // namespace test |
468 } // namespace mojo | 499 } // namespace mojo |
OLD | NEW |