| // Copyright 2020 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <memory> |
| #include <string> |
| |
| #include <base/time/time.h> |
| #include <gtest/gtest.h> |
| |
| #include "dlcservice/ref_count.h" |
| #include "dlcservice/test_utils.h" |
| #include "dlcservice/utils.h" |
| |
| using dlcservice::metrics::InstallResult; |
| using std::string; |
| using testing::_; |
| using testing::ElementsAre; |
| using testing::Return; |
| using testing::SetArgPointee; |
| |
| namespace dlcservice { |
| |
| class DlcManagerTest : public BaseTest { |
| public: |
| DlcManagerTest() { dlc_manager_ = std::make_unique<DlcManager>(); } |
| |
| void Install(const DlcId& id) { |
| EXPECT_CALL(*mock_image_loader_proxy_ptr_, LoadDlcImage(id, _, _, _, _, _)) |
| .WillOnce(DoAll(SetArgPointee<3>(mount_path_.value()), Return(true))); |
| EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(2); |
| EXPECT_CALL(*mock_update_engine_proxy_ptr_, |
| SetDlcActiveValue(true, id, _, _)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*mock_metrics_, |
| SendInstallResult(InstallResult::kSuccessNewInstall)); |
| |
| bool external_install_needed = false; |
| EXPECT_TRUE(dlc_manager_->Install(id, &external_install_needed, &err_)); |
| CheckDlcState(id, DlcState::INSTALLING); |
| |
| InstallWithUpdateEngine({id}); |
| EXPECT_TRUE(dlc_manager_->InstallCompleted({id}, &err_)); |
| EXPECT_TRUE(dlc_manager_->FinishInstall(id, &err_)); |
| CheckDlcState(id, DlcState::INSTALLED); |
| } |
| |
| void Uninstall(const DlcId& id) { |
| EXPECT_CALL(*mock_image_loader_proxy_ptr_, UnloadDlcImage(_, _, _, _, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(true), Return(true))); |
| EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(1); |
| EXPECT_TRUE(dlc_manager_->Uninstall(id, &err_)); |
| CheckDlcState(id, DlcState::NOT_INSTALLED); |
| } |
| |
| void CheckDlcState(const DlcId& id, const DlcState::State& expected_state) { |
| const auto* dlc = dlc_manager_->GetDlc(id, &err_); |
| EXPECT_NE(dlc, nullptr); |
| EXPECT_EQ(expected_state, dlc->GetState().state()); |
| } |
| |
| protected: |
| std::unique_ptr<DlcManager> dlc_manager_; |
| |
| private: |
| DlcManagerTest(const DlcManagerTest&) = delete; |
| DlcManagerTest& operator=(const DlcManagerTest&) = delete; |
| }; |
| |
| TEST_F(DlcManagerTest, PreloadAllowedDlcTest) { |
| // The third DLC has pre-loaded flag on. |
| SetUpDlcPreloadedImage(kThirdDlc); |
| dlc_manager_->Initialize(); |
| |
| EXPECT_CALL(*mock_image_loader_proxy_ptr_, LoadDlcImage(_, _, _, _, _, _)) |
| .WillOnce(DoAll(SetArgPointee<3>(mount_path_.value()), Return(true))); |
| EXPECT_CALL(*mock_update_engine_proxy_ptr_, |
| SetDlcActiveValue(true, kThirdDlc, _, _)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*mock_metrics_, |
| SendInstallResult(InstallResult::kSuccessAlreadyInstalled)); |
| EXPECT_THAT(dlc_manager_->GetInstalled(), ElementsAre()); |
| EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(2); |
| |
| bool external_install_needed = false; |
| EXPECT_TRUE( |
| dlc_manager_->Install(kThirdDlc, &external_install_needed, &err_)); |
| EXPECT_THAT(dlc_manager_->GetInstalled(), ElementsAre(kThirdDlc)); |
| EXPECT_FALSE( |
| dlc_manager_->GetDlc(kThirdDlc, &err_)->GetRoot().value().empty()); |
| CheckDlcState(kThirdDlc, DlcState::INSTALLED); |
| } |
| |
| TEST_F(DlcManagerTest, PreloadAllowedWithBadPreinstalledDlcTest) { |
| // The third DLC has pre-loaded flag on. |
| SetUpDlcWithSlots(kThirdDlc); |
| SetUpDlcPreloadedImage(kThirdDlc); |
| dlc_manager_->Initialize(); |
| |
| EXPECT_CALL(*mock_image_loader_proxy_ptr_, LoadDlcImage(_, _, _, _, _, _)) |
| .WillOnce(DoAll(SetArgPointee<3>(mount_path_.value()), Return(true))); |
| EXPECT_CALL(*mock_update_engine_proxy_ptr_, |
| SetDlcActiveValue(true, kThirdDlc, _, _)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(2); |
| EXPECT_CALL(*mock_metrics_, |
| SendInstallResult(InstallResult::kSuccessAlreadyInstalled)); |
| |
| EXPECT_THAT(dlc_manager_->GetInstalled(), ElementsAre()); |
| bool external_install_needed = false; |
| EXPECT_TRUE( |
| dlc_manager_->Install(kThirdDlc, &external_install_needed, &err_)); |
| EXPECT_THAT(dlc_manager_->GetInstalled(), ElementsAre(kThirdDlc)); |
| EXPECT_FALSE( |
| dlc_manager_->GetDlc(kThirdDlc, &err_)->GetRoot().value().empty()); |
| CheckDlcState(kThirdDlc, DlcState::INSTALLED); |
| } |
| |
| TEST_F(DlcManagerTest, PreloadNotAllowedDlcTest) { |
| SetUpDlcPreloadedImage(kSecondDlc); |
| |
| EXPECT_THAT(dlc_manager_->GetInstalled(), ElementsAre()); |
| |
| dlc_manager_->Initialize(); |
| |
| EXPECT_THAT(dlc_manager_->GetInstalled(), ElementsAre()); |
| CheckDlcState(kSecondDlc, DlcState::NOT_INSTALLED); |
| } |
| |
| TEST_F(DlcManagerTest, UnsupportedContentDlcRemovalCheck) { |
| auto id = "unsupported-dlc"; |
| for (const auto& slot : {BootSlot::Slot::A, BootSlot::Slot::B}) { |
| auto image_path = GetDlcImagePath(content_path_, id, kPackage, slot); |
| base::CreateDirectory(image_path.DirName()); |
| CreateFile(image_path, 1); |
| } |
| EXPECT_TRUE(base::CreateDirectory(JoinPaths(prefs_path_, "dlc", id))); |
| |
| EXPECT_TRUE(base::PathExists(JoinPaths(prefs_path_, "dlc", id))); |
| EXPECT_TRUE(base::PathExists(JoinPaths(content_path_, id))); |
| |
| dlc_manager_->Initialize(); |
| |
| EXPECT_FALSE(base::PathExists(JoinPaths(prefs_path_, "dlc", id))); |
| EXPECT_FALSE(base::PathExists(JoinPaths(content_path_, id))); |
| } |
| |
| TEST_F(DlcManagerTest, UnsupportedPreloadedDlcRemovalCheck) { |
| auto id = "unsupported-dlc"; |
| auto image_path = |
| JoinPaths(preloaded_content_path_, id, kPackage, kDlcImageFileName); |
| base::CreateDirectory(image_path.DirName()); |
| CreateFile(image_path, 1); |
| |
| EXPECT_TRUE(base::PathExists(JoinPaths(preloaded_content_path_, id))); |
| dlc_manager_->Initialize(); |
| EXPECT_FALSE(base::PathExists(JoinPaths(preloaded_content_path_, id))); |
| } |
| |
| TEST_F(DlcManagerTest, CleanupDanglingDlcs) { |
| // The the clock to the system time so it doesn't start with 0; |
| clock_.SetNow(base::Time::Now()); |
| |
| dlc_manager_->Initialize(); |
| Install(kFirstDlc); |
| |
| // Make sure the ref count is not deleted. |
| auto ref_count_path = JoinPaths(SystemState::Get()->dlc_prefs_dir(), |
| kFirstDlc, kRefCountFileName); |
| EXPECT_TRUE(base::PathExists(ref_count_path)); |
| Uninstall(kFirstDlc); |
| EXPECT_TRUE(base::PathExists(ref_count_path)); |
| |
| // Advance the time so the |kFirstDlc| becomes dangling. |
| clock_.Advance(base::TimeDelta::FromDays(6)); |
| |
| // Reinitialize the |dlc_manager_| so it initializes the |kFirstDlc| again. |
| dlc_manager_->Initialize(); |
| // Install another DLC to make sure cleanup dangling doesn't remove them. |
| Install(kSecondDlc); |
| |
| // These should happen when the |kFirstDlc| is purged. |
| EXPECT_CALL(*mock_image_loader_proxy_ptr_, UnloadDlcImage(_, _, _, _, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(true), Return(true))); |
| EXPECT_CALL(*mock_update_engine_proxy_ptr_, |
| SetDlcActiveValue(false, kFirstDlc, _, _)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(mock_state_change_reporter_, DlcStateChanged(_)).Times(1); |
| |
| // Advance by 31 minutes so it kicks in the cleanup method. |
| clock_.Advance(base::TimeDelta::FromDays(31)); |
| loop_.RunOnce(false); |
| |
| // |kFirstDLC| should be gone by now. |
| EXPECT_FALSE(base::PathExists(ref_count_path)); |
| // |kSecondDlc| should still be around. |
| CheckDlcState(kSecondDlc, DlcState::INSTALLED); |
| } |
| |
| } // namespace dlcservice |