chromium/chrome/browser/ash/crostini/crostini_shared_devices_unittest.cc

// Copyright 2022 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/ash/crostini/crostini_shared_devices.h"

#include "base/run_loop.h"
#include "base/test/test_future.h"
#include "chrome/browser/ash/crostini/crostini_manager.h"
#include "chrome/browser/ash/crostini/crostini_test_helper.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/ash/components/dbus/chunneld/chunneld_client.h"
#include "chromeos/ash/components/dbus/cicerone/fake_cicerone_client.h"
#include "chromeos/ash/components/dbus/concierge/concierge_client.h"
#include "chromeos/ash/components/dbus/seneschal/seneschal_client.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace crostini {

using vm_tools::cicerone::UpdateContainerDevicesResponse;

constexpr char kMicrophone[] = "microphone";
constexpr char kInvalidDevice[] = "invalid-device";

class CrostiniSharedDevicesTest : public testing::Test {
 public:
  CrostiniSharedDevicesTest() : container_id_(DefaultContainerId()) {}

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

  ~CrostiniSharedDevicesTest() override = default;

  void SetUp() override {
    ash::ChunneldClient::InitializeFake();
    ash::CiceroneClient::InitializeFake();
    ash::ConciergeClient::InitializeFake();
    ash::SeneschalClient::InitializeFake();
    profile_ = std::make_unique<TestingProfile>();
    test_helper_ = std::make_unique<CrostiniTestHelper>(profile_.get());
    crostini_shared_devices_ =
        std::make_unique<CrostiniSharedDevices>(profile());

    update_container_devices_response_.set_status(
        UpdateContainerDevicesResponse::OK);
    AddContainerToPrefs(profile(), container_id_, {});
  }

  void TearDown() override {
    crostini_shared_devices_.reset();
    test_helper_.reset();
    profile_.reset();
    ash::SeneschalClient::Shutdown();
    ash::ConciergeClient::Shutdown();
    ash::CiceroneClient::Shutdown();
    ash::ChunneldClient::Shutdown();
  }

 protected:
  CrostiniManager* crostini_manager() {
    return CrostiniManager::GetForProfile(profile());
  }

  Profile* profile() { return profile_.get(); }

  void set_vm_device_result(
      std::string vm_device,
      const UpdateContainerDevicesResponse::UpdateResult result) {
    auto& results_map = *update_container_devices_response_.mutable_results();
    results_map[vm_device] = result;
    ash::FakeCiceroneClient::Get()->set_update_container_devices_response(
        update_container_devices_response_);
  }

  void SetVmDeviceShared(const std::string& vm_device,
                         bool shared,
                         bool expect_applied) {
    base::test::TestFuture<bool> result_future;
    crostini_shared_devices_->SetVmDeviceShared(
        container_id_, vm_device, shared, result_future.GetCallback());
    EXPECT_EQ(expect_applied, result_future.Get());
  }

  void AddRunningContainer(const guest_os::GuestId& container_id) {
    crostini_manager()->AddRunningVmForTesting(container_id.vm_name);
    // Trigger the running container logic via
    crostini_manager()->AddRunningContainerForTesting(
        container_id_.vm_name,
        ContainerInfo(container_id.container_name, kCrostiniDefaultUsername,
                      "home/testuser1", "CONTAINER_IP_ADDRESS"),
        true);
    base::RunLoop run_loop;
    run_loop.RunUntilIdle();
  }

  guest_os::GuestId container_id_;

  vm_tools::cicerone::UpdateContainerDevicesResponse
      update_container_devices_response_;
  std::unique_ptr<CrostiniTestHelper> test_helper_;
  std::unique_ptr<TestingProfile> profile_;
  std::unique_ptr<CrostiniSharedDevices> crostini_shared_devices_;
  content::BrowserTaskEnvironment task_environment_;
};

TEST_F(CrostiniSharedDevicesTest, ShareMicSuccess) {
  AddRunningContainer(container_id_);
  EXPECT_FALSE(
      crostini_shared_devices_->IsVmDeviceShared(container_id_, kMicrophone));
  set_vm_device_result(kMicrophone, UpdateContainerDevicesResponse::SUCCESS);
  SetVmDeviceShared(kMicrophone, true, true);

  EXPECT_TRUE(
      crostini_shared_devices_->IsVmDeviceShared(container_id_, kMicrophone));
}

TEST_F(CrostiniSharedDevicesTest, ShareMicNoSuchDevice) {
  AddRunningContainer(container_id_);
  EXPECT_FALSE(
      crostini_shared_devices_->IsVmDeviceShared(container_id_, kMicrophone));
  set_vm_device_result(kMicrophone,
                       UpdateContainerDevicesResponse::NO_SUCH_VM_DEVICE);
  SetVmDeviceShared(kMicrophone, true, true);
  EXPECT_FALSE(
      crostini_shared_devices_->IsVmDeviceShared(container_id_, kMicrophone));
}

TEST_F(CrostiniSharedDevicesTest, ShareMicActionFailed) {
  AddRunningContainer(container_id_);
  EXPECT_FALSE(
      crostini_shared_devices_->IsVmDeviceShared(container_id_, kMicrophone));
  set_vm_device_result(kMicrophone,
                       UpdateContainerDevicesResponse::ACTION_FAILED);
  SetVmDeviceShared(kMicrophone, true, true);
  EXPECT_FALSE(
      crostini_shared_devices_->IsVmDeviceShared(container_id_, kMicrophone));
}

TEST_F(CrostiniSharedDevicesTest, SharedDevicesBeforeContainerRunning) {
  set_vm_device_result(kMicrophone, UpdateContainerDevicesResponse::SUCCESS);
  set_vm_device_result(kInvalidDevice,
                       UpdateContainerDevicesResponse::NO_SUCH_VM_DEVICE);

  EXPECT_FALSE(
      crostini_shared_devices_->IsVmDeviceShared(container_id_, kMicrophone));
  EXPECT_FALSE(crostini_shared_devices_->IsVmDeviceShared(container_id_,
                                                          kInvalidDevice));

  SetVmDeviceShared(kMicrophone, true, false);
  SetVmDeviceShared(kInvalidDevice, true, false);
  EXPECT_TRUE(
      crostini_shared_devices_->IsVmDeviceShared(container_id_, kMicrophone));
  EXPECT_TRUE(crostini_shared_devices_->IsVmDeviceShared(container_id_,
                                                         kInvalidDevice));

  // When the container starts running, we want it to note that there is no
  // such device as kInvalidDevice.

  AddRunningContainer(container_id_);
  EXPECT_TRUE(
      crostini_shared_devices_->IsVmDeviceShared(container_id_, kMicrophone));
  EXPECT_FALSE(crostini_shared_devices_->IsVmDeviceShared(container_id_,
                                                          kInvalidDevice));
}

}  // namespace crostini