blob: 50d268b4a39d0b18742c34dc533fc5df728b66b0 [file] [log] [blame]
// Copyright 2020 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <optional>
#include <string>
#include <vector>
#include <base/files/file_util.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <metrics/metrics_library_mock.h>
#include "dlcservice/boot/mock_boot_slot.h"
#include "dlcservice/metrics.h"
#include "dlcservice/prefs.h"
#include "dlcservice/system_state.h"
#include "dlcservice/test_utils.h"
#include "dlcservice/utils.h"
using dlcservice::metrics::InstallResult;
using testing::_;
using testing::DoAll;
using testing::ElementsAre;
using testing::Return;
using testing::SetArgPointee;
namespace dlcservice {
class DlcBaseTest : public BaseTest {
public:
DlcBaseTest() = default;
DlcBaseTest(const DlcBaseTest&) = delete;
DlcBaseTest& operator=(const DlcBaseTest&) = delete;
std::unique_ptr<DlcBase> Install(const DlcId& id) {
auto dlc = std::make_unique<DlcBase>(id);
dlc->Initialize();
EXPECT_CALL(*mock_update_engine_proxy_ptr_, SetDlcActiveValue(_, id, _, _))
.WillRepeatedly(Return(true));
EXPECT_CALL(*mock_image_loader_proxy_ptr_, LoadDlcImage(_, _, _, _, _, _))
.WillOnce(DoAll(SetArgPointee<3>(mount_path_.value()), Return(true)));
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(2);
EXPECT_CALL(*mock_metrics_,
SendInstallResult(InstallResult::kSuccessNewInstall));
EXPECT_TRUE(dlc->Install(&err_));
InstallWithUpdateEngine({id});
dlc->InstallCompleted(&err_);
dlc->FinishInstall(/*installed_by_ue=*/true, &err_);
return dlc;
}
void SetUp() override {
ON_CALL(*mock_boot_slot_ptr_, GetSlot())
.WillByDefault(Return(BootSlotInterface::Slot::A));
ON_CALL(*mock_boot_slot_ptr_, IsDeviceRemovable())
.WillByDefault(Return(false));
BaseTest::SetUp();
}
};
class DlcBaseTestRemovable : public DlcBaseTest {
public:
DlcBaseTestRemovable() = default;
DlcBaseTestRemovable(const DlcBaseTestRemovable&) = delete;
DlcBaseTestRemovable& operator=(const DlcBaseTestRemovable&) = delete;
void SetUp() override {
DlcBaseTest::SetUp();
ON_CALL(*mock_boot_slot_ptr_, IsDeviceRemovable())
.WillByDefault(Return(true));
}
};
TEST_F(DlcBaseTest, InitializationClearsMountFile) {
Prefs prefs(
JoinPaths(SystemState::Get()->dlc_prefs_dir(), kFirstDlc, kPackage));
EXPECT_TRUE(prefs.Create(kDlcRootMount));
DlcBase dlc(kFirstDlc);
dlc.Initialize();
EXPECT_FALSE(prefs.Exists(kDlcRootMount));
}
TEST_F(DlcBaseTest, InitializationReservedSpace) {
// First DLC has `reserved` set to true.
DlcBase dlc(kFirstDlc);
dlc.Initialize();
EXPECT_TRUE(base::PathExists(
dlc.GetImagePath(SystemState::Get()->active_boot_slot())));
EXPECT_TRUE(base::PathExists(
dlc.GetImagePath(SystemState::Get()->inactive_boot_slot())));
}
TEST_F(DlcBaseTest, InitializationReservedSpaceOmitted) {
// Second DLC has `reserved` set to false/missing.
DlcBase dlc(kSecondDlc);
dlc.Initialize();
EXPECT_FALSE(base::PathExists(
dlc.GetImagePath(SystemState::Get()->active_boot_slot())));
EXPECT_FALSE(base::PathExists(
dlc.GetImagePath(SystemState::Get()->inactive_boot_slot())));
}
TEST_F(DlcBaseTestRemovable, InitializationReservedSpaceOnRemovableDevice) {
// First DLC has `reserved` set to true.
DlcBase dlc(kFirstDlc);
dlc.Initialize();
EXPECT_FALSE(base::PathExists(
dlc.GetImagePath(SystemState::Get()->active_boot_slot())));
EXPECT_FALSE(base::PathExists(
dlc.GetImagePath(SystemState::Get()->inactive_boot_slot())));
}
TEST_F(DlcBaseTest, InitializationReservedSpaceDoesNotSparsifyAgain) {
// First DLC has `reserved` set to true.
DlcBase dlc(kFirstDlc);
dlc.Initialize();
auto* system_state = SystemState::Get();
auto a_img = dlc.GetImagePath(system_state->active_boot_slot());
auto b_img = dlc.GetImagePath(system_state->inactive_boot_slot());
auto a_img_size = GetFileSize(a_img);
auto b_img_size = GetFileSize(b_img);
EXPECT_TRUE(base::PathExists(a_img));
EXPECT_TRUE(base::PathExists(b_img));
EXPECT_TRUE(WriteToFile(a_img, std::string(a_img_size, '1')));
EXPECT_TRUE(WriteToFile(b_img, std::string(b_img_size, '2')));
std::vector<uint8_t> expected_hash_a, expected_hash_b;
EXPECT_TRUE(HashFile(a_img, a_img_size, &expected_hash_a));
EXPECT_TRUE(HashFile(b_img, b_img_size, &expected_hash_b));
// Mimic a reboot.
dlc.Initialize();
// On reboot, there should not be resizing + re-sparsing of images.
std::vector<uint8_t> actual_hash_a, actual_hash_b;
EXPECT_TRUE(HashFile(a_img, a_img_size, &actual_hash_a));
EXPECT_TRUE(HashFile(b_img, b_img_size, &actual_hash_b));
EXPECT_EQ(expected_hash_a, actual_hash_a);
EXPECT_EQ(expected_hash_b, actual_hash_b);
}
TEST_F(DlcBaseTest, ReinstallingNonReservedSpaceDoesNotSparsifyAgain) {
DlcBase dlc(kSecondDlc);
dlc.Initialize();
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(1);
EXPECT_TRUE(dlc.Install(&err_));
auto* system_state = SystemState::Get();
auto a_img = dlc.GetImagePath(system_state->active_boot_slot());
auto b_img = dlc.GetImagePath(system_state->inactive_boot_slot());
auto a_img_size = GetFileSize(a_img);
auto b_img_size = GetFileSize(b_img);
EXPECT_TRUE(base::PathExists(a_img));
EXPECT_TRUE(base::PathExists(b_img));
EXPECT_TRUE(WriteToFile(a_img, std::string(a_img_size, '2')));
EXPECT_TRUE(WriteToFile(b_img, std::string(b_img_size, '3')));
std::vector<uint8_t> expected_hash_a, expected_hash_b;
EXPECT_TRUE(HashFile(a_img, a_img_size, &expected_hash_a));
EXPECT_TRUE(HashFile(b_img, b_img_size, &expected_hash_b));
// Mimic re-install after reboot.
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(2);
dlc.ChangeState(DlcState::NOT_INSTALLED);
EXPECT_TRUE(dlc.Install(&err_));
// There should not be resizing + re-sparsing of images.
std::vector<uint8_t> actual_hash_a, actual_hash_b;
EXPECT_TRUE(HashFile(a_img, a_img_size, &actual_hash_a));
EXPECT_TRUE(HashFile(b_img, b_img_size, &actual_hash_b));
EXPECT_EQ(expected_hash_a, actual_hash_a);
EXPECT_EQ(expected_hash_b, actual_hash_b);
}
TEST_F(DlcBaseTest, CreateDlc) {
DlcBase dlc(kFirstDlc);
dlc.Initialize();
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(1);
EXPECT_TRUE(dlc.Install(&err_));
constexpr int expected_permissions = 0755;
int permissions;
base::FilePath module_path = JoinPaths(content_path_, kFirstDlc, kPackage);
base::GetPosixFilePermissions(module_path, &permissions);
EXPECT_EQ(permissions, expected_permissions);
base::FilePath image_a_path =
GetDlcImagePath(content_path_, kFirstDlc, kPackage, BootSlot::Slot::A);
base::GetPosixFilePermissions(image_a_path.DirName(), &permissions);
EXPECT_EQ(permissions, expected_permissions);
base::FilePath image_b_path =
GetDlcImagePath(content_path_, kFirstDlc, kPackage, BootSlot::Slot::B);
base::GetPosixFilePermissions(image_b_path.DirName(), &permissions);
EXPECT_EQ(permissions, expected_permissions);
base::FilePath dlc_prefs_path = JoinPaths(prefs_path_, "dlc", kFirstDlc);
EXPECT_TRUE(base::PathExists(dlc_prefs_path));
base::GetPosixFilePermissions(dlc_prefs_path, &permissions);
EXPECT_EQ(permissions, expected_permissions);
EXPECT_EQ(dlc.GetState().state(), DlcState::INSTALLING);
}
TEST_F(DlcBaseTest, InstallWithUECompletion) {
DlcBase dlc(kFirstDlc);
dlc.Initialize();
EXPECT_CALL(*mock_update_engine_proxy_ptr_,
SetDlcActiveValue(_, kFirstDlc, _, _))
.WillRepeatedly(Return(true));
EXPECT_CALL(*mock_image_loader_proxy_ptr_, LoadDlcImage(_, _, _, _, _, _))
.WillOnce(DoAll(SetArgPointee<3>(mount_path_.value()), Return(true)));
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(2);
EXPECT_CALL(*mock_metrics_,
SendInstallResult(InstallResult::kSuccessNewInstall));
EXPECT_TRUE(dlc.Install(&err_));
InstallWithUpdateEngine({kFirstDlc});
// UE calls this.
dlc.InstallCompleted(&err_);
EXPECT_EQ(dlc.GetState().state(), DlcState::INSTALLING);
dlc.FinishInstall(/*installed_by_ue=*/true, &err_);
EXPECT_EQ(dlc.GetState().state(), DlcState::INSTALLED);
EXPECT_TRUE(dlc.IsVerified());
}
TEST_F(DlcBaseTest, InstallWithoutUECompletion) {
DlcBase dlc(kFirstDlc);
dlc.Initialize();
EXPECT_CALL(*mock_update_engine_proxy_ptr_,
SetDlcActiveValue(_, kFirstDlc, _, _))
.WillRepeatedly(Return(true));
EXPECT_CALL(*mock_image_loader_proxy_ptr_, LoadDlcImage(_, _, _, _, _, _))
.WillOnce(DoAll(SetArgPointee<3>(mount_path_.value()), Return(true)));
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(2);
EXPECT_CALL(*mock_metrics_,
SendInstallResult(InstallResult::kSuccessNewInstall));
EXPECT_TRUE(dlc.Install(&err_));
InstallWithUpdateEngine({kFirstDlc});
// UE doesn't call InstallComplete anymore. But we still verify.
EXPECT_EQ(dlc.GetState().state(), DlcState::INSTALLING);
dlc.FinishInstall(/*installed_by_ue=*/true, &err_);
EXPECT_EQ(dlc.GetState().state(), DlcState::INSTALLED);
EXPECT_TRUE(dlc.IsVerified());
}
TEST_F(DlcBaseTest, InstallWhenInstalling) {
DlcBase dlc(kFirstDlc);
dlc.Initialize();
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(1);
EXPECT_TRUE(dlc.Install(&err_));
EXPECT_EQ(dlc.GetState().state(), DlcState::INSTALLING);
// A second install should do nothing.
EXPECT_TRUE(dlc.Install(&err_));
EXPECT_EQ(dlc.GetState().state(), DlcState::INSTALLING);
}
TEST_F(DlcBaseTest, VerifiedOnInitialization) {
DlcBase dlc(kSecondDlc);
// Explicitly set |kDlcPrefVerified| here.
std::string value;
EXPECT_TRUE(
base::ReadFileToString(SystemState::Get()->verification_file(), &value));
EXPECT_TRUE(Prefs(dlc, SystemState::Get()->active_boot_slot())
.SetKey(kDlcPrefVerified, value));
EXPECT_EQ(dlc.GetState().state(), DlcState::NOT_INSTALLED);
dlc.Initialize();
EXPECT_TRUE(dlc.IsVerified());
}
TEST_F(DlcBaseTest, StaleVerificationCheckOnInitialization) {
DlcBase dlc(kSecondDlc);
// Explicitly set |kDlcPrefVerified| here w/ stale value.
std::string value;
EXPECT_TRUE(
base::ReadFileToString(SystemState::Get()->verification_file(), &value));
EXPECT_TRUE(Prefs(dlc, SystemState::Get()->active_boot_slot())
.SetKey(kDlcPrefVerified, value + "make it stale"));
EXPECT_EQ(dlc.GetState().state(), DlcState::NOT_INSTALLED);
dlc.Initialize();
EXPECT_FALSE(dlc.IsVerified());
}
TEST_F(DlcBaseTest, InstallCompleted) {
DlcBase dlc(kSecondDlc);
dlc.Initialize();
EXPECT_FALSE(dlc.IsVerified());
EXPECT_TRUE(dlc.InstallCompleted(&err_));
EXPECT_TRUE(dlc.IsVerified());
}
TEST_F(DlcBaseTest, UpdateCompleted) {
DlcBase dlc(kSecondDlc);
dlc.Initialize();
EXPECT_TRUE(dlc.UpdateCompleted(&err_));
EXPECT_TRUE(Prefs(dlc, SystemState::Get()->inactive_boot_slot())
.Exists(kDlcPrefVerified));
}
TEST_F(DlcBaseTest, MakeReadyForUpdate) {
DlcBase dlc(kSecondDlc);
dlc.Initialize();
dlc.MarkVerified();
// Make sure the function recreates the inactive image.
auto inactive_image_path =
dlc.GetImagePath(SystemState::Get()->inactive_boot_slot());
base::DeleteFile(inactive_image_path);
EXPECT_FALSE(base::PathExists(inactive_image_path));
Prefs prefs(dlc, SystemState::Get()->inactive_boot_slot());
EXPECT_TRUE(prefs.Create(kDlcPrefVerified));
EXPECT_TRUE(dlc.MakeReadyForUpdate());
EXPECT_TRUE(base::PathExists(inactive_image_path));
EXPECT_FALSE(prefs.Exists(kDlcPrefVerified));
}
TEST_F(DlcBaseTest, MakeReadyForUpdateNotVerfied) {
DlcBase dlc(kSecondDlc);
dlc.Initialize();
Prefs prefs(dlc, SystemState::Get()->inactive_boot_slot());
EXPECT_TRUE(prefs.Create(kDlcPrefVerified));
// Since DLC is not verfied, it should return false.
EXPECT_FALSE(dlc.MakeReadyForUpdate());
EXPECT_FALSE(prefs.Exists(kDlcPrefVerified));
}
TEST_F(DlcBaseTest, OfficialBuildsDoNotPreloadDLCs) {
DlcBase dlc(kThirdDlc);
dlc.Initialize();
// Place preloaded images.
base::FilePath image_path = SetUpDlcPreloadedImage(kThirdDlc);
EXPECT_CALL(*mock_update_engine_proxy_ptr_,
SetDlcActiveValue(_, kThirdDlc, _, _))
.WillRepeatedly(Return(true));
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(1);
EXPECT_CALL(*mock_system_properties_, IsOfficialBuild())
.WillOnce(Return(true));
EXPECT_TRUE(dlc.Install(&err_));
// Instead of being preloaded, it should start installing.
EXPECT_TRUE(dlc.IsInstalling());
}
// TODO(crbug.com/1042704): Deprecate after DLCs are provisioned using TLS API.
TEST_F(DlcBaseTest, BootingFromNonRemovableDeviceKeepsPreloadedDLCs) {
DlcBase dlc(kThirdDlc);
dlc.Initialize();
// Place preloaded images.
base::FilePath image_path = SetUpDlcPreloadedImage(kThirdDlc);
EXPECT_CALL(*mock_update_engine_proxy_ptr_,
SetDlcActiveValue(_, kThirdDlc, _, _))
.WillRepeatedly(Return(true));
EXPECT_CALL(*mock_image_loader_proxy_ptr_, LoadDlcImage(_, _, _, _, _, _))
.WillOnce(DoAll(SetArgPointee<3>(mount_path_.value()), Return(true)));
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(2);
EXPECT_CALL(*mock_metrics_,
SendInstallResult(InstallResult::kSuccessAlreadyInstalled));
EXPECT_CALL(*mock_system_properties_, IsOfficialBuild())
.WillOnce(Return(false));
EXPECT_TRUE(dlc.Install(&err_));
// Preloaded DLC image should still exists.
EXPECT_TRUE(base::PathExists(image_path));
}
// TODO(crbug.com/1042704): Deprecate after DLCs are provisioned using TLS API.
TEST_F(DlcBaseTestRemovable, BootingFromRemovableDeviceKeepsPreloadedDLCs) {
DlcBase dlc(kThirdDlc);
dlc.Initialize();
// Place preloaded images.
base::FilePath image_path = SetUpDlcPreloadedImage(kThirdDlc);
EXPECT_CALL(*mock_update_engine_proxy_ptr_,
SetDlcActiveValue(_, kThirdDlc, _, _))
.WillRepeatedly(Return(true));
EXPECT_CALL(*mock_image_loader_proxy_ptr_, LoadDlcImage(_, _, _, _, _, _))
.WillOnce(DoAll(SetArgPointee<3>(mount_path_.value()), Return(true)));
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(2);
EXPECT_CALL(*mock_metrics_,
SendInstallResult(InstallResult::kSuccessAlreadyInstalled));
EXPECT_CALL(*mock_system_properties_, IsOfficialBuild())
.WillOnce(Return(false));
EXPECT_TRUE(dlc.Install(&err_));
// Preloaded DLC image should still exists.
EXPECT_TRUE(base::PathExists(image_path));
}
// TODO(crbug.com/1042704): Deprecate after DLCs are provisioned using TLS API.
TEST_F(DlcBaseTest, PreloadCopyShouldMarkUnverified) {
DlcBase dlc(kThirdDlc);
dlc.Initialize();
SetUpDlcPreloadedImage(kThirdDlc);
// Don't preload the image so we can simulate a preload failure.
EXPECT_TRUE(dlc.MarkVerified());
EXPECT_FALSE(dlc.PreloadedCopier(&err_));
EXPECT_FALSE(dlc.IsVerified());
}
// TODO(crbug.com/1042704): Deprecate after DLCs are provisioned using TLS API.
TEST_F(DlcBaseTest, PreloadCopyFailOnInvalidFileSize) {
DlcBase dlc(kThirdDlc);
dlc.Initialize();
base::FilePath image_path = SetUpDlcPreloadedImage(kThirdDlc);
EXPECT_TRUE(ResizeFile(image_path, 10));
EXPECT_TRUE(dlc.MarkVerified());
EXPECT_FALSE(dlc.PreloadedCopier(&err_));
// This failure should not render the image as unverified.
EXPECT_TRUE(dlc.IsVerified());
}
// TODO(crbug.com/1042704): Deprecate after DLCs are provisioned using TLS API.
TEST_F(DlcBaseTest, InstallingCorruptPreloadedImageCleansUp) {
DlcBase dlc(kThirdDlc);
dlc.Initialize();
base::FilePath image_path = SetUpDlcPreloadedImage(kThirdDlc);
EXPECT_TRUE(ResizeFile(image_path, 10));
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(2);
EXPECT_CALL(*mock_system_properties_, IsOfficialBuild())
.WillOnce(Return(false));
EXPECT_FALSE(dlc.Install(&err_));
for (const auto& path : {dlc.GetImagePath(BootSlot::Slot::A),
dlc.GetImagePath(BootSlot::Slot::B)})
EXPECT_FALSE(base::PathExists(path));
}
// TODO(crbug.com/1042704): Deprecate after DLCs are provisioned using TLS API.
TEST_F(DlcBaseTest, PreloadingSkippedOnAlreadyVerifiedDlc) {
DlcBase dlc(kThirdDlc);
dlc.Initialize();
// Cause |PreloadedCopier()| to fail due to size mismatch in manifest if it
// were to be called.
EXPECT_TRUE(ResizeFile(SetUpDlcPreloadedImage(kThirdDlc), 1));
SetUpDlcWithSlots(kThirdDlc);
InstallWithUpdateEngine({kThirdDlc});
EXPECT_TRUE(dlc.MarkVerified());
EXPECT_EQ(dlc.GetState().state(), DlcState::NOT_INSTALLED);
EXPECT_CALL(*mock_image_loader_proxy_ptr_,
LoadDlcImage(kThirdDlc, _, _, _, _, _))
.WillOnce(DoAll(SetArgPointee<3>(mount_path_.value()), Return(true)));
EXPECT_CALL(*mock_update_engine_proxy_ptr_,
SetDlcActiveValue(_, kThirdDlc, _, _))
.WillOnce(Return(true));
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(2);
EXPECT_CALL(*mock_metrics_,
SendInstallResult(InstallResult::kSuccessAlreadyInstalled));
EXPECT_TRUE(dlc.Install(&err_));
EXPECT_TRUE(dlc.IsInstalled());
}
// TODO(crbug.com/1042704): Deprecate after DLCs are provisioned using TLS API.
TEST_F(DlcBaseTest, PreloadingSkippedOnAlreadyExistingAndVerifiableDlc) {
DlcBase dlc(kThirdDlc);
dlc.Initialize();
// Cause |PreloadedCopier()| to fail due to size mismatch in manifest if it
// were to be called.
EXPECT_TRUE(ResizeFile(SetUpDlcPreloadedImage(kThirdDlc), 1));
SetUpDlcWithSlots(kThirdDlc);
InstallWithUpdateEngine({kThirdDlc});
EXPECT_EQ(dlc.GetState().state(), DlcState::NOT_INSTALLED);
EXPECT_CALL(*mock_image_loader_proxy_ptr_,
LoadDlcImage(kThirdDlc, _, _, _, _, _))
.WillOnce(DoAll(SetArgPointee<3>(mount_path_.value()), Return(true)));
EXPECT_CALL(*mock_update_engine_proxy_ptr_,
SetDlcActiveValue(_, kThirdDlc, _, _))
.WillOnce(Return(true));
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(2);
EXPECT_CALL(*mock_metrics_,
SendInstallResult(InstallResult::kSuccessAlreadyInstalled));
EXPECT_TRUE(dlc.Install(&err_));
EXPECT_TRUE(dlc.IsInstalled());
}
TEST_F(DlcBaseTest, FactoryInstalledImagesSupportedIntialization) {
base::FilePath factory_image_path = SetUpDlcFactoryImage(kThirdDlc);
DlcBase dlc(kThirdDlc);
dlc.Initialize();
EXPECT_TRUE(base::PathExists(factory_image_path));
}
TEST_F(DlcBaseTest, FactoryInstalledImagesUnsupportedIntialization) {
base::FilePath unsupported_factory_image_path =
SetUpDlcFactoryImage(kFirstDlc);
DlcBase dlc(kFirstDlc);
dlc.Initialize();
EXPECT_FALSE(base::PathExists(unsupported_factory_image_path));
}
TEST_F(DlcBaseTest, FactoryInstalledImageClearsAfterInstallation) {
DlcBase dlc(kThirdDlc);
dlc.Initialize();
base::FilePath factory_image_path = SetUpDlcFactoryImage(kThirdDlc);
EXPECT_TRUE(base::PathExists(factory_image_path));
EXPECT_CALL(*mock_image_loader_proxy_ptr_,
LoadDlcImage(kThirdDlc, _, _, _, _, _))
.WillOnce(DoAll(SetArgPointee<3>(mount_path_.value()), Return(true)));
EXPECT_CALL(*mock_update_engine_proxy_ptr_,
SetDlcActiveValue(_, kThirdDlc, _, _))
.WillOnce(Return(true));
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(2);
EXPECT_CALL(*mock_metrics_,
SendInstallResult(InstallResult::kSuccessAlreadyInstalled));
EXPECT_TRUE(dlc.Install(&err_));
EXPECT_TRUE(dlc.IsInstalled());
EXPECT_FALSE(base::PathExists(factory_image_path));
}
TEST_F(DlcBaseTest, FactoryInstalledImageSizeCorruption) {
DlcBase dlc(kThirdDlc);
dlc.Initialize();
base::FilePath factory_image_path = SetUpDlcFactoryImage(kThirdDlc);
EXPECT_TRUE(ResizeFile(factory_image_path, 1));
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(1);
EXPECT_CALL(*mock_system_properties_, IsOfficialBuild())
.WillOnce(Return(true));
EXPECT_TRUE(dlc.Install(&err_));
EXPECT_TRUE(dlc.IsInstalling());
EXPECT_FALSE(base::PathExists(factory_image_path));
}
TEST_F(DlcBaseTest, FactoryInstalledImageDataCorruption) {
DlcBase dlc(kThirdDlc);
dlc.Initialize();
base::FilePath factory_image_path = SetUpDlcFactoryImage(kThirdDlc);
EXPECT_TRUE(WriteToFile(factory_image_path, "foobar"));
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(1);
EXPECT_CALL(*mock_system_properties_, IsOfficialBuild())
.WillOnce(Return(true));
EXPECT_TRUE(dlc.Install(&err_));
EXPECT_TRUE(dlc.IsInstalling());
EXPECT_FALSE(base::PathExists(factory_image_path));
}
TEST_F(DlcBaseTest, HasContent) {
DlcBase dlc(kSecondDlc);
dlc.Initialize();
EXPECT_FALSE(dlc.HasContent());
SetUpDlcWithSlots(kSecondDlc);
EXPECT_TRUE(dlc.HasContent());
}
TEST_F(DlcBaseTest, GetUsedBytesOnDisk) {
DlcBase dlc(kSecondDlc);
dlc.Initialize();
EXPECT_EQ(dlc.GetUsedBytesOnDisk(), 0);
SetUpDlcWithSlots(kSecondDlc);
uint64_t expected_size = 0;
for (const auto& path : {dlc.GetImagePath(BootSlot::Slot::A),
dlc.GetImagePath(BootSlot::Slot::B)}) {
expected_size += GetFileSize(path);
}
EXPECT_GT(expected_size, 0);
EXPECT_EQ(dlc.GetUsedBytesOnDisk(), expected_size);
}
TEST_F(DlcBaseTest, MarkVerified) {
DlcBase dlc(kFirstDlc);
dlc.Initialize();
EXPECT_FALSE(dlc.IsVerified());
EXPECT_TRUE(dlc.MarkVerified());
EXPECT_TRUE(dlc.IsVerified());
EXPECT_TRUE(Prefs(DlcBase(kFirstDlc), SystemState::Get()->active_boot_slot())
.Exists(kDlcPrefVerified));
}
TEST_F(DlcBaseTest, MarkUnverified) {
DlcBase dlc(kFirstDlc);
dlc.Initialize();
EXPECT_TRUE(dlc.MarkVerified());
EXPECT_TRUE(dlc.MarkUnverified());
EXPECT_FALSE(dlc.IsVerified());
EXPECT_FALSE(Prefs(DlcBase(kFirstDlc), SystemState::Get()->active_boot_slot())
.Exists(kDlcPrefVerified));
}
TEST_F(DlcBaseTest, ImageOnDiskButNotVerifiedInstalls) {
DlcBase dlc(kSecondDlc);
dlc.Initialize();
SetUpDlcWithSlots(kSecondDlc);
InstallWithUpdateEngine({kSecondDlc});
EXPECT_EQ(dlc.GetState().state(), DlcState::NOT_INSTALLED);
EXPECT_CALL(*mock_image_loader_proxy_ptr_,
LoadDlcImage(kSecondDlc, _, _, _, _, _))
.WillOnce(DoAll(SetArgPointee<3>(mount_path_.value()), Return(true)));
EXPECT_CALL(*mock_update_engine_proxy_ptr_,
SetDlcActiveValue(_, kSecondDlc, _, _))
.WillOnce(Return(true));
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(2);
EXPECT_CALL(*mock_metrics_,
SendInstallResult(InstallResult::kSuccessAlreadyInstalled));
EXPECT_TRUE(dlc.Install(&err_));
EXPECT_TRUE(dlc.IsInstalled());
}
TEST_F(DlcBaseTest, ImageOnDiskVerifiedInstalls) {
DlcBase dlc(kSecondDlc);
EXPECT_TRUE(Prefs(dlc, SystemState::Get()->active_boot_slot())
.Create(kDlcPrefVerified));
SetUpDlcWithSlots(kSecondDlc);
InstallWithUpdateEngine({kSecondDlc});
dlc.Initialize();
EXPECT_EQ(dlc.GetState().state(), DlcState::NOT_INSTALLED);
EXPECT_CALL(*mock_image_loader_proxy_ptr_,
LoadDlcImage(kSecondDlc, _, _, _, _, _))
.WillOnce(DoAll(SetArgPointee<3>(mount_path_.value()), Return(true)));
EXPECT_CALL(*mock_update_engine_proxy_ptr_,
SetDlcActiveValue(_, kSecondDlc, _, _))
.WillOnce(Return(true));
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(2);
EXPECT_CALL(*mock_metrics_,
SendInstallResult(InstallResult::kSuccessAlreadyInstalled));
EXPECT_TRUE(dlc.Install(&err_));
EXPECT_TRUE(dlc.IsInstalled());
}
TEST_F(DlcBaseTest, VerifyDlcImageOnUEFailureToCompleteInstall) {
DlcBase dlc(kSecondDlc);
dlc.Initialize();
EXPECT_CALL(*mock_update_engine_proxy_ptr_,
SetDlcActiveValue(_, kSecondDlc, _, _))
.WillOnce(Return(true));
EXPECT_CALL(*mock_image_loader_proxy_ptr_,
LoadDlcImage(kSecondDlc, _, _, _, _, _))
.WillOnce(DoAll(SetArgPointee<3>(mount_path_.value()), Return(true)));
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(2);
EXPECT_CALL(*mock_metrics_,
SendInstallResult(InstallResult::kSuccessNewInstall));
EXPECT_TRUE(dlc.Install(&err_));
EXPECT_TRUE(dlc.IsInstalling());
// Intentionally skip over setting verified mark before |FinishInstall()|.
InstallWithUpdateEngine({kSecondDlc});
EXPECT_TRUE(dlc.FinishInstall(/*installed_by_ue=*/true, &err_));
EXPECT_TRUE(dlc.IsInstalled());
}
TEST_F(DlcBaseTest, NoImageFoundOnUEFailureToDownloadDlc) {
DlcBase dlc(kSecondDlc);
dlc.Initialize();
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(2);
EXPECT_CALL(*mock_metrics_,
SendInstallResult(InstallResult::kFailedNoImageFound));
EXPECT_TRUE(dlc.Install(&err_));
EXPECT_TRUE(dlc.IsInstalling());
// Make sure the `last_attempt_error` in update_engine is set to `kNoUpdate`.
update_engine::StatusResult ue_status;
ue_status.set_last_attempt_error(
static_cast<int32_t>(update_engine::ErrorCode::kNoUpdate));
SystemState::Get()->set_update_engine_status(ue_status);
EXPECT_FALSE(dlc.FinishInstall(/*installed_by_ue=*/true, &err_));
}
TEST_F(DlcBaseTest, DefaultState) {
DlcBase dlc(kFirstDlc);
dlc.Initialize();
dlc.mount_point_ = base::FilePath("foo-path");
DlcState state = dlc.GetState();
EXPECT_EQ(state.id(), kFirstDlc);
EXPECT_EQ(state.state(), DlcState::NOT_INSTALLED);
EXPECT_EQ(state.progress(), 0);
EXPECT_EQ(state.root_path(), "");
}
TEST_F(DlcBaseTest, ChangeStateNotInstalled) {
DlcBase dlc(kFirstDlc);
dlc.Initialize();
dlc.mount_point_ = base::FilePath("foo-path");
EXPECT_CALL(
mock_state_change_reporter_,
DlcStateChanged(CheckDlcStateProto(DlcState::NOT_INSTALLED, 0, "")));
dlc.ChangeState(DlcState::NOT_INSTALLED);
}
TEST_F(DlcBaseTest, ChangeStateInstalling) {
DlcBase dlc(kFirstDlc);
dlc.Initialize();
dlc.mount_point_ = base::FilePath("foo-path");
EXPECT_CALL(mock_state_change_reporter_,
DlcStateChanged(CheckDlcStateProto(DlcState::INSTALLING, 0, "")));
dlc.ChangeState(DlcState::INSTALLING);
}
TEST_F(DlcBaseTest, ChangeStateInstalled) {
DlcBase dlc(kFirstDlc);
dlc.Initialize();
dlc.mount_point_ = base::FilePath("foo-path");
// The |root_path| in |DlcState| should point to the root of the mount point.
EXPECT_CALL(mock_state_change_reporter_,
DlcStateChanged(CheckDlcStateProto(DlcState::INSTALLED, 1.0,
"foo-path/root")));
dlc.ChangeState(DlcState::INSTALLED);
}
TEST_F(DlcBaseTest, ChangeProgress) {
DlcBase dlc(kFirstDlc);
dlc.Initialize();
// Any state other than installing should not change the progress.
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(0);
dlc.ChangeProgress(0.5);
EXPECT_CALL(mock_state_change_reporter_,
DlcStateChanged(CheckDlcStateProto(DlcState::INSTALLING, 0, "")));
dlc.ChangeState(DlcState::INSTALLING);
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(CheckDlcStateProto(
DlcState::INSTALLING, 0.5, "")));
dlc.ChangeProgress(0.5);
// Lower progress should not send signal.
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(0);
dlc.ChangeProgress(0.3);
// Same progress should not send the signal.
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(0);
dlc.ChangeProgress(0.5);
}
TEST_F(DlcBaseTest, MountFileCreated) {
// |kFirstDlc| has 'mount-file-required' as true in the manifest.
DlcBase dlc(kFirstDlc);
SetUpDlcWithSlots(kFirstDlc);
InstallWithUpdateEngine({kFirstDlc});
dlc.Initialize();
EXPECT_CALL(*mock_update_engine_proxy_ptr_,
SetDlcActiveValue(_, kFirstDlc, _, _))
.WillRepeatedly(Return(true));
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(2);
EXPECT_CALL(*mock_image_loader_proxy_ptr_, LoadDlcImage(_, _, _, _, _, _))
.WillOnce(DoAll(SetArgPointee<3>(mount_path_.value()), Return(true)));
EXPECT_CALL(*mock_metrics_,
SendInstallResult(InstallResult::kSuccessAlreadyInstalled));
EXPECT_TRUE(dlc.Install(&err_));
EXPECT_TRUE(
Prefs(JoinPaths(SystemState::Get()->dlc_prefs_dir(), kFirstDlc, kPackage))
.Exists(kDlcRootMount));
}
TEST_F(DlcBaseTest, MountFileNotCreated) {
// |kSecondDlc| has 'mount-file-required' as false in the manifest.
DlcBase dlc(kSecondDlc);
SetUpDlcWithSlots(kSecondDlc);
InstallWithUpdateEngine({kSecondDlc});
dlc.Initialize();
EXPECT_CALL(*mock_update_engine_proxy_ptr_,
SetDlcActiveValue(_, kSecondDlc, _, _))
.WillRepeatedly(Return(true));
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(2);
EXPECT_CALL(*mock_image_loader_proxy_ptr_, LoadDlcImage(_, _, _, _, _, _))
.WillOnce(DoAll(SetArgPointee<3>(mount_path_.value()), Return(true)));
EXPECT_CALL(*mock_metrics_,
SendInstallResult(InstallResult::kSuccessAlreadyInstalled));
EXPECT_TRUE(dlc.Install(&err_));
EXPECT_FALSE(Prefs(JoinPaths(SystemState::Get()->dlc_prefs_dir(), kSecondDlc,
kPackage))
.Exists(kDlcRootMount));
}
TEST_F(DlcBaseTest, MountFileRequiredDeletionOnUninstall) {
DlcBase dlc(kFirstDlc);
SetUpDlcWithSlots(kFirstDlc);
InstallWithUpdateEngine({kFirstDlc});
dlc.Initialize();
// Process |Install()|.
EXPECT_CALL(*mock_update_engine_proxy_ptr_,
SetDlcActiveValue(_, kFirstDlc, _, _))
.WillRepeatedly(Return(true));
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(2);
EXPECT_CALL(*mock_image_loader_proxy_ptr_, LoadDlcImage(_, _, _, _, _, _))
.WillOnce(DoAll(SetArgPointee<3>(mount_path_.value()), Return(true)));
EXPECT_CALL(*mock_metrics_,
SendInstallResult(InstallResult::kSuccessAlreadyInstalled));
EXPECT_TRUE(dlc.Install(&err_));
// Process |Uninstall()| + check.
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(1);
EXPECT_CALL(*mock_image_loader_proxy_ptr_, UnloadDlcImage(_, _, _, _, _))
.WillOnce(DoAll(SetArgPointee<2>(true), Return(true)));
EXPECT_TRUE(dlc.Uninstall(&err_));
EXPECT_FALSE(
Prefs(JoinPaths(SystemState::Get()->dlc_prefs_dir(), kFirstDlc, kPackage))
.Exists(kDlcRootMount));
}
TEST_F(DlcBaseTest, UnmountClearsMountPoint) {
auto dlc = Install(kFirstDlc);
EXPECT_CALL(*mock_image_loader_proxy_ptr_, UnloadDlcImage(_, _, _, _, _))
.WillOnce(DoAll(SetArgPointee<2>(true), Return(true)));
EXPECT_TRUE(dlc->Unmount(&err_));
EXPECT_TRUE(dlc->GetRoot().empty());
}
TEST_F(DlcBaseTest, ReserveInstall) {
DlcBase dlc(kFirstDlc);
dlc.Initialize();
dlc.SetReserve(true);
EXPECT_CALL(*mock_update_engine_proxy_ptr_,
SetDlcActiveValue(_, kFirstDlc, _, _))
.WillRepeatedly(Return(true));
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(2);
EXPECT_CALL(*mock_metrics_,
SendInstallResult(InstallResult::kFailedNoImageFound));
EXPECT_TRUE(dlc.Install(&err_));
EXPECT_EQ(dlc.GetState().state(), DlcState::INSTALLING);
update_engine::StatusResult ue_status;
ue_status.set_last_attempt_error(
static_cast<int32_t>(update_engine::ErrorCode::kNoUpdate));
SystemState::Get()->set_update_engine_status(ue_status);
dlc.FinishInstall(/*installed_by_ue=*/true, &err_);
EXPECT_EQ(dlc.GetState().state(), DlcState::NOT_INSTALLED);
// DLC images should be reserved.
EXPECT_TRUE(base::PathExists(
dlc.GetImagePath(SystemState::Get()->active_boot_slot())));
EXPECT_TRUE(base::PathExists(
dlc.GetImagePath(SystemState::Get()->inactive_boot_slot())));
}
TEST_F(DlcBaseTest, UnReservedInstall) {
DlcBase dlc(kFirstDlc);
dlc.Initialize();
dlc.SetReserve(false);
EXPECT_CALL(*mock_update_engine_proxy_ptr_,
SetDlcActiveValue(_, kFirstDlc, _, _))
.WillRepeatedly(Return(true));
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(2);
EXPECT_CALL(*mock_metrics_,
SendInstallResult(InstallResult::kFailedNoImageFound));
EXPECT_TRUE(dlc.Install(&err_));
EXPECT_EQ(dlc.GetState().state(), DlcState::INSTALLING);
update_engine::StatusResult ue_status;
ue_status.set_last_attempt_error(
static_cast<int32_t>(update_engine::ErrorCode::kNoUpdate));
SystemState::Get()->set_update_engine_status(ue_status);
dlc.FinishInstall(/*installed_by_ue=*/true, &err_);
EXPECT_EQ(dlc.GetState().state(), DlcState::NOT_INSTALLED);
// DLC images should not be reserved.
EXPECT_FALSE(base::PathExists(
dlc.GetImagePath(SystemState::Get()->active_boot_slot())));
EXPECT_FALSE(base::PathExists(
dlc.GetImagePath(SystemState::Get()->inactive_boot_slot())));
}
TEST_F(DlcBaseTest, ReserveValueClearsAfterUninstall) {
DlcBase dlc(kFirstDlc);
dlc.Initialize();
dlc.SetReserve(true);
// Uninstall the DLC.
EXPECT_CALL(*mock_update_engine_proxy_ptr_,
SetDlcActiveValue(_, kFirstDlc, _, _))
.WillRepeatedly(Return(true));
EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(1);
EXPECT_CALL(*mock_image_loader_proxy_ptr_, UnloadDlcImage(_, _, _, _, _))
.WillOnce(DoAll(SetArgPointee<2>(true), Return(true)));
EXPECT_TRUE(dlc.Uninstall(&err_));
EXPECT_FALSE(dlc.SetReserve(std::nullopt));
}
} // namespace dlcservice