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 <stdint.h>
6#include <utility>
7
8#include "base/bind.h"
9#include "base/callback.h"
10#include "base/message_loop/message_loop.h"
11#include "base/run_loop.h"
12#include "mojo/public/cpp/bindings/binding.h"
13#include "mojo/public/cpp/bindings/strong_binding.h"
14#include "mojo/public/interfaces/bindings/tests/math_calculator.mojom.h"
15#include "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.h"
16#include "mojo/public/interfaces/bindings/tests/sample_service.mojom.h"
17#include "mojo/public/interfaces/bindings/tests/scoping.mojom.h"
18#include "testing/gtest/include/gtest/gtest.h"
19
20namespace mojo {
21namespace test {
22namespace {
23
24typedef base::Callback<void(double)> CalcCallback;
25
26class MathCalculatorImpl : public math::Calculator {
27 public:
28  explicit MathCalculatorImpl(InterfaceRequest<math::Calculator> request)
29      : total_(0.0), binding_(this, std::move(request)) {}
30  ~MathCalculatorImpl() override {}
31
32  void CloseMessagePipe() { binding_.Close(); }
33
34  void WaitForIncomingMethodCall() { binding_.WaitForIncomingMethodCall(); }
35
36  void Clear(const CalcCallback& callback) override {
37    total_ = 0.0;
38    callback.Run(total_);
39  }
40
41  void Add(double value, const CalcCallback& callback) override {
42    total_ += value;
43    callback.Run(total_);
44  }
45
46  void Multiply(double value, const CalcCallback& callback) override {
47    total_ *= value;
48    callback.Run(total_);
49  }
50
51 private:
52  double total_;
53  Binding<math::Calculator> binding_;
54};
55
56class MathCalculatorUI {
57 public:
58  explicit MathCalculatorUI(math::CalculatorPtr calculator)
59      : calculator_(std::move(calculator)),
60        output_(0.0) {}
61
62  bool encountered_error() const { return calculator_.encountered_error(); }
63  void set_connection_error_handler(const base::Closure& closure) {
64    calculator_.set_connection_error_handler(closure);
65  }
66
67  void Add(double value, const base::Closure& closure) {
68    calculator_->Add(
69        value,
70        base::Bind(&MathCalculatorUI::Output, base::Unretained(this), closure));
71  }
72
73  void Multiply(double value, const base::Closure& closure) {
74    calculator_->Multiply(
75        value,
76        base::Bind(&MathCalculatorUI::Output, base::Unretained(this), closure));
77  }
78
79  double GetOutput() const { return output_; }
80
81 private:
82  void Output(const base::Closure& closure, double output) {
83    output_ = output;
84    if (!closure.is_null())
85      closure.Run();
86  }
87
88  math::CalculatorPtr calculator_;
89  double output_;
90  base::Closure closure_;
91};
92
93class SelfDestructingMathCalculatorUI {
94 public:
95  explicit SelfDestructingMathCalculatorUI(math::CalculatorPtr calculator)
96      : calculator_(std::move(calculator)), nesting_level_(0) {
97    ++num_instances_;
98  }
99
100  void BeginTest(bool nested, const base::Closure& closure) {
101    nesting_level_ = nested ? 2 : 1;
102    calculator_->Add(
103        1.0,
104        base::Bind(&SelfDestructingMathCalculatorUI::Output,
105                   base::Unretained(this), closure));
106  }
107
108  static int num_instances() { return num_instances_; }
109
110  void Output(const base::Closure& closure, double value) {
111    if (--nesting_level_ > 0) {
112      // Add some more and wait for re-entrant call to Output!
113      calculator_->Add(
114          1.0,
115          base::Bind(&SelfDestructingMathCalculatorUI::Output,
116                     base::Unretained(this), closure));
117    } else {
118      closure.Run();
119      delete this;
120    }
121  }
122
123 private:
124  ~SelfDestructingMathCalculatorUI() { --num_instances_; }
125
126  math::CalculatorPtr calculator_;
127  int nesting_level_;
128  static int num_instances_;
129};
130
131// static
132int SelfDestructingMathCalculatorUI::num_instances_ = 0;
133
134class ReentrantServiceImpl : public sample::Service {
135 public:
136  ~ReentrantServiceImpl() override {}
137
138  explicit ReentrantServiceImpl(InterfaceRequest<sample::Service> request)
139      : call_depth_(0),
140        max_call_depth_(0),
141        binding_(this, std::move(request)) {}
142
143  int max_call_depth() { return max_call_depth_; }
144
145  void Frobinate(sample::FooPtr foo,
146                 sample::Service::BazOptions baz,
147                 sample::PortPtr port,
148                 const sample::Service::FrobinateCallback& callback) override {
149    max_call_depth_ = std::max(++call_depth_, max_call_depth_);
150    if (call_depth_ == 1) {
151      EXPECT_TRUE(binding_.WaitForIncomingMethodCall());
152    }
153    call_depth_--;
154    callback.Run(5);
155  }
156
157  void GetPort(mojo::InterfaceRequest<sample::Port> port) override {}
158
159 private:
160  int call_depth_;
161  int max_call_depth_;
162  Binding<sample::Service> binding_;
163};
164
165class IntegerAccessorImpl : public sample::IntegerAccessor {
166 public:
167  IntegerAccessorImpl() : integer_(0) {}
168  ~IntegerAccessorImpl() override {}
169
170  int64_t integer() const { return integer_; }
171
172  void set_closure(const base::Closure& closure) { closure_ = closure; }
173
174 private:
175  // sample::IntegerAccessor implementation.
176  void GetInteger(const GetIntegerCallback& callback) override {
177    callback.Run(integer_, sample::Enum::VALUE);
178  }
179  void SetInteger(int64_t data, sample::Enum type) override {
180    integer_ = data;
181    if (!closure_.is_null()) {
182      closure_.Run();
183      closure_.Reset();
184    }
185  }
186
187  int64_t integer_;
188  base::Closure closure_;
189};
190
191class InterfacePtrTest : public testing::Test {
192 public:
193  InterfacePtrTest() {}
194  ~InterfacePtrTest() override { base::RunLoop().RunUntilIdle(); }
195
196  void PumpMessages() { base::RunLoop().RunUntilIdle(); }
197
198 private:
199  base::MessageLoop loop_;
200};
201
202void SetFlagAndRunClosure(bool* flag, const base::Closure& closure) {
203  *flag = true;
204  closure.Run();
205}
206
207void IgnoreValueAndRunClosure(const base::Closure& closure, int32_t value) {
208  closure.Run();
209}
210
211void ExpectValueAndRunClosure(uint32_t expected_value,
212                              const base::Closure& closure,
213                              uint32_t value) {
214  EXPECT_EQ(expected_value, value);
215  closure.Run();
216}
217
218TEST_F(InterfacePtrTest, IsBound) {
219  math::CalculatorPtr calc;
220  EXPECT_FALSE(calc.is_bound());
221  MathCalculatorImpl calc_impl(GetProxy(&calc));
222  EXPECT_TRUE(calc.is_bound());
223}
224
225TEST_F(InterfacePtrTest, EndToEnd) {
226  math::CalculatorPtr calc;
227  MathCalculatorImpl calc_impl(GetProxy(&calc));
228
229  // Suppose this is instantiated in a process that has pipe1_.
230  MathCalculatorUI calculator_ui(std::move(calc));
231
232  base::RunLoop run_loop, run_loop2;
233  calculator_ui.Add(2.0, run_loop.QuitClosure());
234  calculator_ui.Multiply(5.0, run_loop2.QuitClosure());
235  run_loop.Run();
236  run_loop2.Run();
237
238  EXPECT_EQ(10.0, calculator_ui.GetOutput());
239}
240
241TEST_F(InterfacePtrTest, EndToEnd_Synchronous) {
242  math::CalculatorPtr calc;
243  MathCalculatorImpl calc_impl(GetProxy(&calc));
244
245  // Suppose this is instantiated in a process that has pipe1_.
246  MathCalculatorUI calculator_ui(std::move(calc));
247
248  EXPECT_EQ(0.0, calculator_ui.GetOutput());
249
250  base::RunLoop run_loop;
251  calculator_ui.Add(2.0, run_loop.QuitClosure());
252  EXPECT_EQ(0.0, calculator_ui.GetOutput());
253  calc_impl.WaitForIncomingMethodCall();
254  run_loop.Run();
255  EXPECT_EQ(2.0, calculator_ui.GetOutput());
256
257  base::RunLoop run_loop2;
258  calculator_ui.Multiply(5.0, run_loop2.QuitClosure());
259  EXPECT_EQ(2.0, calculator_ui.GetOutput());
260  calc_impl.WaitForIncomingMethodCall();
261  run_loop2.Run();
262  EXPECT_EQ(10.0, calculator_ui.GetOutput());
263}
264
265TEST_F(InterfacePtrTest, Movable) {
266  math::CalculatorPtr a;
267  math::CalculatorPtr b;
268  MathCalculatorImpl calc_impl(GetProxy(&b));
269
270  EXPECT_TRUE(!a);
271  EXPECT_FALSE(!b);
272
273  a = std::move(b);
274
275  EXPECT_FALSE(!a);
276  EXPECT_TRUE(!b);
277}
278
279TEST_F(InterfacePtrTest, Resettable) {
280  math::CalculatorPtr a;
281
282  EXPECT_TRUE(!a);
283
284  MessagePipe pipe;
285
286  // Save this so we can test it later.
287  Handle handle = pipe.handle0.get();
288
289  a = MakeProxy(
290      InterfacePtrInfo<math::Calculator>(std::move(pipe.handle0), 0u));
291
292  EXPECT_FALSE(!a);
293
294  a.reset();
295
296  EXPECT_TRUE(!a);
297  EXPECT_FALSE(a.internal_state()->is_bound());
298
299  // Test that handle was closed.
300  EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, CloseRaw(handle));
301}
302
303TEST_F(InterfacePtrTest, BindInvalidHandle) {
304  math::CalculatorPtr ptr;
305  EXPECT_FALSE(ptr.get());
306  EXPECT_FALSE(ptr);
307
308  ptr.Bind(InterfacePtrInfo<math::Calculator>());
309  EXPECT_FALSE(ptr.get());
310  EXPECT_FALSE(ptr);
311}
312
313TEST_F(InterfacePtrTest, EncounteredError) {
314  math::CalculatorPtr proxy;
315  MathCalculatorImpl calc_impl(GetProxy(&proxy));
316
317  MathCalculatorUI calculator_ui(std::move(proxy));
318
319  base::RunLoop run_loop;
320  calculator_ui.Add(2.0, run_loop.QuitClosure());
321  run_loop.Run();
322  EXPECT_EQ(2.0, calculator_ui.GetOutput());
323  EXPECT_FALSE(calculator_ui.encountered_error());
324
325  calculator_ui.Multiply(5.0, base::Closure());
326  EXPECT_FALSE(calculator_ui.encountered_error());
327
328  // Close the server.
329  calc_impl.CloseMessagePipe();
330
331  // The state change isn't picked up locally yet.
332  base::RunLoop run_loop2;
333  calculator_ui.set_connection_error_handler(run_loop2.QuitClosure());
334  EXPECT_FALSE(calculator_ui.encountered_error());
335
336  run_loop2.Run();
337
338  // OK, now we see the error.
339  EXPECT_TRUE(calculator_ui.encountered_error());
340}
341
342TEST_F(InterfacePtrTest, EncounteredErrorCallback) {
343  math::CalculatorPtr proxy;
344  MathCalculatorImpl calc_impl(GetProxy(&proxy));
345
346  bool encountered_error = false;
347  base::RunLoop run_loop;
348  proxy.set_connection_error_handler(
349      base::Bind(&SetFlagAndRunClosure, &encountered_error,
350                 run_loop.QuitClosure()));
351
352  MathCalculatorUI calculator_ui(std::move(proxy));
353
354  base::RunLoop run_loop2;
355  calculator_ui.Add(2.0, run_loop2.QuitClosure());
356  run_loop2.Run();
357  EXPECT_EQ(2.0, calculator_ui.GetOutput());
358  EXPECT_FALSE(calculator_ui.encountered_error());
359
360  calculator_ui.Multiply(5.0, base::Closure());
361  EXPECT_FALSE(calculator_ui.encountered_error());
362
363  // Close the server.
364  calc_impl.CloseMessagePipe();
365
366  // The state change isn't picked up locally yet.
367  EXPECT_FALSE(calculator_ui.encountered_error());
368
369  run_loop.Run();
370
371  // OK, now we see the error.
372  EXPECT_TRUE(calculator_ui.encountered_error());
373
374  // We should have also been able to observe the error through the error
375  // handler.
376  EXPECT_TRUE(encountered_error);
377}
378
379TEST_F(InterfacePtrTest, DestroyInterfacePtrOnMethodResponse) {
380  math::CalculatorPtr proxy;
381  MathCalculatorImpl calc_impl(GetProxy(&proxy));
382
383  EXPECT_EQ(0, SelfDestructingMathCalculatorUI::num_instances());
384
385  SelfDestructingMathCalculatorUI* impl =
386      new SelfDestructingMathCalculatorUI(std::move(proxy));
387  base::RunLoop run_loop;
388  impl->BeginTest(false, run_loop.QuitClosure());
389  run_loop.Run();
390
391  EXPECT_EQ(0, SelfDestructingMathCalculatorUI::num_instances());
392}
393
394TEST_F(InterfacePtrTest, NestedDestroyInterfacePtrOnMethodResponse) {
395  math::CalculatorPtr proxy;
396  MathCalculatorImpl calc_impl(GetProxy(&proxy));
397
398  EXPECT_EQ(0, SelfDestructingMathCalculatorUI::num_instances());
399
400  SelfDestructingMathCalculatorUI* impl =
401      new SelfDestructingMathCalculatorUI(std::move(proxy));
402  base::RunLoop run_loop;
403  impl->BeginTest(true, run_loop.QuitClosure());
404  run_loop.Run();
405
406  EXPECT_EQ(0, SelfDestructingMathCalculatorUI::num_instances());
407}
408
409TEST_F(InterfacePtrTest, ReentrantWaitForIncomingMethodCall) {
410  sample::ServicePtr proxy;
411  ReentrantServiceImpl impl(GetProxy(&proxy));
412
413  base::RunLoop run_loop, run_loop2;
414  proxy->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr,
415                   base::Bind(&IgnoreValueAndRunClosure,
416                              run_loop.QuitClosure()));
417  proxy->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr,
418                   base::Bind(&IgnoreValueAndRunClosure,
419                              run_loop2.QuitClosure()));
420
421  run_loop.Run();
422  run_loop2.Run();
423
424  EXPECT_EQ(2, impl.max_call_depth());
425}
426
427TEST_F(InterfacePtrTest, QueryVersion) {
428  IntegerAccessorImpl impl;
429  sample::IntegerAccessorPtr ptr;
430  Binding<sample::IntegerAccessor> binding(&impl, GetProxy(&ptr));
431
432  EXPECT_EQ(0u, ptr.version());
433
434  base::RunLoop run_loop;
435  ptr.QueryVersion(base::Bind(&ExpectValueAndRunClosure, 3u,
436                              run_loop.QuitClosure()));
437  run_loop.Run();
438
439  EXPECT_EQ(3u, ptr.version());
440}
441
442TEST_F(InterfacePtrTest, RequireVersion) {
443  IntegerAccessorImpl impl;
444  sample::IntegerAccessorPtr ptr;
445  Binding<sample::IntegerAccessor> binding(&impl, GetProxy(&ptr));
446
447  EXPECT_EQ(0u, ptr.version());
448
449  ptr.RequireVersion(1u);
450  EXPECT_EQ(1u, ptr.version());
451  base::RunLoop run_loop;
452  impl.set_closure(run_loop.QuitClosure());
453  ptr->SetInteger(123, sample::Enum::VALUE);
454  run_loop.Run();
455  EXPECT_FALSE(ptr.encountered_error());
456  EXPECT_EQ(123, impl.integer());
457
458  ptr.RequireVersion(3u);
459  EXPECT_EQ(3u, ptr.version());
460  base::RunLoop run_loop2;
461  impl.set_closure(run_loop2.QuitClosure());
462  ptr->SetInteger(456, sample::Enum::VALUE);
463  run_loop2.Run();
464  EXPECT_FALSE(ptr.encountered_error());
465  EXPECT_EQ(456, impl.integer());
466
467  // Require a version that is not supported by the impl side.
468  ptr.RequireVersion(4u);
469  // This value is set to the input of RequireVersion() synchronously.
470  EXPECT_EQ(4u, ptr.version());
471  base::RunLoop run_loop3;
472  ptr.set_connection_error_handler(run_loop3.QuitClosure());
473  ptr->SetInteger(789, sample::Enum::VALUE);
474  run_loop3.Run();
475  EXPECT_TRUE(ptr.encountered_error());
476  // The call to SetInteger() after RequireVersion(4u) is ignored.
477  EXPECT_EQ(456, impl.integer());
478}
479
480class StrongMathCalculatorImpl : public math::Calculator {
481 public:
482  StrongMathCalculatorImpl(ScopedMessagePipeHandle handle,
483                           bool* error_received,
484                           bool* destroyed,
485                           const base::Closure& closure)
486      : error_received_(error_received),
487        destroyed_(destroyed),
488        closure_(closure),
489        binding_(this, std::move(handle)) {
490    binding_.set_connection_error_handler(
491        base::Bind(&SetFlagAndRunClosure, error_received_, closure_));
492  }
493  ~StrongMathCalculatorImpl() override { *destroyed_ = true; }
494
495  // math::Calculator implementation.
496  void Clear(const CalcCallback& callback) override { callback.Run(total_); }
497
498  void Add(double value, const CalcCallback& callback) override {
499    total_ += value;
500    callback.Run(total_);
501  }
502
503  void Multiply(double value, const CalcCallback& callback) override {
504    total_ *= value;
505    callback.Run(total_);
506  }
507
508 private:
509  double total_ = 0.0;
510  bool* error_received_;
511  bool* destroyed_;
512  base::Closure closure_;
513
514  StrongBinding<math::Calculator> binding_;
515};
516
517TEST(StrongConnectorTest, Math) {
518  base::MessageLoop loop;
519
520  bool error_received = false;
521  bool destroyed = false;
522  MessagePipe pipe;
523  base::RunLoop run_loop;
524  new StrongMathCalculatorImpl(std::move(pipe.handle0), &error_received,
525                               &destroyed, run_loop.QuitClosure());
526
527  math::CalculatorPtr calc;
528  calc.Bind(InterfacePtrInfo<math::Calculator>(std::move(pipe.handle1), 0u));
529
530  {
531    // Suppose this is instantiated in a process that has the other end of the
532    // message pipe.
533    MathCalculatorUI calculator_ui(std::move(calc));
534
535    base::RunLoop run_loop, run_loop2;
536    calculator_ui.Add(2.0, run_loop.QuitClosure());
537    calculator_ui.Multiply(5.0, run_loop2.QuitClosure());
538    run_loop.Run();
539    run_loop2.Run();
540
541    EXPECT_EQ(10.0, calculator_ui.GetOutput());
542    EXPECT_FALSE(error_received);
543    EXPECT_FALSE(destroyed);
544  }
545  // Destroying calculator_ui should close the pipe and generate an error on the
546  // other
547  // end which will destroy the instance since it is strongly bound.
548
549  run_loop.Run();
550  EXPECT_TRUE(error_received);
551  EXPECT_TRUE(destroyed);
552}
553
554class WeakMathCalculatorImpl : public math::Calculator {
555 public:
556  WeakMathCalculatorImpl(ScopedMessagePipeHandle handle,
557                         bool* error_received,
558                         bool* destroyed,
559                         const base::Closure& closure)
560      : error_received_(error_received),
561        destroyed_(destroyed),
562        closure_(closure),
563        binding_(this, std::move(handle)) {
564    binding_.set_connection_error_handler(
565        base::Bind(&SetFlagAndRunClosure, error_received_, closure_));
566  }
567  ~WeakMathCalculatorImpl() override { *destroyed_ = true; }
568
569  void Clear(const CalcCallback& callback) override { callback.Run(total_); }
570
571  void Add(double value, const CalcCallback& callback) override {
572    total_ += value;
573    callback.Run(total_);
574  }
575
576  void Multiply(double value, const CalcCallback& callback) override {
577    total_ *= value;
578    callback.Run(total_);
579  }
580
581 private:
582  double total_ = 0.0;
583  bool* error_received_;
584  bool* destroyed_;
585  base::Closure closure_;
586
587  Binding<math::Calculator> binding_;
588};
589
590TEST(WeakConnectorTest, Math) {
591  base::MessageLoop loop;
592
593  bool error_received = false;
594  bool destroyed = false;
595  MessagePipe pipe;
596  base::RunLoop run_loop;
597  WeakMathCalculatorImpl impl(std::move(pipe.handle0), &error_received,
598                              &destroyed, run_loop.QuitClosure());
599
600  math::CalculatorPtr calc;
601  calc.Bind(InterfacePtrInfo<math::Calculator>(std::move(pipe.handle1), 0u));
602
603  {
604    // Suppose this is instantiated in a process that has the other end of the
605    // message pipe.
606    MathCalculatorUI calculator_ui(std::move(calc));
607
608    base::RunLoop run_loop, run_loop2;
609    calculator_ui.Add(2.0, run_loop.QuitClosure());
610    calculator_ui.Multiply(5.0, run_loop2.QuitClosure());
611    run_loop.Run();
612    run_loop2.Run();
613
614    EXPECT_EQ(10.0, calculator_ui.GetOutput());
615    EXPECT_FALSE(error_received);
616    EXPECT_FALSE(destroyed);
617    // Destroying calculator_ui should close the pipe and generate an error on
618    // the other
619    // end which will destroy the instance since it is strongly bound.
620  }
621
622  run_loop.Run();
623  EXPECT_TRUE(error_received);
624  EXPECT_FALSE(destroyed);
625}
626
627class CImpl : public C {
628 public:
629  CImpl(bool* d_called, InterfaceRequest<C> request,
630        const base::Closure& closure)
631      : d_called_(d_called), binding_(this, std::move(request)),
632        closure_(closure) {}
633  ~CImpl() override {}
634
635 private:
636  void D() override {
637    *d_called_ = true;
638    closure_.Run();
639  }
640
641  bool* d_called_;
642  StrongBinding<C> binding_;
643  base::Closure closure_;
644};
645
646class BImpl : public B {
647 public:
648  BImpl(bool* d_called, InterfaceRequest<B> request,
649        const base::Closure& closure)
650      : d_called_(d_called), binding_(this, std::move(request)),
651        closure_(closure) {}
652  ~BImpl() override {}
653
654 private:
655  void GetC(InterfaceRequest<C> c) override {
656    new CImpl(d_called_, std::move(c), closure_);
657  }
658
659  bool* d_called_;
660  StrongBinding<B> binding_;
661  base::Closure closure_;
662};
663
664class AImpl : public A {
665 public:
666  AImpl(InterfaceRequest<A> request, const base::Closure& closure)
667      : d_called_(false), binding_(this, std::move(request)),
668        closure_(closure) {}
669  ~AImpl() override {}
670
671  bool d_called() const { return d_called_; }
672
673 private:
674  void GetB(InterfaceRequest<B> b) override {
675    new BImpl(&d_called_, std::move(b), closure_);
676  }
677
678  bool d_called_;
679  Binding<A> binding_;
680  base::Closure closure_;
681};
682
683TEST_F(InterfacePtrTest, Scoping) {
684  APtr a;
685  base::RunLoop run_loop;
686  AImpl a_impl(GetProxy(&a), run_loop.QuitClosure());
687
688  EXPECT_FALSE(a_impl.d_called());
689
690  {
691    BPtr b;
692    a->GetB(GetProxy(&b));
693    CPtr c;
694    b->GetC(GetProxy(&c));
695    c->D();
696  }
697
698  // While B & C have fallen out of scope, the pipes will remain until they are
699  // flushed.
700  EXPECT_FALSE(a_impl.d_called());
701  run_loop.Run();
702  EXPECT_TRUE(a_impl.d_called());
703}
704
705class PingTestImpl : public sample::PingTest {
706 public:
707  explicit PingTestImpl(InterfaceRequest<sample::PingTest> request)
708      : binding_(this, std::move(request)) {}
709  ~PingTestImpl() override {}
710
711 private:
712  // sample::PingTest:
713  void Ping(const PingCallback& callback) override { callback.Run(); }
714
715  Binding<sample::PingTest> binding_;
716};
717
718// Tests that FuseProxy does what it's supposed to do.
719TEST_F(InterfacePtrTest, Fusion) {
720  sample::PingTestPtr proxy;
721  PingTestImpl impl(GetProxy(&proxy));
722
723  // Create another PingTest pipe.
724  sample::PingTestPtr ptr;
725  sample::PingTestRequest request = GetProxy(&ptr);
726
727  // Fuse the new pipe to the one hanging off |impl|.
728  EXPECT_TRUE(FuseInterface(std::move(request), proxy.PassInterface()));
729
730  // Ping!
731  bool called = false;
732  base::RunLoop loop;
733  ptr->Ping(base::Bind(&SetFlagAndRunClosure, &called, loop.QuitClosure()));
734  loop.Run();
735  EXPECT_TRUE(called);
736}
737
738}  // namespace
739}  // namespace test
740}  // namespace mojo
741