| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "mojo/public/cpp/bindings/lib/synchronous_connector.h" |
| |
| #include <thread> |
| #include <utility> |
| |
| #include "mojo/public/cpp/bindings/binding.h" |
| #include "mojo/public/cpp/bindings/interface_request.h" |
| #include "mojo/public/cpp/bindings/strong_binding.h" |
| #include "mojo/public/cpp/bindings/synchronous_interface_ptr.h" |
| #include "mojo/public/cpp/utility/run_loop.h" |
| #include "mojo/public/interfaces/bindings/tests/math_calculator.mojom-sync.h" |
| #include "mojo/public/interfaces/bindings/tests/math_calculator.mojom.h" |
| #include "mojo/public/interfaces/bindings/tests/scoping.mojom-sync.h" |
| #include "mojo/public/interfaces/bindings/tests/scoping.mojom.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace mojo { |
| namespace test { |
| namespace { |
| |
| using CalcCallback = mojo::Callback<void(double)>; |
| |
| // This runs in a separate thread. |
| class MathCalculatorImpl : public math::Calculator { |
| public: |
| explicit MathCalculatorImpl(InterfaceRequest<math::Calculator> request) |
| : total_(0.0), binding_(this, request.Pass()) {} |
| ~MathCalculatorImpl() override {} |
| |
| void Clear(const CalcCallback& callback) override { |
| total_ = 0.0; |
| callback.Run(total_); |
| } |
| |
| void Add(double value, const CalcCallback& callback) override { |
| total_ += value; |
| callback.Run(total_); |
| } |
| |
| void Multiply(double value, const CalcCallback& callback) override { |
| total_ *= value; |
| callback.Run(total_); |
| } |
| |
| private: |
| double total_; |
| Binding<math::Calculator> binding_; |
| }; |
| |
| class SynchronousInterfacePtrTest : public testing::Test { |
| public: |
| ~SynchronousInterfacePtrTest() override { loop_.RunUntilIdle(); } |
| |
| void PumpMessages() { loop_.RunUntilIdle(); } |
| |
| // This is meant to be passed in to an std::thread -- the thread will have its |
| // own RunLoop! |
| static void StartMathCalculator(InterfaceRequest<math::Calculator> server) { |
| // Runloop is thread-local, and this is what the MathCalculatorImpl will end |
| // up using. |
| RunLoop loop; |
| MathCalculatorImpl calc_impl(std::move(server)); |
| loop.Run(); |
| } |
| |
| private: |
| RunLoop loop_; |
| }; |
| |
| TEST_F(SynchronousInterfacePtrTest, IsBound) { |
| SynchronousInterfacePtr<math::Calculator> calc; |
| EXPECT_FALSE(calc.is_bound()); |
| EXPECT_FALSE(calc); |
| |
| MathCalculatorImpl calc_impl(GetSynchronousProxy(&calc)); |
| EXPECT_TRUE(calc.is_bound()); |
| EXPECT_TRUE(calc); |
| } |
| |
| // Do an end to end |
| TEST_F(SynchronousInterfacePtrTest, EndToEnd) { |
| SynchronousInterfacePtr<math::Calculator> calc; |
| std::thread server(StartMathCalculator, GetSynchronousProxy(&calc)); |
| |
| double out; |
| calc->Add(2.0, &out); |
| calc->Multiply(5.0, &out); |
| EXPECT_EQ(10.0, out); |
| |
| calc.PassInterfaceHandle(); |
| server.join(); |
| } |
| |
| // Move them around. |
| TEST_F(SynchronousInterfacePtrTest, Movable) { |
| SynchronousInterfacePtr<math::Calculator> a; |
| SynchronousInterfacePtr<math::Calculator> b; |
| MathCalculatorImpl calc_impl(GetSynchronousProxy(&b)); |
| |
| EXPECT_TRUE(!a); |
| EXPECT_FALSE(!b); |
| |
| a = std::move(b); |
| |
| EXPECT_FALSE(!a); |
| EXPECT_TRUE(!b); |
| } |
| |
| // Test ::reset() and the explicit bool operator. |
| TEST_F(SynchronousInterfacePtrTest, Resettable) { |
| MessagePipe pipe; |
| // Save this so we can test it later. |
| Handle handle = pipe.handle0.get(); |
| |
| SynchronousInterfacePtr<math::Calculator> a; |
| EXPECT_TRUE(!a); |
| a = SynchronousInterfacePtr<math::Calculator>::Create( |
| InterfaceHandle<math::Calculator>(std::move(pipe.handle0), 0u)); |
| EXPECT_FALSE(!a); |
| |
| a.reset(); |
| EXPECT_TRUE(!a); |
| |
| // Test that handle was closed. |
| EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, CloseRaw(handle)); |
| } |
| |
| TEST_F(SynchronousInterfacePtrTest, SetNull) { |
| MessagePipe pipe; |
| auto a = SynchronousInterfacePtr<math::Calculator>::Create( |
| InterfaceHandle<math::Calculator>(std::move(pipe.handle0), 0u)); |
| |
| EXPECT_TRUE(a); |
| a = nullptr; |
| EXPECT_FALSE(a); |
| } |
| |
| // SynchronousInterfacePtr<> will return false on method invocations if its |
| // underlying end of the pipe is dead. |
| TEST_F(SynchronousInterfacePtrTest, EncounteredError) { |
| SynchronousInterfacePtr<math::Calculator> calc; |
| { |
| MathCalculatorImpl calc_impl(GetSynchronousProxy(&calc)); |
| EXPECT_TRUE(calc.is_bound()); |
| } |
| |
| EXPECT_TRUE(calc.is_bound()); |
| |
| double out = 0.0; |
| EXPECT_FALSE(calc->Add(2.0, &out)); |
| EXPECT_EQ(0.0, out); |
| } |
| |
| class CImpl : public C { |
| public: |
| CImpl(bool* d_called, InterfaceRequest<C> request) |
| : d_called_(d_called), binding_(this, request.Pass()) {} |
| ~CImpl() override {} |
| |
| private: |
| void D() override { *d_called_ = true; } |
| |
| bool* d_called_; |
| StrongBinding<C> binding_; |
| }; |
| |
| class BImpl : public B { |
| public: |
| BImpl(bool* d_called, InterfaceRequest<B> request) |
| : d_called_(d_called), binding_(this, request.Pass()) {} |
| ~BImpl() override {} |
| |
| private: |
| void GetC(InterfaceRequest<C> c) override { new CImpl(d_called_, c.Pass()); } |
| |
| bool* d_called_; |
| StrongBinding<B> binding_; |
| }; |
| |
| class AImpl : public A { |
| public: |
| explicit AImpl(InterfaceRequest<A> request) |
| : d_called_(false), binding_(this, request.Pass()) {} |
| ~AImpl() override {} |
| |
| bool d_called() const { return d_called_; } |
| |
| private: |
| void GetB(InterfaceRequest<B> b) override { new BImpl(&d_called_, b.Pass()); } |
| |
| bool d_called_; |
| Binding<A> binding_; |
| }; |
| |
| // Test that, for synchronous method calls that don't have return args, the |
| // bindings return right away, leaving the messages in the message pipe while |
| // closing their end. |
| TEST_F(SynchronousInterfacePtrTest, Scoping) { |
| SynchronousInterfacePtr<A> a; |
| AImpl a_impl(GetSynchronousProxy(&a)); |
| |
| EXPECT_FALSE(a_impl.d_called()); |
| |
| { |
| SynchronousInterfacePtr<B> b; |
| a->GetB(GetSynchronousProxy(&b)); |
| SynchronousInterfacePtr<C> c; |
| b->GetC(GetSynchronousProxy(&c)); |
| c->D(); |
| } |
| |
| // While B & C have fallen out of scope, the service-side endpoints of the |
| // pipes will remain until they are flushed. |
| EXPECT_FALSE(a_impl.d_called()); |
| PumpMessages(); |
| EXPECT_TRUE(a_impl.d_called()); |
| } |
| |
| } // namespace |
| } // namespace test |
| } // namespace mojo |