| // Copyright 2014 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/ui/webui/help/version_updater_chromeos.h" |
| |
| #include <memory> |
| #include <optional> |
| |
| #include "base/compiler_specific.h" |
| #include "base/functional/bind.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/run_loop.h" |
| #include "base/test/mock_callback.h" |
| #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h" |
| #include "chrome/browser/ash/settings/scoped_cros_settings_test_helper.h" |
| #include "chromeos/ash/components/dbus/shill/shill_service_client.h" |
| #include "chromeos/ash/components/dbus/update_engine/fake_update_engine_client.h" |
| #include "chromeos/ash/components/network/network_handler_test_helper.h" |
| #include "components/user_manager/scoped_user_manager.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/cros_system_api/dbus/service_constants.h" |
| |
| using ::testing::_; |
| using ::testing::AtLeast; |
| using ::testing::Return; |
| using ::testing::StrictMock; |
| |
| namespace chromeos { |
| |
| namespace { |
| |
| void CheckNotification(VersionUpdater::Status /* status */, |
| int /* progress */, |
| bool /* rollback */, |
| bool /* powerwash */, |
| const std::string& /* version */, |
| int64_t /* size */, |
| const std::u16string& /* message */) {} |
| |
| } // namespace |
| |
| class VersionUpdaterCrosTest : public ::testing::Test { |
| public: |
| VersionUpdaterCrosTest(const VersionUpdaterCrosTest&) = delete; |
| VersionUpdaterCrosTest& operator=(const VersionUpdaterCrosTest&) = delete; |
| |
| protected: |
| VersionUpdaterCrosTest() |
| : version_updater_(std::make_unique<VersionUpdaterCros>(nullptr)), |
| fake_update_engine_client_(nullptr), |
| user_manager_enabler_(std::make_unique<FakeChromeUserManager>()) {} |
| |
| ~VersionUpdaterCrosTest() override = default; |
| |
| void SetUp() override { |
| fake_update_engine_client_ = |
| ash::UpdateEngineClient::InitializeFakeForTest(); |
| |
| network_handler_test_helper_ = |
| std::make_unique<ash::NetworkHandlerTestHelper>(); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| void SetEthernetService() { |
| ash::ShillServiceClient::TestInterface* service_test = |
| network_handler_test_helper_->service_test(); |
| service_test->ClearServices(); |
| service_test->AddService("/service/eth", "eth" /* guid */, "eth", |
| shill::kTypeEthernet, shill::kStateOnline, |
| true /* visible */); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| void SetCellularService() { |
| ash::ShillServiceClient::TestInterface* service_test = |
| network_handler_test_helper_->service_test(); |
| service_test->ClearServices(); |
| service_test->AddService("/service/cell", "cell" /* guid */, "cell", |
| shill::kTypeCellular, shill::kStateOnline, |
| true /* visible */); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| void TearDown() override { |
| network_handler_test_helper_.reset(); |
| version_updater_.reset(); |
| ash::UpdateEngineClient::Shutdown(); |
| } |
| |
| content::BrowserTaskEnvironment task_environment_; |
| std::unique_ptr<ash::NetworkHandlerTestHelper> network_handler_test_helper_; |
| std::unique_ptr<VersionUpdaterCros> version_updater_; |
| raw_ptr<ash::FakeUpdateEngineClient, DanglingUntriaged> |
| fake_update_engine_client_; // Not owned. |
| |
| user_manager::ScopedUserManager user_manager_enabler_; |
| ash::ScopedCrosSettingsTestHelper cros_settings_test_helper_; |
| }; |
| |
| // The test checks following behaviour: |
| // 1. The device is currently on the dev channel and an user decides to switch |
| // to the beta channel. |
| // 2. In the middle of channel switch the user decides to switch to the stable |
| // channel. |
| // 3. Update engine reports an error because downloading channel (beta) is not |
| // equal |
| // to the target channel (stable). |
| // 4. When update engine becomes idle downloading of the stable channel is |
| // initiated. |
| TEST_F(VersionUpdaterCrosTest, TwoOverlappingSetChannelRequests) { |
| SetEthernetService(); |
| version_updater_->SetChannel("beta-channel", true); |
| |
| { |
| update_engine::StatusResult status; |
| status.set_current_operation(update_engine::Operation::IDLE); |
| fake_update_engine_client_->set_default_status(status); |
| fake_update_engine_client_->NotifyObserversThatStatusChanged(status); |
| } |
| |
| EXPECT_EQ(0, fake_update_engine_client_->request_update_check_call_count()); |
| |
| // IDLE -> DOWNLOADING transition after update check. |
| version_updater_->CheckForUpdate(base::BindRepeating(&CheckNotification), |
| VersionUpdater::PromoteCallback()); |
| EXPECT_EQ(1, fake_update_engine_client_->request_update_check_call_count()); |
| |
| { |
| update_engine::StatusResult status; |
| status.set_current_operation(update_engine::Operation::DOWNLOADING); |
| status.set_progress(0.1); |
| fake_update_engine_client_->set_default_status(status); |
| fake_update_engine_client_->NotifyObserversThatStatusChanged(status); |
| } |
| |
| version_updater_->SetChannel("stable-channel", true); |
| |
| // DOWNLOADING -> REPORTING_ERROR_EVENT transition since target channel is not |
| // equal to downloading channel now. |
| { |
| update_engine::StatusResult status; |
| status.set_current_operation( |
| update_engine::Operation::REPORTING_ERROR_EVENT); |
| fake_update_engine_client_->set_default_status(status); |
| fake_update_engine_client_->NotifyObserversThatStatusChanged(status); |
| } |
| |
| version_updater_->CheckForUpdate(base::BindRepeating(&CheckNotification), |
| VersionUpdater::PromoteCallback()); |
| EXPECT_EQ(1, fake_update_engine_client_->request_update_check_call_count()); |
| |
| // REPORTING_ERROR_EVENT -> IDLE transition, update check should be |
| // automatically scheduled. |
| { |
| update_engine::StatusResult status; |
| status.set_current_operation(update_engine::Operation::IDLE); |
| fake_update_engine_client_->set_default_status(status); |
| fake_update_engine_client_->NotifyObserversThatStatusChanged(status); |
| } |
| |
| EXPECT_EQ(2, fake_update_engine_client_->request_update_check_call_count()); |
| } |
| |
| // Test that when interactively checking for update, cellular connection is |
| // allowed in Chrome by default, so that the request will be sent to Update |
| // Engine. |
| TEST_F(VersionUpdaterCrosTest, InteractiveCellularUpdateAllowed) { |
| SetCellularService(); |
| EXPECT_EQ(0, fake_update_engine_client_->request_update_check_call_count()); |
| version_updater_->CheckForUpdate(base::BindRepeating(&CheckNotification), |
| VersionUpdater::PromoteCallback()); |
| EXPECT_EQ(1, fake_update_engine_client_->request_update_check_call_count()); |
| } |
| |
| // Test that after update over cellular one time permission is set successfully, |
| // an update check will be triggered. |
| TEST_F(VersionUpdaterCrosTest, CellularUpdateOneTimePermission) { |
| SetCellularService(); |
| EXPECT_EQ(0, fake_update_engine_client_->request_update_check_call_count()); |
| const std::string& update_version = "9999.0.0"; |
| const int64_t update_size = 99999; |
| version_updater_->SetUpdateOverCellularOneTimePermission( |
| base::BindRepeating(&CheckNotification), update_version, update_size); |
| EXPECT_EQ(1, fake_update_engine_client_->request_update_check_call_count()); |
| } |
| |
| TEST_F(VersionUpdaterCrosTest, |
| GetUpdateStatus_CallbackDuringInstallationsWhenDownloading) { |
| SetEthernetService(); |
| update_engine::StatusResult status; |
| status.set_is_install(true); |
| status.set_current_operation(update_engine::Operation::DOWNLOADING); |
| fake_update_engine_client_->set_default_status(status); |
| |
| StrictMock<base::MockCallback<VersionUpdater::StatusCallback>> mock_callback; |
| EXPECT_CALL(mock_callback, Run(VersionUpdater::UPDATED, 0, false, false, |
| std::string(), 0, std::u16string())) |
| .Times(1); |
| version_updater_->GetUpdateStatus(mock_callback.Get()); |
| } |
| |
| TEST_F(VersionUpdaterCrosTest, |
| GetUpdateStatus_CallbackDuringInstallationsWhenIdle) { |
| SetEthernetService(); |
| update_engine::StatusResult status; |
| status.set_is_install(true); |
| status.set_current_operation(update_engine::Operation::IDLE); |
| fake_update_engine_client_->set_default_status(status); |
| |
| StrictMock<base::MockCallback<VersionUpdater::StatusCallback>> mock_callback; |
| EXPECT_CALL(mock_callback, Run(VersionUpdater::UPDATED, 0, false, false, |
| std::string(), 0, std::u16string())) |
| .Times(1); |
| version_updater_->GetUpdateStatus(mock_callback.Get()); |
| } |
| |
| TEST_F(VersionUpdaterCrosTest, GetUpdateStatus_CallbackDuringUpdates) { |
| SetEthernetService(); |
| update_engine::StatusResult status; |
| fake_update_engine_client_->set_default_status(status); |
| |
| // Expect the callback to be called as it's an update status change. |
| StrictMock<base::MockCallback<VersionUpdater::StatusCallback>> mock_callback; |
| EXPECT_CALL(mock_callback, Run(_, _, _, _, _, _, _)).Times(1); |
| version_updater_->GetUpdateStatus(mock_callback.Get()); |
| } |
| |
| TEST_F(VersionUpdaterCrosTest, |
| GetUpdateStatus_SetToUpdatedForNonInteractiveDeferredUpdate) { |
| SetEthernetService(); |
| update_engine::StatusResult status; |
| // The update is non-interactive and will be deferred. |
| status.set_is_interactive(false); |
| status.set_will_defer_update(true); |
| fake_update_engine_client_->set_default_status(status); |
| |
| // Expect to set status to `UPDATED`. |
| StrictMock<base::MockCallback<VersionUpdater::StatusCallback>> mock_callback; |
| EXPECT_CALL(mock_callback, Run(VersionUpdater::UPDATED, 0, _, _, _, _, _)) |
| .Times(1); |
| version_updater_->GetUpdateStatus(mock_callback.Get()); |
| } |
| |
| TEST_F(VersionUpdaterCrosTest, GetUpdateStatus_UpdatedButDeferred) { |
| SetEthernetService(); |
| update_engine::StatusResult status; |
| // The update is deferred. |
| status.set_is_interactive(false); |
| status.set_will_defer_update(true); |
| status.set_current_operation(update_engine::Operation::UPDATED_BUT_DEFERRED); |
| fake_update_engine_client_->set_default_status(status); |
| |
| // Expect the status to be `DEFERRED`. |
| StrictMock<base::MockCallback<VersionUpdater::StatusCallback>> mock_callback; |
| EXPECT_CALL(mock_callback, Run(VersionUpdater::DEFERRED, _, _, _, _, _, _)) |
| .Times(1); |
| version_updater_->GetUpdateStatus(mock_callback.Get()); |
| } |
| |
| TEST_F(VersionUpdaterCrosTest, GetUpdateStatus_UpdatedNeedReboot) { |
| SetEthernetService(); |
| update_engine::StatusResult status; |
| status.set_is_interactive(false); |
| status.set_current_operation(update_engine::Operation::UPDATED_NEED_REBOOT); |
| fake_update_engine_client_->set_default_status(status); |
| |
| // Expect the status to be `NEARLY_UPDATED`. |
| StrictMock<base::MockCallback<VersionUpdater::StatusCallback>> mock_callback; |
| EXPECT_CALL(mock_callback, |
| Run(VersionUpdater::NEARLY_UPDATED, _, _, _, _, _, _)) |
| .Times(1); |
| version_updater_->GetUpdateStatus(mock_callback.Get()); |
| } |
| |
| TEST_F(VersionUpdaterCrosTest, |
| GetUpdateStatus_UpdateToRollbackVersionDisallowed) { |
| SetEthernetService(); |
| update_engine::StatusResult status; |
| status.set_is_interactive(true); |
| status.set_current_operation(update_engine::Operation::DISABLED); |
| int32_t error_code = static_cast<int32_t>( |
| update_engine::ErrorCode::kOmahaUpdateIgnoredPerPolicy); |
| status.set_last_attempt_error(error_code); |
| fake_update_engine_client_->set_default_status(status); |
| |
| // Expect the status to be `UPDATE_TO_ROLLBACK_VERSION_DISALLOWED`. |
| StrictMock<base::MockCallback<VersionUpdater::StatusCallback>> mock_callback; |
| EXPECT_CALL(mock_callback, |
| Run(VersionUpdater::UPDATE_TO_ROLLBACK_VERSION_DISALLOWED, _, _, |
| _, _, _, _)) |
| .Times(1); |
| version_updater_->GetUpdateStatus(mock_callback.Get()); |
| } |
| |
| TEST_F(VersionUpdaterCrosTest, ToggleFeature) { |
| EXPECT_EQ(0, fake_update_engine_client_->toggle_feature_count()); |
| version_updater_->ToggleFeature("feature-foo", true); |
| EXPECT_EQ(1, fake_update_engine_client_->toggle_feature_count()); |
| version_updater_->ToggleFeature("feature-foo", false); |
| EXPECT_EQ(2, fake_update_engine_client_->toggle_feature_count()); |
| } |
| |
| TEST_F(VersionUpdaterCrosTest, IsFeatureEnabled) { |
| EXPECT_EQ(0, fake_update_engine_client_->is_feature_enabled_count()); |
| |
| StrictMock<base::MockCallback<VersionUpdater::IsFeatureEnabledCallback>> |
| mock_callback; |
| EXPECT_CALL(mock_callback, Run(_)).Times(1); |
| version_updater_->IsFeatureEnabled("feature-foo", mock_callback.Get()); |
| |
| EXPECT_EQ(1, fake_update_engine_client_->is_feature_enabled_count()); |
| } |
| |
| TEST_F(VersionUpdaterCrosTest, ApplyDeferredUpdateAdvanced) { |
| update_engine::StatusResult status; |
| status.set_current_operation(update_engine::Operation::UPDATED_BUT_DEFERRED); |
| fake_update_engine_client_->set_default_status(status); |
| fake_update_engine_client_->NotifyObserversThatStatusChanged(status); |
| |
| EXPECT_EQ(0, |
| fake_update_engine_client_->apply_deferred_update_advanced_count()); |
| version_updater_->ApplyDeferredUpdateAdvanced(); |
| EXPECT_EQ(1, |
| fake_update_engine_client_->apply_deferred_update_advanced_count()); |
| } |
| |
| } // namespace chromeos |