blob: 098bdb11da79a7676146a83f1e22c2b6ad5310d3 [file] [log] [blame]
// Copyright 2020 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/borealis/borealis_task.h"
#include <memory>
#include "chrome/browser/ash/borealis/borealis_context.h"
#include "chrome/browser/ash/borealis/borealis_context_manager.h"
#include "chrome/browser/ash/borealis/borealis_disk_manager.h"
#include "chrome/browser/ash/borealis/borealis_metrics.h"
#include "chrome/browser/ash/borealis/testing/callback_factory.h"
#include "chrome/browser/ash/guest_os/dbus_test_helper.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/ash/components/dbus/cicerone/fake_cicerone_client.h"
#include "chromeos/ash/components/dbus/concierge/fake_concierge_client.h"
#include "chromeos/ash/components/dbus/dbus_thread_manager.h"
#include "chromeos/ash/components/dbus/dlcservice/fake_dlcservice_client.h"
#include "chromeos/ash/components/dbus/seneschal/seneschal_client.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
using ::testing::_;
using ::testing::StrNe;
namespace borealis {
namespace {
class DiskManagerMock : public BorealisDiskManager {
public:
DiskManagerMock() = default;
~DiskManagerMock() override = default;
MOCK_METHOD(void,
GetDiskInfo,
(base::OnceCallback<
void(Expected<GetDiskInfoResponse,
Described<BorealisGetDiskInfoResult>>)>),
());
MOCK_METHOD(void,
RequestSpace,
(uint64_t,
base::OnceCallback<void(
Expected<uint64_t, Described<BorealisResizeDiskResult>>)>),
());
MOCK_METHOD(void,
ReleaseSpace,
(uint64_t,
base::OnceCallback<void(
Expected<uint64_t, Described<BorealisResizeDiskResult>>)>),
());
MOCK_METHOD(void,
SyncDiskSize,
(base::OnceCallback<
void(Expected<BorealisSyncDiskSizeResult,
Described<BorealisSyncDiskSizeResult>>)>),
());
};
using CallbackFactory =
NiceCallbackFactory<void(BorealisStartupResult, std::string)>;
class BorealisTasksTest : public testing::Test,
protected guest_os::FakeVmServicesHelper {
public:
BorealisTasksTest() = default;
~BorealisTasksTest() override = default;
// Disallow copy and assign.
BorealisTasksTest(const BorealisTasksTest&) = delete;
BorealisTasksTest& operator=(const BorealisTasksTest&) = delete;
protected:
void SetUp() override {
CreateProfile();
context_ = BorealisContext::CreateBorealisContextForTesting(profile_.get());
context_->set_vm_name("borealis");
}
void TearDown() override {
context_.reset(); // must destroy before DBus shutdown
profile_.reset();
}
std::unique_ptr<TestingProfile> profile_;
std::unique_ptr<BorealisContext> context_;
content::BrowserTaskEnvironment task_environment_;
private:
void CreateProfile() {
TestingProfile::Builder profile_builder;
profile_builder.SetProfileName("defaultprofile");
profile_ = profile_builder.Build();
}
};
TEST_F(BorealisTasksTest, MountDlcSucceedsAndCallbackRanWithResults) {
FakeDlcserviceClient()->set_install_error(dlcservice::kErrorNone);
CallbackFactory callback_factory;
EXPECT_CALL(callback_factory, Call(BorealisStartupResult::kSuccess, _));
MountDlc task;
task.Run(context_.get(), callback_factory.BindOnce());
task_environment_.RunUntilIdle();
}
TEST_F(BorealisTasksTest, CreateDiskSucceedsAndCallbackRanWithResults) {
vm_tools::concierge::CreateDiskImageResponse response;
base::FilePath path = base::FilePath("test/path");
response.set_status(vm_tools::concierge::DISK_STATUS_CREATED);
response.set_disk_path(path.AsUTF8Unsafe());
FakeConciergeClient()->set_create_disk_image_response(std::move(response));
EXPECT_EQ(context_->disk_path(), base::FilePath());
CallbackFactory callback_factory;
EXPECT_CALL(callback_factory, Call(BorealisStartupResult::kSuccess, _));
CreateDiskImage task;
task.Run(context_.get(), callback_factory.BindOnce());
task_environment_.RunUntilIdle();
EXPECT_GE(FakeConciergeClient()->create_disk_image_call_count(), 1);
EXPECT_EQ(context_->disk_path(), path);
}
TEST_F(BorealisTasksTest,
CreateDiskImageAlreadyExistsAndCallbackRanWithResults) {
vm_tools::concierge::CreateDiskImageResponse response;
base::FilePath path = base::FilePath("test/path");
response.set_status(vm_tools::concierge::DISK_STATUS_EXISTS);
response.set_disk_path(path.AsUTF8Unsafe());
FakeConciergeClient()->set_create_disk_image_response(std::move(response));
EXPECT_EQ(context_->disk_path(), base::FilePath());
CallbackFactory callback_factory;
EXPECT_CALL(callback_factory, Call(BorealisStartupResult::kSuccess, _));
CreateDiskImage task;
task.Run(context_.get(), callback_factory.BindOnce());
task_environment_.RunUntilIdle();
EXPECT_GE(FakeConciergeClient()->create_disk_image_call_count(), 1);
EXPECT_EQ(context_->disk_path(), path);
}
TEST_F(BorealisTasksTest, StartBorealisVmSucceedsAndCallbackRanWithResults) {
vm_tools::concierge::StartVmResponse response;
response.set_status(vm_tools::concierge::VM_STATUS_STARTING);
FakeConciergeClient()->set_start_vm_response(std::move(response));
CallbackFactory callback_factory;
EXPECT_CALL(callback_factory, Call(BorealisStartupResult::kSuccess, _));
StartBorealisVm task;
task.Run(context_.get(), callback_factory.BindOnce());
task_environment_.RunUntilIdle();
EXPECT_GE(FakeConciergeClient()->start_vm_call_count(), 1);
}
TEST_F(BorealisTasksTest,
StartBorealisVmVmAlreadyRunningAndCallbackRanWithResults) {
vm_tools::concierge::StartVmResponse response;
response.set_status(vm_tools::concierge::VM_STATUS_RUNNING);
FakeConciergeClient()->set_start_vm_response(std::move(response));
CallbackFactory callback_factory;
EXPECT_CALL(callback_factory, Call(BorealisStartupResult::kSuccess, _));
StartBorealisVm task;
task.Run(context_.get(), callback_factory.BindOnce());
task_environment_.RunUntilIdle();
EXPECT_GE(FakeConciergeClient()->start_vm_call_count(), 1);
}
TEST_F(BorealisTasksTest,
AwaitBorealisStartupSucceedsAndCallbackRanWithResults) {
vm_tools::cicerone::ContainerStartedSignal signal;
signal.set_owner_id(
ash::ProfileHelper::GetUserIdHashFromProfile(context_->profile()));
signal.set_vm_name(context_->vm_name());
signal.set_container_name("penguin");
CallbackFactory callback_factory;
EXPECT_CALL(callback_factory, Call(BorealisStartupResult::kSuccess, _));
AwaitBorealisStartup task(context_->profile(), context_->vm_name());
task.Run(context_.get(), callback_factory.BindOnce());
FakeCiceroneClient()->NotifyContainerStarted(std::move(signal));
task_environment_.RunUntilIdle();
}
TEST_F(BorealisTasksTest,
AwaitBorealisStartupContainerAlreadyStartedAndCallbackRanWithResults) {
vm_tools::cicerone::ContainerStartedSignal signal;
signal.set_owner_id(
ash::ProfileHelper::GetUserIdHashFromProfile(context_->profile()));
signal.set_vm_name(context_->vm_name());
signal.set_container_name("penguin");
CallbackFactory callback_factory;
EXPECT_CALL(callback_factory, Call(BorealisStartupResult::kSuccess, _));
AwaitBorealisStartup task(context_->profile(), context_->vm_name());
FakeCiceroneClient()->NotifyContainerStarted(std::move(signal));
task.Run(context_.get(), callback_factory.BindOnce());
task_environment_.RunUntilIdle();
}
TEST_F(BorealisTasksTest,
AwaitBorealisStartupTimesOutAndCallbackRanWithResults) {
CallbackFactory callback_factory;
EXPECT_CALL(
callback_factory,
Call(BorealisStartupResult::kAwaitBorealisStartupFailed, StrNe("")));
AwaitBorealisStartup task(context_->profile(), context_->vm_name());
task.GetWatcherForTesting().SetTimeoutForTesting(base::Milliseconds(0));
task.Run(context_.get(), callback_factory.BindOnce());
task_environment_.RunUntilIdle();
}
TEST_F(BorealisTasksTest, SyncBorealisDiskFailureIgnored) {
auto disk_mock = std::make_unique<DiskManagerMock>();
EXPECT_CALL(*disk_mock, SyncDiskSize(_))
.WillOnce(testing::Invoke(
[](base::OnceCallback<void(
Expected<BorealisSyncDiskSizeResult,
Described<BorealisSyncDiskSizeResult>>)> callback) {
std::move(callback).Run(
Expected<BorealisSyncDiskSizeResult,
Described<BorealisSyncDiskSizeResult>>::
Unexpected(Described<BorealisSyncDiskSizeResult>(
BorealisSyncDiskSizeResult::kFailedToGetDiskInfo,
"error message")));
}));
CallbackFactory callback_factory;
EXPECT_CALL(callback_factory, Call(BorealisStartupResult::kSuccess, ""));
context_->SetDiskManagerForTesting(std::move(disk_mock));
SyncBorealisDisk task;
task.Run(context_.get(), callback_factory.BindOnce());
task_environment_.RunUntilIdle();
}
TEST_F(BorealisTasksTest, SyncBorealisDiskSucceeds) {
auto disk_mock = std::make_unique<DiskManagerMock>();
EXPECT_CALL(*disk_mock, SyncDiskSize(_))
.WillOnce(testing::Invoke(
[](base::OnceCallback<void(
Expected<BorealisSyncDiskSizeResult,
Described<BorealisSyncDiskSizeResult>>)> callback) {
std::move(callback).Run(
Expected<BorealisSyncDiskSizeResult,
Described<BorealisSyncDiskSizeResult>>(
BorealisSyncDiskSizeResult::kNoActionNeeded));
}));
CallbackFactory callback_factory;
EXPECT_CALL(callback_factory, Call(BorealisStartupResult::kSuccess, _));
context_->SetDiskManagerForTesting(std::move(disk_mock));
SyncBorealisDisk task;
task.Run(context_.get(), callback_factory.BindOnce());
task_environment_.RunUntilIdle();
}
class BorealisTasksTestDlc : public BorealisTasksTest,
public testing::WithParamInterface<std::string> {};
TEST_P(BorealisTasksTestDlc, MountDlcFailsAndCallbackRanWithResults) {
FakeDlcserviceClient()->set_install_error(GetParam());
CallbackFactory callback_factory;
EXPECT_CALL(callback_factory,
Call(BorealisStartupResult::kMountFailed, StrNe("")));
MountDlc task;
task.Run(context_.get(), callback_factory.BindOnce());
task_environment_.RunUntilIdle();
}
INSTANTIATE_TEST_SUITE_P(BorealisTasksTestDlcErrors,
BorealisTasksTestDlc,
testing::Values(dlcservice::kErrorInternal,
dlcservice::kErrorInvalidDlc,
dlcservice::kErrorBusy,
dlcservice::kErrorNeedReboot,
dlcservice::kErrorAllocation,
"unknown"));
class BorealisTasksTestDiskImage
: public BorealisTasksTest,
public testing::WithParamInterface<vm_tools::concierge::DiskImageStatus> {
};
TEST_P(BorealisTasksTestDiskImage, CreateDiskFailsAndCallbackRanWithResults) {
vm_tools::concierge::CreateDiskImageResponse response;
response.set_status(GetParam());
FakeConciergeClient()->set_create_disk_image_response(std::move(response));
EXPECT_EQ(context_->disk_path(), base::FilePath());
CallbackFactory callback_factory;
EXPECT_CALL(callback_factory,
Call(BorealisStartupResult::kDiskImageFailed, StrNe("")));
CreateDiskImage task;
task.Run(context_.get(), callback_factory.BindOnce());
task_environment_.RunUntilIdle();
EXPECT_GE(FakeConciergeClient()->create_disk_image_call_count(), 1);
EXPECT_EQ(context_->disk_path(), base::FilePath());
}
INSTANTIATE_TEST_SUITE_P(
BorealisTasksTestCreateDiskImageErrors,
BorealisTasksTestDiskImage,
testing::Values(vm_tools::concierge::DISK_STATUS_UNKNOWN,
vm_tools::concierge::DISK_STATUS_FAILED,
vm_tools::concierge::DISK_STATUS_DOES_NOT_EXIST,
vm_tools::concierge::DISK_STATUS_DESTROYED,
vm_tools::concierge::DISK_STATUS_IN_PROGRESS,
vm_tools::concierge::DISK_STATUS_RESIZED));
class BorealisTasksTestsStartBorealisVm
: public BorealisTasksTest,
public testing::WithParamInterface<vm_tools::concierge::VmStatus> {};
TEST_P(BorealisTasksTestsStartBorealisVm,
StartBorealisVmErrorsAndCallbackRanWithResults) {
vm_tools::concierge::StartVmResponse response;
response.set_status(GetParam());
FakeConciergeClient()->set_start_vm_response(std::move(response));
CallbackFactory callback_factory;
EXPECT_CALL(callback_factory,
Call(BorealisStartupResult::kStartVmFailed, StrNe("")));
StartBorealisVm task;
task.Run(context_.get(), callback_factory.BindOnce());
task_environment_.RunUntilIdle();
EXPECT_GE(FakeConciergeClient()->start_vm_call_count(), 1);
}
INSTANTIATE_TEST_SUITE_P(
BorealisTasksTestStartBorealisVmErrors,
BorealisTasksTestsStartBorealisVm,
testing::Values(vm_tools::concierge::VM_STATUS_UNKNOWN,
vm_tools::concierge::VM_STATUS_FAILURE));
} // namespace
} // namespace borealis