普通文本  |  317行  |  10.07 KB

// Copyright 2014 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/router.h"

#include <utility>

#include "base/bind.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/threading/thread_task_runner_handle.h"
#include "mojo/public/cpp/bindings/tests/message_queue.h"
#include "mojo/public/cpp/bindings/tests/router_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace mojo {
namespace test {
namespace {

class RouterTest : public testing::Test {
 public:
  RouterTest() {}

  void SetUp() override {
    CreateMessagePipe(nullptr, &handle0_, &handle1_);
  }

  void TearDown() override {}

  void PumpMessages() { base::RunLoop().RunUntilIdle(); }

 protected:
  ScopedMessagePipeHandle handle0_;
  ScopedMessagePipeHandle handle1_;

 private:
  base::MessageLoop loop_;
};

TEST_F(RouterTest, BasicRequestResponse) {
  internal::Router router0(std::move(handle0_), internal::FilterChain(), false,
                           base::ThreadTaskRunnerHandle::Get());
  internal::Router router1(std::move(handle1_), internal::FilterChain(), false,
                           base::ThreadTaskRunnerHandle::Get());

  ResponseGenerator generator;
  router1.set_incoming_receiver(&generator);

  Message request;
  AllocRequestMessage(1, "hello", &request);

  MessageQueue message_queue;
  base::RunLoop run_loop;
  router0.AcceptWithResponder(
      &request, new MessageAccumulator(&message_queue, run_loop.QuitClosure()));

  run_loop.Run();

  EXPECT_FALSE(message_queue.IsEmpty());

  Message response;
  message_queue.Pop(&response);

  EXPECT_EQ(std::string("hello world!"),
            std::string(reinterpret_cast<const char*>(response.payload())));

  // Send a second message on the pipe.
  Message request2;
  AllocRequestMessage(1, "hello again", &request2);

  base::RunLoop run_loop2;
  router0.AcceptWithResponder(
      &request2,
      new MessageAccumulator(&message_queue, run_loop2.QuitClosure()));

  run_loop2.Run();

  EXPECT_FALSE(message_queue.IsEmpty());

  message_queue.Pop(&response);

  EXPECT_EQ(std::string("hello again world!"),
            std::string(reinterpret_cast<const char*>(response.payload())));
}

TEST_F(RouterTest, BasicRequestResponse_Synchronous) {
  internal::Router router0(std::move(handle0_), internal::FilterChain(), false,
                           base::ThreadTaskRunnerHandle::Get());
  internal::Router router1(std::move(handle1_), internal::FilterChain(), false,
                           base::ThreadTaskRunnerHandle::Get());

  ResponseGenerator generator;
  router1.set_incoming_receiver(&generator);

  Message request;
  AllocRequestMessage(1, "hello", &request);

  MessageQueue message_queue;
  router0.AcceptWithResponder(&request, new MessageAccumulator(&message_queue));

  router1.WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE);
  router0.WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE);

  EXPECT_FALSE(message_queue.IsEmpty());

  Message response;
  message_queue.Pop(&response);

  EXPECT_EQ(std::string("hello world!"),
            std::string(reinterpret_cast<const char*>(response.payload())));

  // Send a second message on the pipe.
  Message request2;
  AllocRequestMessage(1, "hello again", &request2);

  router0.AcceptWithResponder(&request2,
                              new MessageAccumulator(&message_queue));

  router1.WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE);
  router0.WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE);

  EXPECT_FALSE(message_queue.IsEmpty());

  message_queue.Pop(&response);

  EXPECT_EQ(std::string("hello again world!"),
            std::string(reinterpret_cast<const char*>(response.payload())));
}

TEST_F(RouterTest, RequestWithNoReceiver) {
  internal::Router router0(std::move(handle0_), internal::FilterChain(), false,
                           base::ThreadTaskRunnerHandle::Get());
  internal::Router router1(std::move(handle1_), internal::FilterChain(), false,
                           base::ThreadTaskRunnerHandle::Get());

  // Without an incoming receiver set on router1, we expect router0 to observe
  // an error as a result of sending a message.

  Message request;
  AllocRequestMessage(1, "hello", &request);

  MessageQueue message_queue;
  base::RunLoop run_loop, run_loop2;
  router0.set_connection_error_handler(run_loop.QuitClosure());
  router1.set_connection_error_handler(run_loop2.QuitClosure());
  router0.AcceptWithResponder(&request, new MessageAccumulator(&message_queue));

  run_loop.Run();
  run_loop2.Run();

  EXPECT_TRUE(router0.encountered_error());
  EXPECT_TRUE(router1.encountered_error());
  EXPECT_TRUE(message_queue.IsEmpty());
}

// Tests Router using the LazyResponseGenerator. The responses will not be
// sent until after the requests have been accepted.
TEST_F(RouterTest, LazyResponses) {
  internal::Router router0(std::move(handle0_), internal::FilterChain(), false,
                           base::ThreadTaskRunnerHandle::Get());
  internal::Router router1(std::move(handle1_), internal::FilterChain(), false,
                           base::ThreadTaskRunnerHandle::Get());

  base::RunLoop run_loop;
  LazyResponseGenerator generator(run_loop.QuitClosure());
  router1.set_incoming_receiver(&generator);

  Message request;
  AllocRequestMessage(1, "hello", &request);

  MessageQueue message_queue;
  base::RunLoop run_loop2;
  router0.AcceptWithResponder(
      &request,
      new MessageAccumulator(&message_queue, run_loop2.QuitClosure()));
  run_loop.Run();

  // The request has been received but the response has not been sent yet.
  EXPECT_TRUE(message_queue.IsEmpty());

  // Send the response.
  EXPECT_TRUE(generator.responder_is_valid());
  generator.CompleteWithResponse();
  run_loop2.Run();

  // Check the response.
  EXPECT_FALSE(message_queue.IsEmpty());
  Message response;
  message_queue.Pop(&response);
  EXPECT_EQ(std::string("hello world!"),
            std::string(reinterpret_cast<const char*>(response.payload())));

  // Send a second message on the pipe.
  base::RunLoop run_loop3;
  LazyResponseGenerator generator2(run_loop3.QuitClosure());

  router1.set_incoming_receiver(&generator2);
  Message request2;
  AllocRequestMessage(1, "hello again", &request2);

  base::RunLoop run_loop4;
  router0.AcceptWithResponder(
      &request2,
      new MessageAccumulator(&message_queue, run_loop4.QuitClosure()));
  run_loop3.Run();

  // The request has been received but the response has not been sent yet.
  EXPECT_TRUE(message_queue.IsEmpty());

  // Send the second response.
  EXPECT_TRUE(generator2.responder_is_valid());
  generator2.CompleteWithResponse();
  run_loop4.Run();

  // Check the second response.
  EXPECT_FALSE(message_queue.IsEmpty());
  message_queue.Pop(&response);
  EXPECT_EQ(std::string("hello again world!"),
            std::string(reinterpret_cast<const char*>(response.payload())));
}

void ForwardErrorHandler(bool* called, const base::Closure& callback) {
  *called = true;
  callback.Run();
}

// Tests that if the receiving application destroys the responder_ without
// sending a response, then we trigger connection error at both sides. Moreover,
// both sides still appear to have a valid message pipe handle bound.
TEST_F(RouterTest, MissingResponses) {
  base::RunLoop run_loop0, run_loop1;
  internal::Router router0(std::move(handle0_), internal::FilterChain(), false,
                           base::ThreadTaskRunnerHandle::Get());
  bool error_handler_called0 = false;
  router0.set_connection_error_handler(
      base::Bind(&ForwardErrorHandler, &error_handler_called0,
                 run_loop0.QuitClosure()));

  internal::Router router1(std::move(handle1_), internal::FilterChain(), false,
                           base::ThreadTaskRunnerHandle::Get());
  bool error_handler_called1 = false;
  router1.set_connection_error_handler(
      base::Bind(&ForwardErrorHandler, &error_handler_called1,
                 run_loop1.QuitClosure()));

  base::RunLoop run_loop3;
  LazyResponseGenerator generator(run_loop3.QuitClosure());
  router1.set_incoming_receiver(&generator);
  router1.set_incoming_receiver(&generator);

  Message request;
  AllocRequestMessage(1, "hello", &request);

  MessageQueue message_queue;
  router0.AcceptWithResponder(&request, new MessageAccumulator(&message_queue));
  run_loop3.Run();

  // The request has been received but no response has been sent.
  EXPECT_TRUE(message_queue.IsEmpty());

  // Destroy the responder MessagerReceiver but don't send any response.
  generator.CompleteWithoutResponse();
  run_loop0.Run();
  run_loop1.Run();

  // Check that no response was received.
  EXPECT_TRUE(message_queue.IsEmpty());

  // Connection error handler is called at both sides.
  EXPECT_TRUE(error_handler_called0);
  EXPECT_TRUE(error_handler_called1);

  // The error flag is set at both sides.
  EXPECT_TRUE(router0.encountered_error());
  EXPECT_TRUE(router1.encountered_error());

  // The message pipe handle is valid at both sides.
  EXPECT_TRUE(router0.is_valid());
  EXPECT_TRUE(router1.is_valid());
}

TEST_F(RouterTest, LateResponse) {
  // Test that things won't blow up if we try to send a message to a
  // MessageReceiver, which was given to us via AcceptWithResponder,
  // after the router has gone away.

  base::RunLoop run_loop;
  LazyResponseGenerator generator(run_loop.QuitClosure());
  {
    internal::Router router0(std::move(handle0_), internal::FilterChain(),
                             false, base::ThreadTaskRunnerHandle::Get());
    internal::Router router1(std::move(handle1_), internal::FilterChain(),
                             false, base::ThreadTaskRunnerHandle::Get());

    router1.set_incoming_receiver(&generator);

    Message request;
    AllocRequestMessage(1, "hello", &request);

    MessageQueue message_queue;
    router0.AcceptWithResponder(&request,
                                new MessageAccumulator(&message_queue));

    run_loop.Run();

    EXPECT_TRUE(generator.has_responder());
  }

  EXPECT_FALSE(generator.responder_is_valid());
  generator.CompleteWithResponse();  // This should end up doing nothing.
}

}  // namespace
}  // namespace test
}  // namespace mojo