chromium/remoting/host/security_key/security_key_message_handler_unittest.cc

// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "remoting/host/security_key/security_key_message_handler.h"

#include <memory>
#include <string>
#include <utility>

#include "base/functional/bind.h"
#include "base/memory/weak_ptr.h"
#include "base/run_loop.h"
#include "base/test/task_environment.h"
#include "remoting/host/security_key/fake_security_key_ipc_client.h"
#include "remoting/host/security_key/fake_security_key_message_reader.h"
#include "remoting/host/security_key/fake_security_key_message_writer.h"
#include "remoting/host/security_key/security_key_ipc_constants.h"
#include "remoting/host/security_key/security_key_message.h"
#include "remoting/host/setup/test_util.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace remoting {

class SecurityKeyMessageHandlerTest : public testing::Test {
 public:
  SecurityKeyMessageHandlerTest();

  SecurityKeyMessageHandlerTest(const SecurityKeyMessageHandlerTest&) = delete;
  SecurityKeyMessageHandlerTest& operator=(
      const SecurityKeyMessageHandlerTest&) = delete;

  ~SecurityKeyMessageHandlerTest() override;

  // Passed to the object used for testing to be called back to signal
  // completion of an action.
  void OperationComplete();

 protected:
  // testing::Test interface.
  void SetUp() override;

  // Waits until the current |run_loop_| instance is signaled, then resets it.
  void WaitForOperationComplete();

  // Passed to |message_channel_| and called back when a message is received.
  void OnSecurityKeyMessage(SecurityKeyMessageType message_type,
                            const std::string& message_payload);

  bool last_operation_failed_ = false;
  SecurityKeyMessageType last_message_type_received_ =
      SecurityKeyMessageType::INVALID;
  std::string last_message_payload_received_;

  base::WeakPtr<FakeSecurityKeyIpcClient> ipc_client_weak_ptr_;
  base::WeakPtr<FakeSecurityKeyMessageReader> reader_weak_ptr_;
  base::WeakPtr<FakeSecurityKeyMessageWriter> writer_weak_ptr_;
  std::unique_ptr<SecurityKeyMessageHandler> message_handler_;

 private:
  base::test::SingleThreadTaskEnvironment task_environment_{
      base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
  std::unique_ptr<base::RunLoop> run_loop_;
};

SecurityKeyMessageHandlerTest::SecurityKeyMessageHandlerTest() = default;

SecurityKeyMessageHandlerTest::~SecurityKeyMessageHandlerTest() = default;

void SecurityKeyMessageHandlerTest::OperationComplete() {
  run_loop_->Quit();
}

void SecurityKeyMessageHandlerTest::SetUp() {
  run_loop_ = std::make_unique<base::RunLoop>();
  message_handler_ = std::make_unique<SecurityKeyMessageHandler>();

  auto ipc_client = std::make_unique<FakeSecurityKeyIpcClient>(
      base::BindRepeating(&SecurityKeyMessageHandlerTest::OperationComplete,
                          base::Unretained(this)));
  ipc_client_weak_ptr_ = ipc_client->AsWeakPtr();

  auto reader = std::make_unique<FakeSecurityKeyMessageReader>();
  reader_weak_ptr_ = reader->AsWeakPtr();

  auto writer = std::make_unique<FakeSecurityKeyMessageWriter>(
      base::BindRepeating(&SecurityKeyMessageHandlerTest::OperationComplete,
                          base::Unretained(this)));
  writer_weak_ptr_ = writer->AsWeakPtr();

  message_handler_->SetSecurityKeyMessageReaderForTest(std::move(reader));

  message_handler_->SetSecurityKeyMessageWriterForTest(std::move(writer));

  base::File read_file;
  base::File write_file;
  ASSERT_TRUE(MakePipe(&read_file, &write_file));
  message_handler_->Start(
      std::move(read_file), std::move(write_file), std::move(ipc_client),
      base::BindOnce(&SecurityKeyMessageHandlerTest::OperationComplete,
                     base::Unretained(this)));
}

void SecurityKeyMessageHandlerTest::WaitForOperationComplete() {
  run_loop_->Run();
  run_loop_ = std::make_unique<base::RunLoop>();
}

void SecurityKeyMessageHandlerTest::OnSecurityKeyMessage(
    SecurityKeyMessageType message_type,
    const std::string& message_payload) {
  last_message_type_received_ = message_type;
  last_message_payload_received_ = message_payload;

  OperationComplete();
}

TEST_F(SecurityKeyMessageHandlerTest,
       ProcessConnectMessage_SessionExists_ConnectionAttemptSuccess) {
  ipc_client_weak_ptr_->set_check_for_ipc_channel_return_value(true);
  ipc_client_weak_ptr_->set_establish_ipc_connection_should_succeed(true);

  reader_weak_ptr_->message_callback().Run(
      SecurityKeyMessage::CreateMessageForTest(SecurityKeyMessageType::CONNECT,
                                               std::string()));
  WaitForOperationComplete();

  ASSERT_EQ(SecurityKeyMessageType::CONNECT_RESPONSE,
            writer_weak_ptr_->last_message_type());
  ASSERT_EQ(std::string(1, kConnectResponseActiveSession),
            writer_weak_ptr_->last_message_payload());
}

TEST_F(SecurityKeyMessageHandlerTest,
       ProcessConnectMessage_SessionExists_WriteFails) {
  ipc_client_weak_ptr_->set_check_for_ipc_channel_return_value(true);
  ipc_client_weak_ptr_->set_establish_ipc_connection_should_succeed(true);
  writer_weak_ptr_->set_write_request_succeeded(/*should_succeed=*/false);

  reader_weak_ptr_->message_callback().Run(
      SecurityKeyMessage::CreateMessageForTest(SecurityKeyMessageType::CONNECT,
                                               std::string()));
  WaitForOperationComplete();

  ASSERT_FALSE(ipc_client_weak_ptr_.get());
  ASSERT_FALSE(reader_weak_ptr_.get());
  ASSERT_FALSE(writer_weak_ptr_.get());
}

TEST_F(SecurityKeyMessageHandlerTest,
       ProcessConnectMessage_SessionExists_ConnectionAttemptFailure) {
  ipc_client_weak_ptr_->set_check_for_ipc_channel_return_value(true);
  ipc_client_weak_ptr_->set_establish_ipc_connection_should_succeed(false);

  reader_weak_ptr_->message_callback().Run(
      SecurityKeyMessage::CreateMessageForTest(SecurityKeyMessageType::CONNECT,
                                               std::string()));
  WaitForOperationComplete();

  ASSERT_EQ(SecurityKeyMessageType::CONNECT_ERROR,
            writer_weak_ptr_->last_message_type());
  ASSERT_FALSE(writer_weak_ptr_->last_message_payload().empty());
}

TEST_F(SecurityKeyMessageHandlerTest, ProcessConnectMessage_NoSessionExists) {
  ipc_client_weak_ptr_->set_check_for_ipc_channel_return_value(false);
  ipc_client_weak_ptr_->set_establish_ipc_connection_should_succeed(false);

  reader_weak_ptr_->message_callback().Run(
      SecurityKeyMessage::CreateMessageForTest(SecurityKeyMessageType::CONNECT,
                                               std::string()));
  WaitForOperationComplete();

  ASSERT_EQ(SecurityKeyMessageType::CONNECT_RESPONSE,
            writer_weak_ptr_->last_message_type());
  ASSERT_EQ(std::string(1, kConnectResponseNoSession),
            writer_weak_ptr_->last_message_payload());
}

TEST_F(SecurityKeyMessageHandlerTest, ProcessConnectMessage_IncorrectPayload) {
  ipc_client_weak_ptr_->set_check_for_ipc_channel_return_value(true);
  ipc_client_weak_ptr_->set_establish_ipc_connection_should_succeed(false);

  reader_weak_ptr_->message_callback().Run(
      SecurityKeyMessage::CreateMessageForTest(SecurityKeyMessageType::CONNECT,
                                               "Invalid request payload"));
  WaitForOperationComplete();

  ASSERT_EQ(SecurityKeyMessageType::CONNECT_ERROR,
            writer_weak_ptr_->last_message_type());
  ASSERT_FALSE(writer_weak_ptr_->last_message_payload().empty());
}

TEST_F(SecurityKeyMessageHandlerTest,
       ProcessRequestMessage_ValidPayload_IpcSendSuccess) {
  std::string request_payload("I AM A VALID REQUEST PAYLOAD!");
  std::string response_payload("I AM A VALID RESPONSE PAYLOAD!");
  ipc_client_weak_ptr_->set_send_security_request_should_succeed(true);
  ipc_client_weak_ptr_->set_security_key_response_payload(response_payload);

  reader_weak_ptr_->message_callback().Run(
      SecurityKeyMessage::CreateMessageForTest(SecurityKeyMessageType::REQUEST,
                                               request_payload));
  WaitForOperationComplete();

  ASSERT_EQ(SecurityKeyMessageType::REQUEST_RESPONSE,
            writer_weak_ptr_->last_message_type());
  ASSERT_EQ(response_payload, writer_weak_ptr_->last_message_payload());
}

TEST_F(SecurityKeyMessageHandlerTest, ProcessRequestMessage_WriteFails) {
  std::string request_payload("I AM A VALID REQUEST PAYLOAD!");
  std::string response_payload("I AM A VALID RESPONSE PAYLOAD!");

  ipc_client_weak_ptr_->set_send_security_request_should_succeed(true);
  ipc_client_weak_ptr_->set_security_key_response_payload(response_payload);
  writer_weak_ptr_->set_write_request_succeeded(/*should_succeed=*/false);

  reader_weak_ptr_->message_callback().Run(
      SecurityKeyMessage::CreateMessageForTest(SecurityKeyMessageType::REQUEST,
                                               request_payload));
  WaitForOperationComplete();

  ASSERT_FALSE(ipc_client_weak_ptr_.get());
  ASSERT_FALSE(reader_weak_ptr_.get());
  ASSERT_FALSE(writer_weak_ptr_.get());
}

TEST_F(SecurityKeyMessageHandlerTest,
       ProcessRequestMessage_ValidPayload_IpcSendFailure) {
  std::string request_payload("I AM A VALID REQUEST PAYLOAD!");
  ipc_client_weak_ptr_->set_send_security_request_should_succeed(false);

  reader_weak_ptr_->message_callback().Run(
      SecurityKeyMessage::CreateMessageForTest(SecurityKeyMessageType::REQUEST,
                                               request_payload));
  WaitForOperationComplete();

  ASSERT_EQ(SecurityKeyMessageType::REQUEST_ERROR,
            writer_weak_ptr_->last_message_type());
  ASSERT_FALSE(writer_weak_ptr_->last_message_payload().empty());
}

TEST_F(SecurityKeyMessageHandlerTest,
       ProcessRequestMessage_ValidPayload_EmptyClientResponse) {
  std::string request_payload("I AM A VALID REQUEST PAYLOAD!");
  std::string response_payload("");
  ipc_client_weak_ptr_->set_send_security_request_should_succeed(true);
  ipc_client_weak_ptr_->set_security_key_response_payload(response_payload);

  reader_weak_ptr_->message_callback().Run(
      SecurityKeyMessage::CreateMessageForTest(SecurityKeyMessageType::REQUEST,
                                               request_payload));
  WaitForOperationComplete();

  ASSERT_EQ(SecurityKeyMessageType::REQUEST_ERROR,
            writer_weak_ptr_->last_message_type());
  ASSERT_FALSE(writer_weak_ptr_->last_message_payload().empty());
}

TEST_F(SecurityKeyMessageHandlerTest,
       ProcessRequestMessage_ValidPayload_ClientResponseError) {
  std::string request_payload("I AM A VALID REQUEST PAYLOAD!");
  std::string response_payload(kSecurityKeyConnectionError);
  ipc_client_weak_ptr_->set_send_security_request_should_succeed(true);
  ipc_client_weak_ptr_->set_security_key_response_payload(response_payload);

  reader_weak_ptr_->message_callback().Run(
      SecurityKeyMessage::CreateMessageForTest(SecurityKeyMessageType::REQUEST,
                                               request_payload));
  WaitForOperationComplete();

  ASSERT_EQ(SecurityKeyMessageType::REQUEST_ERROR,
            writer_weak_ptr_->last_message_type());
  ASSERT_FALSE(writer_weak_ptr_->last_message_payload().empty());
}

TEST_F(SecurityKeyMessageHandlerTest, ProcessRequestMessage_InvalidPayload) {
  std::string invalid_payload("");
  reader_weak_ptr_->message_callback().Run(
      SecurityKeyMessage::CreateMessageForTest(SecurityKeyMessageType::REQUEST,
                                               invalid_payload));
  WaitForOperationComplete();

  ASSERT_EQ(SecurityKeyMessageType::REQUEST_ERROR,
            writer_weak_ptr_->last_message_type());
  ASSERT_FALSE(writer_weak_ptr_->last_message_payload().empty());
}

TEST_F(SecurityKeyMessageHandlerTest, ProcessUnknownMessage) {
  reader_weak_ptr_->message_callback().Run(
      SecurityKeyMessage::CreateMessageForTest(
          SecurityKeyMessageType::UNKNOWN_ERROR, std::string()));
  WaitForOperationComplete();

  ASSERT_EQ(SecurityKeyMessageType::UNKNOWN_COMMAND,
            writer_weak_ptr_->last_message_type());
}

}  // namespace remoting