chromium/chrome/browser/chromeos/drivefs/drivefs_native_message_host_unittest.cc

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

#include "chrome/browser/chromeos/drivefs/drivefs_native_message_host.h"

#include <memory>

#include "base/run_loop.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/test/browser_task_environment.h"
#include "extensions/browser/api/messaging/native_message_host.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace drive {
namespace {

using base::test::RunOnceClosure;
using testing::_;
using testing::InvokeWithoutArgs;

class MockClient : public extensions::NativeMessageHost::Client {
 public:
  MockClient() = default;

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

  MOCK_METHOD(void,
              PostMessageFromNativeHost,
              (const std::string& message),
              (override));
  MOCK_METHOD(void,
              CloseChannel,
              (const std::string& error_message),
              (override));
};

class DriveFsNativeMessageHostTest
    : public testing::Test,
      public drivefs::mojom::NativeMessagingHost {
 public:
  DriveFsNativeMessageHostTest() = default;

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

  void CreateNativeHostSession(
      drivefs::mojom::ExtensionConnectionParamsPtr params,
      mojo::PendingReceiver<drivefs::mojom::NativeMessagingHost> session,
      mojo::PendingRemote<drivefs::mojom::NativeMessagingPort> port) {
    params_ = std::move(params);
    extension_port_.Bind(std::move(port));
    receiver_.Bind(std::move(session));
  }

  MOCK_METHOD(void,
              HandleMessageFromExtension,
              (const std::string& message),
              (override));

  content::BrowserTaskEnvironment task_environment_;
  TestingProfile profile_;

  drivefs::mojom::ExtensionConnectionParamsPtr params_;
  mojo::Receiver<drivefs::mojom::NativeMessagingHost> receiver_{this};
  mojo::Remote<drivefs::mojom::NativeMessagingPort> extension_port_;

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
};

TEST_F(DriveFsNativeMessageHostTest, DriveFsInitiatedMessaging) {
  base::RunLoop run_loop;

  std::unique_ptr<extensions::NativeMessageHost> host =
      CreateDriveFsInitiatedNativeMessageHostInternal(
          &profile_, extension_port_.BindNewPipeAndPassReceiver(),
          receiver_.BindNewPipeAndPassRemote());
  MockClient client;
  EXPECT_CALL(client, PostMessageFromNativeHost("foo"))
      .WillOnce(InvokeWithoutArgs([&host]() { host->OnMessage("bar"); }));
  EXPECT_CALL(*this, HandleMessageFromExtension("bar"))
      .WillOnce(InvokeWithoutArgs(
          [this]() { extension_port_->PostMessageToExtension("baz"); }));
  EXPECT_CALL(client, PostMessageFromNativeHost("baz"))
      .WillOnce(RunOnceClosure(run_loop.QuitClosure()));

  host->Start(&client);
  extension_port_->PostMessageToExtension("foo");
  run_loop.Run();
}

TEST_F(DriveFsNativeMessageHostTest, ExtensionInitiatedMessaging) {
  base::RunLoop run_loop;

  std::unique_ptr<extensions::NativeMessageHost> host =
      CreateDriveFsNativeMessageHost(
          base::BindOnce(&DriveFsNativeMessageHostTest::CreateNativeHostSession,
                         base::Unretained(this)));
  MockClient client;
  host->Start(&client);
  EXPECT_CALL(*this, HandleMessageFromExtension("foo"))
      .WillOnce(InvokeWithoutArgs(
          [this]() { extension_port_->PostMessageToExtension("bar"); }));
  EXPECT_CALL(client, PostMessageFromNativeHost("bar"))
      .WillOnce(RunOnceClosure(run_loop.QuitClosure()));
  host->OnMessage("foo");
  run_loop.Run();
}

TEST_F(DriveFsNativeMessageHostTest, NativeHostSendsMessageBeforeStart) {
  std::unique_ptr<extensions::NativeMessageHost> host =
      CreateDriveFsInitiatedNativeMessageHostInternal(
          &profile_, extension_port_.BindNewPipeAndPassReceiver(),
          receiver_.BindNewPipeAndPassRemote());
  MockClient client;

  EXPECT_CALL(client, PostMessageFromNativeHost(_)).Times(0);
  extension_port_->PostMessageToExtension("foo");
  base::RunLoop().RunUntilIdle();

  {
    base::RunLoop run_loop;
    EXPECT_CALL(client, PostMessageFromNativeHost("foo"))
        .WillOnce(InvokeWithoutArgs([&host]() { host->OnMessage("bar"); }));
    EXPECT_CALL(*this, HandleMessageFromExtension("bar"))
        .WillOnce(RunOnceClosure(run_loop.QuitClosure()));
    host->Start(&client);
    run_loop.Run();
  }
}

TEST_F(DriveFsNativeMessageHostTest, Error) {
  base::RunLoop run_loop;

  std::unique_ptr<extensions::NativeMessageHost> host =
      CreateDriveFsInitiatedNativeMessageHostInternal(
          &profile_, extension_port_.BindNewPipeAndPassReceiver(),
          receiver_.BindNewPipeAndPassRemote());
  MockClient client;
  EXPECT_CALL(*this, HandleMessageFromExtension).Times(0);
  EXPECT_CALL(client, PostMessageFromNativeHost).Times(0);
  EXPECT_CALL(client, CloseChannel("FILE_ERROR_FAILED: foo"));
  receiver_.set_disconnect_handler(run_loop.QuitClosure());

  host->Start(&client);
  extension_port_.ResetWithReason(1u, "foo");

  run_loop.Run();

  host->OnMessage("bar");
  base::RunLoop().RunUntilIdle();
}

}  // namespace
}  // namespace drive