| // 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/crostini/termina_installer.h" |
| |
| #include "base/memory/raw_ptr.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/task_environment.h" |
| #include "base/test/test_future.h" |
| #include "chrome/browser/ash/crostini/crostini_util.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/component_updater/fake_cros_component_manager.h" |
| #include "chrome/test/base/browser_process_platform_part_test_api_chromeos.h" |
| #include "chromeos/ash/components/dbus/dlcservice/dlcservice_client.h" |
| #include "chromeos/ash/components/dbus/dlcservice/fake_dlcservice_client.h" |
| #include "services/network/test/test_network_connection_tracker.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/cros_system_api/dbus/dlcservice/dbus-constants.h" |
| #include "third_party/cros_system_api/dbus/service_constants.h" |
| |
| using base::test::TestFuture; |
| |
| namespace crostini { |
| |
| class TerminaInstallTest : public testing::Test { |
| public: |
| TerminaInstallTest() : browser_part_(g_browser_process->platform_part()) {} |
| |
| void CommonSetUp() { |
| component_manager_ = |
| base::MakeRefCounted<component_updater::FakeCrOSComponentManager>(); |
| browser_part_.InitializeCrosComponentManager(component_manager_); |
| fake_dlc_client_.set_install_root_path(dlc_root_path_); |
| } |
| |
| void SetUp() override { |
| this->CommonSetUp(); |
| feature_list_.InitWithFeatures( |
| /*enabled_features=*/{}, |
| /*disabled_features=*/{}); |
| } |
| |
| void TearDown() override { |
| browser_part_.ShutdownCrosComponentManager(); |
| component_manager_.reset(); |
| } |
| |
| void InjectDlc() { |
| dlcservice::DlcsWithContent dlcs; |
| auto* dlc_info = dlcs.add_dlc_infos(); |
| dlc_info->set_id(kCrostiniDlcName); |
| fake_dlc_client_.set_dlcs_with_content(dlcs); |
| } |
| |
| const base::FilePath component_install_path_ = |
| base::FilePath("/install/path"); |
| const base::FilePath component_mount_path_ = base::FilePath("/mount/path"); |
| using ComponentError = component_updater::CrOSComponentManager::Error; |
| using ComponentInfo = |
| component_updater::FakeCrOSComponentManager::ComponentInfo; |
| |
| protected: |
| base::test::ScopedFeatureList feature_list_; |
| |
| void PrepareComponentForLoad() { |
| component_manager_->set_supported_components( |
| {imageloader::kTerminaComponentName}); |
| component_manager_->ResetComponentState( |
| imageloader::kTerminaComponentName, |
| ComponentInfo(ComponentError::NONE, component_install_path_, |
| component_mount_path_)); |
| } |
| |
| const std::string dlc_root_path_ = "/dlc/root/path"; |
| |
| void CheckDlcInstallCalledTimes(int times) { |
| TestFuture<const std::string&, const dlcservice::DlcsWithContent&> |
| result_future; |
| fake_dlc_client_.GetExistingDlcs(result_future.GetCallback()); |
| |
| const dlcservice::DlcsWithContent& dlcs_with_content = |
| result_future.Get<1>(); |
| ASSERT_EQ(dlcs_with_content.dlc_infos_size(), times); |
| for (auto dlc : dlcs_with_content.dlc_infos()) { |
| EXPECT_EQ(dlc.id(), kCrostiniDlcName); |
| } |
| } |
| |
| void ExpectDlcInstalled() { |
| EXPECT_EQ(termina_installer_.GetInstallLocation(), |
| base::FilePath(dlc_root_path_)); |
| EXPECT_EQ(termina_installer_.GetDlcId(), "termina-dlc"); |
| } |
| |
| protected: |
| scoped_refptr<component_updater::FakeCrOSComponentManager> component_manager_; |
| BrowserProcessPlatformPartTestApi browser_part_; |
| ash::FakeDlcserviceClient fake_dlc_client_; |
| TerminaInstaller termina_installer_; |
| base::test::TaskEnvironment task_env_{ |
| base::test::TaskEnvironment::TimeSource::MOCK_TIME}; |
| }; |
| |
| TEST_F(TerminaInstallTest, UninstallWithNothingInstalled) { |
| TestFuture<bool> result_future; |
| termina_installer_.Uninstall(result_future.GetCallback()); |
| EXPECT_TRUE(result_future.Get()); |
| } |
| |
| TEST_F(TerminaInstallTest, UninstallWithNothingInstalledListError) { |
| fake_dlc_client_.set_get_existing_dlcs_error("An error"); |
| |
| TestFuture<bool> result_future; |
| termina_installer_.Uninstall(result_future.GetCallback()); |
| EXPECT_FALSE(result_future.Get()); |
| } |
| |
| TEST_F(TerminaInstallTest, UninstallWithNothingInstalledUninstallError) { |
| // These should be ignored because nothing needs to be uninstalled |
| component_manager_->set_unload_component_result(false); |
| fake_dlc_client_.set_uninstall_error("An error"); |
| |
| TestFuture<bool> result_future; |
| termina_installer_.Uninstall(result_future.GetCallback()); |
| EXPECT_TRUE(result_future.Get()); |
| } |
| |
| TEST_F(TerminaInstallTest, UninstallWithComponentInstalled) { |
| component_manager_->SetRegisteredComponents( |
| {imageloader::kTerminaComponentName}); |
| |
| TestFuture<bool> result_future; |
| termina_installer_.Uninstall(result_future.GetCallback()); |
| EXPECT_TRUE(result_future.Get()); |
| |
| EXPECT_FALSE(component_manager_->IsRegisteredMayBlock( |
| imageloader::kTerminaComponentName)); |
| } |
| |
| TEST_F(TerminaInstallTest, UninstallWithComponentInstalledError) { |
| component_manager_->SetRegisteredComponents( |
| {imageloader::kTerminaComponentName}); |
| component_manager_->set_unload_component_result(false); |
| |
| TestFuture<bool> result_future; |
| termina_installer_.Uninstall(result_future.GetCallback()); |
| EXPECT_FALSE(result_future.Get()); |
| } |
| |
| TEST_F(TerminaInstallTest, UninstallWithDlcInstalled) { |
| InjectDlc(); |
| |
| TestFuture<bool> result_future; |
| termina_installer_.Uninstall(result_future.GetCallback()); |
| EXPECT_TRUE(result_future.Get()); |
| |
| CheckDlcInstallCalledTimes(0); |
| } |
| |
| TEST_F(TerminaInstallTest, UninstallWithDlcInstalledUninstallError) { |
| InjectDlc(); |
| fake_dlc_client_.set_uninstall_error("An error"); |
| |
| TestFuture<bool> result_future; |
| termina_installer_.Uninstall(result_future.GetCallback()); |
| EXPECT_FALSE(result_future.Get()); |
| } |
| |
| TEST_F(TerminaInstallTest, UninstallWithBothInstalled) { |
| component_manager_->SetRegisteredComponents( |
| {imageloader::kTerminaComponentName}); |
| InjectDlc(); |
| |
| TestFuture<bool> result_future; |
| termina_installer_.Uninstall(result_future.GetCallback()); |
| EXPECT_TRUE(result_future.Get()); |
| |
| EXPECT_FALSE(component_manager_->IsRegisteredMayBlock( |
| imageloader::kTerminaComponentName)); |
| CheckDlcInstallCalledTimes(0); |
| } |
| |
| TEST_F(TerminaInstallTest, InstallDlc) { |
| TestFuture<TerminaInstaller::InstallResult> result_future; |
| termina_installer_.Install(result_future.GetCallback()); |
| EXPECT_EQ(TerminaInstaller::InstallResult::Success, result_future.Get()); |
| |
| CheckDlcInstallCalledTimes(1); |
| ExpectDlcInstalled(); |
| } |
| |
| TEST_F(TerminaInstallTest, InstallDlcCancell) { |
| fake_dlc_client_.set_install_error(dlcservice::kErrorBusy); |
| |
| TestFuture<TerminaInstaller::InstallResult> result_future; |
| termina_installer_.Install(result_future.GetCallback()); |
| |
| // The installer should *not* complete until dlcservice stops being busy. |
| task_env_.RunUntilIdle(); |
| termina_installer_.CancelInstall(); |
| task_env_.RunUntilIdle(); |
| EXPECT_FALSE(result_future.IsReady()); |
| |
| task_env_.FastForwardBy(base::Seconds(10)); |
| EXPECT_TRUE(result_future.IsReady()); |
| EXPECT_EQ(TerminaInstaller::InstallResult::Cancelled, result_future.Get()); |
| } |
| |
| TEST_F(TerminaInstallTest, InstallDlcError) { |
| fake_dlc_client_.set_install_error("An error"); |
| |
| TestFuture<TerminaInstaller::InstallResult> result_future; |
| termina_installer_.Install(result_future.GetCallback()); |
| EXPECT_EQ(TerminaInstaller::InstallResult::Failure, result_future.Get()); |
| } |
| |
| TEST_F(TerminaInstallTest, InstallDlcNeedsReboot) { |
| fake_dlc_client_.set_install_error(dlcservice::kErrorNeedReboot); |
| |
| TestFuture<TerminaInstaller::InstallResult> result_future; |
| termina_installer_.Install(result_future.GetCallback()); |
| EXPECT_EQ(TerminaInstaller::InstallResult::NeedUpdate, result_future.Get()); |
| } |
| |
| TEST_F(TerminaInstallTest, InstallDlcNoImageFound) { |
| fake_dlc_client_.set_install_error(dlcservice::kErrorNoImageFound); |
| |
| TestFuture<TerminaInstaller::InstallResult> result_future; |
| termina_installer_.Install(result_future.GetCallback()); |
| EXPECT_EQ(TerminaInstaller::InstallResult::NeedUpdate, result_future.Get()); |
| } |
| |
| TEST_F(TerminaInstallTest, InstallDlcBusyTriggersRetry) { |
| fake_dlc_client_.set_install_error(dlcservice::kErrorBusy); |
| |
| TestFuture<TerminaInstaller::InstallResult> result_future; |
| termina_installer_.Install(result_future.GetCallback()); |
| task_env_.FastForwardBy(base::Seconds(0)); |
| |
| fake_dlc_client_.set_install_error(dlcservice::kErrorNone); |
| EXPECT_EQ(TerminaInstaller::InstallResult::Success, result_future.Get()); |
| |
| CheckDlcInstallCalledTimes(2); |
| ExpectDlcInstalled(); |
| } |
| |
| TEST_F(TerminaInstallTest, InstallDlcBusyRetryIsCancelable) { |
| fake_dlc_client_.set_install_error(dlcservice::kErrorBusy); |
| |
| TestFuture<TerminaInstaller::InstallResult> result_future; |
| termina_installer_.Install(result_future.GetCallback()); |
| task_env_.FastForwardBy(base::Seconds(0)); |
| |
| CheckDlcInstallCalledTimes(1); |
| |
| termina_installer_.CancelInstall(); |
| EXPECT_EQ(TerminaInstaller::InstallResult::Cancelled, result_future.Get()); |
| |
| task_env_.FastForwardBy(base::Days(1)); |
| |
| CheckDlcInstallCalledTimes(1); |
| } |
| |
| TEST_F(TerminaInstallTest, InstallDlcOffline) { |
| fake_dlc_client_.set_install_error("An error"); |
| |
| auto* network_connection_tracker = |
| network::TestNetworkConnectionTracker::GetInstance(); |
| network_connection_tracker->SetConnectionType( |
| network::mojom::ConnectionType::CONNECTION_NONE); |
| |
| TestFuture<TerminaInstaller::InstallResult> result_future; |
| termina_installer_.Install(result_future.GetCallback()); |
| EXPECT_EQ(TerminaInstaller::InstallResult::Offline, result_future.Get()); |
| } |
| |
| TEST_F(TerminaInstallTest, InstallDlcWithComponentInstalled) { |
| component_manager_->SetRegisteredComponents( |
| {imageloader::kTerminaComponentName}); |
| |
| TestFuture<TerminaInstaller::InstallResult> result_future; |
| termina_installer_.Install(result_future.GetCallback()); |
| EXPECT_EQ(TerminaInstaller::InstallResult::Success, result_future.Get()); |
| |
| CheckDlcInstallCalledTimes(1); |
| ExpectDlcInstalled(); |
| |
| task_env_.RunUntilIdle(); |
| EXPECT_FALSE(component_manager_->IsRegisteredMayBlock( |
| imageloader::kTerminaComponentName)); |
| } |
| |
| TEST_F(TerminaInstallTest, InstallDlcWithComponentInstalledUninstallError) { |
| component_manager_->SetRegisteredComponents( |
| {imageloader::kTerminaComponentName}); |
| component_manager_->set_unload_component_result(false); |
| |
| TestFuture<TerminaInstaller::InstallResult> result_future; |
| termina_installer_.Install(result_future.GetCallback()); |
| EXPECT_EQ(TerminaInstaller::InstallResult::Success, result_future.Get()); |
| |
| CheckDlcInstallCalledTimes(1); |
| ExpectDlcInstalled(); |
| } |
| |
| TEST_F(TerminaInstallTest, InstallDlcFallback) { |
| TestFuture<TerminaInstaller::InstallResult> result_future; |
| termina_installer_.Install(result_future.GetCallback()); |
| EXPECT_EQ(TerminaInstaller::InstallResult::Success, result_future.Get()); |
| |
| CheckDlcInstallCalledTimes(1); |
| ExpectDlcInstalled(); |
| } |
| |
| TEST_F(TerminaInstallTest, InstallDlcFallbackError) { |
| fake_dlc_client_.set_install_error("An error"); |
| PrepareComponentForLoad(); |
| |
| TestFuture<TerminaInstaller::InstallResult> result_future; |
| termina_installer_.Install(result_future.GetCallback()); |
| EXPECT_EQ(TerminaInstaller::InstallResult::Failure, result_future.Get()); |
| |
| CheckDlcInstallCalledTimes(1); |
| EXPECT_FALSE(component_manager_->IsRegisteredMayBlock( |
| imageloader::kTerminaComponentName)); |
| } |
| |
| TEST_F(TerminaInstallTest, InstallDlcFallbackOffline) { |
| fake_dlc_client_.set_install_error("An error"); |
| PrepareComponentForLoad(); |
| |
| auto* network_connection_tracker = |
| network::TestNetworkConnectionTracker::GetInstance(); |
| network_connection_tracker->SetConnectionType( |
| network::mojom::ConnectionType::CONNECTION_NONE); |
| |
| TestFuture<TerminaInstaller::InstallResult> result_future; |
| termina_installer_.Install(result_future.GetCallback()); |
| EXPECT_EQ(TerminaInstaller::InstallResult::Offline, result_future.Get()); |
| |
| EXPECT_FALSE(component_manager_->IsRegisteredMayBlock( |
| imageloader::kTerminaComponentName)); |
| } |
| |
| TEST_F(TerminaInstallTest, InstallDlcFallbackOfflineComponentAlreadyInstalled) { |
| fake_dlc_client_.set_install_error("An error"); |
| PrepareComponentForLoad(); |
| component_manager_->RegisterCompatiblePath( |
| imageloader::kTerminaComponentName, |
| component_updater::CompatibleComponentInfo(component_install_path_, |
| /* version= */ std::nullopt)); |
| |
| auto* network_connection_tracker = |
| network::TestNetworkConnectionTracker::GetInstance(); |
| network_connection_tracker->SetConnectionType( |
| network::mojom::ConnectionType::CONNECTION_NONE); |
| |
| TestFuture<TerminaInstaller::InstallResult> result_future; |
| termina_installer_.Install(result_future.GetCallback()); |
| EXPECT_EQ(TerminaInstaller::InstallResult::Offline, result_future.Get()); |
| |
| EXPECT_FALSE(component_manager_->IsRegisteredMayBlock( |
| imageloader::kTerminaComponentName)); |
| } |
| |
| } // namespace crostini |