// Copyright 2014 The Chromium 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 "chrome/browser/google/google_update_win.h" #include #include #include #include #include "base/base_paths.h" #include "base/memory/ref_counted.h" #include "base/path_service.h" #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/test/scoped_path_override.h" #include "base/test/test_reg_util_win.h" #include "base/test/test_simple_task_runner.h" #include "base/thread_task_runner_handle.h" #include "base/version.h" #include "base/win/registry.h" #include "chrome/installer/util/browser_distribution.h" #include "chrome/installer/util/google_update_settings.h" #include "chrome/installer/util/helper.h" #include "google_update/google_update_idl.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/win/atl_module.h" #include "version.h" using ::testing::DoAll; using ::testing::Invoke; using ::testing::IsEmpty; using ::testing::Return; using ::testing::SetArgPointee; using ::testing::StrEq; using ::testing::Unused; using ::testing::Values; using ::testing::_; namespace { class UpdateCheckCallbackReceiver { public: UpdateCheckCallbackReceiver() {} virtual ~UpdateCheckCallbackReceiver() {} virtual void OnUpdateCheckCallback(GoogleUpdateUpgradeResult result, GoogleUpdateErrorCode error_code, const base::string16& error_message, const base::string16& version) = 0; UpdateCheckCallback GetCallback() { return base::Bind(&UpdateCheckCallbackReceiver::UpdateCheckCallback, base::Unretained(this)); } private: void UpdateCheckCallback(GoogleUpdateUpgradeResult result, GoogleUpdateErrorCode error_code, const base::string16& error_message, const base::string16& version) { OnUpdateCheckCallback(result, error_code, error_message, version); } DISALLOW_COPY_AND_ASSIGN(UpdateCheckCallbackReceiver); }; class MockUpdateCheckCallbackReceiver : public UpdateCheckCallbackReceiver { public: MockUpdateCheckCallbackReceiver() {} MOCK_METHOD4(OnUpdateCheckCallback, void(GoogleUpdateUpgradeResult, GoogleUpdateErrorCode, const base::string16&, const base::string16&)); private: DISALLOW_COPY_AND_ASSIGN(MockUpdateCheckCallbackReceiver); }; class GoogleUpdateFactory { public: virtual ~GoogleUpdateFactory() {} virtual HRESULT Create(base::win::ScopedComPtr* on_demand) = 0; }; class MockGoogleUpdateFactory : public GoogleUpdateFactory { public: MockGoogleUpdateFactory() {} MOCK_METHOD1(Create, HRESULT(base::win::ScopedComPtr*)); private: DISALLOW_COPY_AND_ASSIGN(MockGoogleUpdateFactory); }; // A mock IGoogleUpdate on-demand update class that can run an IJobObserver // through a set of states. class MockOnDemand : public CComObjectRootEx, public IGoogleUpdate { public: BEGIN_COM_MAP(MockOnDemand) COM_INTERFACE_ENTRY(IGoogleUpdate) END_COM_MAP() MockOnDemand() : task_runner_(base::ThreadTaskRunnerHandle::Get()) {} MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, CheckForUpdate, HRESULT(const wchar_t*, IJobObserver*)); MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, Update, HRESULT(const wchar_t*, IJobObserver*)); void OnCheckRunUpToDateSequence(const base::char16* app_guid) { EXPECT_CALL(*this, CheckForUpdate(StrEq(app_guid), _)) .WillOnce(DoAll(Invoke(this, &MockOnDemand::BeginUpToDateSequence), Return(S_OK))); } void OnCheckRunUpdateAvailableSequence(const base::char16* app_guid, const base::string16& new_version) { new_version_ = new_version; EXPECT_CALL(*this, CheckForUpdate(StrEq(app_guid), _)) .WillOnce( DoAll(Invoke(this, &MockOnDemand::BeginUpdateAvailableSequence), Return(S_OK))); } void OnUpdateRunInstallUpdateSequence(const base::char16* app_guid, const base::string16& new_version) { new_version_ = new_version; EXPECT_CALL(*this, Update(StrEq(app_guid), _)) .WillOnce(DoAll(Invoke(this, &MockOnDemand::BeginInstallUpdateSequence), Return(S_OK))); } void OnUpdateRunUpdateErrorSequence(const base::char16* app_guid, const base::char16* error_text) { error_text_ = error_text; EXPECT_CALL(*this, Update(StrEq(app_guid), _)) .WillOnce(DoAll(Invoke(this, &MockOnDemand::BeginUpdateErrorSequence), Return(S_OK))); } private: enum State { STATE_CHECKING, STATE_COMPLETE_SUCCESS, STATE_UPDATE_AVAILABLE, STATE_WAITING_TO_DOWNLOAD, STATE_DOWNLOADING_25, STATE_DOWNLOADING_100, STATE_WAITING_TO_INSTALL, STATE_INSTALLING, STATE_COMPLETE_ERROR, }; void BeginUpToDateSequence(Unused, IJobObserver* job_observer_ptr) { job_observer_ = job_observer_ptr; states_.push(STATE_CHECKING); states_.push(STATE_COMPLETE_SUCCESS); task_runner_->PostTask( FROM_HERE, base::Bind(&MockOnDemand::Advance, base::Unretained(this))); } void BeginUpdateAvailableSequence(Unused, IJobObserver* job_observer_ptr) { job_observer_ = job_observer_ptr; states_.push(STATE_CHECKING); states_.push(STATE_UPDATE_AVAILABLE); states_.push(STATE_COMPLETE_SUCCESS); task_runner_->PostTask( FROM_HERE, base::Bind(&MockOnDemand::Advance, base::Unretained(this))); } void BeginInstallUpdateSequence(Unused, IJobObserver* job_observer_ptr) { job_observer_ = job_observer_ptr; states_.push(STATE_CHECKING); states_.push(STATE_UPDATE_AVAILABLE); states_.push(STATE_WAITING_TO_DOWNLOAD); states_.push(STATE_DOWNLOADING_25); states_.push(STATE_DOWNLOADING_100); states_.push(STATE_WAITING_TO_INSTALL); states_.push(STATE_INSTALLING); states_.push(STATE_COMPLETE_SUCCESS); task_runner_->PostTask( FROM_HERE, base::Bind(&MockOnDemand::Advance, base::Unretained(this))); } void BeginUpdateErrorSequence(Unused, IJobObserver* job_observer_ptr) { job_observer_ = job_observer_ptr; states_.push(STATE_CHECKING); states_.push(STATE_UPDATE_AVAILABLE); states_.push(STATE_WAITING_TO_DOWNLOAD); states_.push(STATE_DOWNLOADING_25); states_.push(STATE_DOWNLOADING_100); states_.push(STATE_WAITING_TO_INSTALL); states_.push(STATE_INSTALLING); states_.push(STATE_COMPLETE_ERROR); task_runner_->PostTask( FROM_HERE, base::Bind(&MockOnDemand::Advance, base::Unretained(this))); } // Advance to the next state. If this state is non-terminal, a task is posted // to advance to the next state a bit later. void Advance() { ASSERT_FALSE(states_.empty()); switch (states_.front()) { case STATE_CHECKING: EXPECT_EQ(S_OK, job_observer_->OnCheckingForUpdate()); break; case STATE_COMPLETE_SUCCESS: EXPECT_EQ(S_OK, job_observer_->OnComplete(COMPLETION_CODE_SUCCESS, nullptr)); break; case STATE_UPDATE_AVAILABLE: EXPECT_EQ(S_OK, job_observer_->OnUpdateAvailable(new_version_.c_str())); break; case STATE_WAITING_TO_DOWNLOAD: EXPECT_EQ(S_OK, job_observer_->OnWaitingToDownload()); break; case STATE_DOWNLOADING_25: EXPECT_EQ(S_OK, job_observer_->OnDownloading(47, 25)); break; case STATE_DOWNLOADING_100: EXPECT_EQ(S_OK, job_observer_->OnDownloading(42, 100)); break; case STATE_WAITING_TO_INSTALL: EXPECT_EQ(S_OK, job_observer_->OnWaitingToInstall()); break; case STATE_INSTALLING: EXPECT_EQ(S_OK, job_observer_->OnInstalling()); break; case STATE_COMPLETE_ERROR: EXPECT_EQ(S_OK, job_observer_->OnComplete(COMPLETION_CODE_ERROR, error_text_.c_str())); break; } states_.pop(); if (states_.empty()) { // Drop the reference to the observer when the terminal state is reached. job_observer_ = nullptr; } else { task_runner_->PostTask(FROM_HERE, base::Bind(&MockOnDemand::Advance, base::Unretained(this))); } } // The task runner on which the state machine runs. scoped_refptr task_runner_; // The new version for a successful update check or update. base::string16 new_version_; // Error text to be supplied for an unsuccessful update check or update. base::string16 error_text_; // The set of states to be run on an IJobObserver. std::queue states_; // The IJobObserver given to either CheckForUpdate() or Update() that is being // driven through the desired state transitions. base::win::ScopedComPtr job_observer_; DISALLOW_COPY_AND_ASSIGN(MockOnDemand); }; } // namespace class GoogleUpdateWinTest : public ::testing::TestWithParam { public: static void SetUpTestCase() { ui::win::CreateATLModuleIfNeeded(); } protected: GoogleUpdateWinTest() : task_runner_(new base::TestSimpleTaskRunner()), task_runner_handle_(task_runner_), system_level_(GetParam()), mock_on_demand_(nullptr) {} void SetUp() override { ::testing::TestWithParam::SetUp(); // Override FILE_EXE so that it looks like the test is running from the // standard install location for this mode (system-level or user-level). base::FilePath file_exe; ASSERT_TRUE(PathService::Get(base::FILE_EXE, &file_exe)); base::FilePath install_dir(installer::GetChromeInstallPath( system_level_, BrowserDistribution::GetDistribution())); file_exe_override_.reset(new base::ScopedPathOverride( base::FILE_EXE, install_dir.Append(file_exe.BaseName()), true /* is_absolute */, false /* create */)); // Override these paths so that they can be found after the registry // override manager is in place. base::FilePath temp; PathService::Get(base::DIR_PROGRAM_FILES, &temp); program_files_override_.reset( new base::ScopedPathOverride(base::DIR_PROGRAM_FILES, temp)); PathService::Get(base::DIR_PROGRAM_FILESX86, &temp); program_files_x86_override_.reset( new base::ScopedPathOverride(base::DIR_PROGRAM_FILESX86, temp)); PathService::Get(base::DIR_LOCAL_APP_DATA, &temp); local_app_data_override_.reset( new base::ScopedPathOverride(base::DIR_LOCAL_APP_DATA, temp)); // Override the registry so that tests can freely push state to it. registry_override_manager_.OverrideRegistry(HKEY_CURRENT_USER); registry_override_manager_.OverrideRegistry(HKEY_LOCAL_MACHINE); // Chrome is installed as multi-install. const HKEY root = system_level_ ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; base::win::RegKey key(root, kClients, KEY_WRITE | KEY_WOW64_32KEY); ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(kChromeGuid, KEY_WRITE | KEY_WOW64_32KEY)); ASSERT_EQ(ERROR_SUCCESS, key.WriteValue( L"pv", base::ASCIIToUTF16(CHROME_VERSION_STRING).c_str())); ASSERT_EQ(ERROR_SUCCESS, key.Create(root, kClientState, KEY_WRITE | KEY_WOW64_32KEY)); ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(kChromeGuid, KEY_WRITE | KEY_WOW64_32KEY)); ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(L"UninstallArguments", L"--uninstall --multi-install --chrome")); // Provide an IGoogleUpdate on-demand update class factory so that this test // can provide a mocked-out instance. SetGoogleUpdateFactoryForTesting( base::Bind(&GoogleUpdateFactory::Create, base::Unretained(&mock_google_update_factory_))); // Configure the factory to return a generic failure by default. ON_CALL(mock_google_update_factory_, Create(_)) .WillByDefault(Return(E_FAIL)); // Create a mock IGoogleUpdate on-demand update class ASSERT_EQ(S_OK, CComObject::CreateInstance(&mock_on_demand_)); on_demand_holder_ = mock_on_demand_; // Configure the mock to return a generic failure by default. ON_CALL(*mock_on_demand_, CheckForUpdate(_, _)) .WillByDefault(Return(E_FAIL)); ON_CALL(*mock_on_demand_, Update(_, _)).WillByDefault(Return(E_FAIL)); // Compute a newer version. base::Version current_version(CHROME_VERSION_STRING); new_version_ = base::StringPrintf(L"%u.%u.%u.%u", current_version.components()[0], current_version.components()[1], current_version.components()[2] + 1, current_version.components()[3]); } void TearDown() override { // Remove the test's IGoogleUpdate on-demand update class factory. SetGoogleUpdateFactoryForTesting(OnDemandAppsClassFactory()); } // Set the default update policy in the registry. void SetDefaultUpdatePolicy(GoogleUpdateSettings::UpdatePolicy policy) const { base::win::RegKey policy_key(HKEY_LOCAL_MACHINE, kPoliciesKey, KEY_SET_VALUE); ASSERT_EQ(ERROR_SUCCESS, policy_key.WriteValue(kUpdateDefault, policy)); } // Stuffs |policy| in the registry for the app identified by |app_guid|. void SetAppUpdatePolicy(const base::char16* app_guid, GoogleUpdateSettings::UpdatePolicy policy) const { base::string16 value_name(L"Update"); value_name += app_guid; base::win::RegKey policy_key(HKEY_LOCAL_MACHINE, kPoliciesKey, KEY_SET_VALUE); ASSERT_EQ(ERROR_SUCCESS, policy_key.WriteValue(value_name.c_str(), policy)); } static const base::char16 kPoliciesKey[]; static const base::char16 kUpdateDefault[]; static const base::char16 kClients[]; static const base::char16 kClientState[]; static const base::char16 kChromeGuid[]; static const base::char16 kChromeBinariesGuid[]; scoped_refptr task_runner_; base::ThreadTaskRunnerHandle task_runner_handle_; bool system_level_; scoped_ptr file_exe_override_; scoped_ptr program_files_override_; scoped_ptr program_files_x86_override_; scoped_ptr local_app_data_override_; registry_util::RegistryOverrideManager registry_override_manager_; MockUpdateCheckCallbackReceiver callback_receiver_; MockGoogleUpdateFactory mock_google_update_factory_; CComObject* mock_on_demand_; base::win::ScopedComPtr on_demand_holder_; base::string16 new_version_; DISALLOW_COPY_AND_ASSIGN(GoogleUpdateWinTest); }; // static const base::char16 GoogleUpdateWinTest::kPoliciesKey[] = L"SOFTWARE\\Policies\\Google\\Update"; const base::char16 GoogleUpdateWinTest::kUpdateDefault[] = L"UpdateDefault"; const base::char16 GoogleUpdateWinTest::kClients[] = L"Software\\Google\\Update\\Clients"; const base::char16 GoogleUpdateWinTest::kClientState[] = L"Software\\Google\\Update\\ClientState"; const base::char16 GoogleUpdateWinTest::kChromeGuid[] = L"{8A69D345-D564-463c-AFF1-A69D9E530F96}"; const base::char16 GoogleUpdateWinTest::kChromeBinariesGuid[] = L"{4DC8B4CA-1BDA-483e-B5FA-D3C12E15B62D}"; // Test that an update check fails with the proper error code if Chrome isn't in // one of the expected install directories. TEST_P(GoogleUpdateWinTest, InvalidInstallDirectory) { // Override FILE_EXE so that it looks like the test is running from a // non-standard location. base::FilePath file_exe; base::FilePath dir_temp; ASSERT_TRUE(PathService::Get(base::FILE_EXE, &file_exe)); ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &dir_temp)); file_exe_override_.reset(); file_exe_override_.reset(new base::ScopedPathOverride( base::FILE_EXE, dir_temp.Append(file_exe.BaseName()), true /* is_absolute */, false /* create */)); EXPECT_CALL( callback_receiver_, OnUpdateCheckCallback(UPGRADE_ERROR, CANNOT_UPGRADE_CHROME_IN_THIS_DIRECTORY, _, _)); BeginUpdateCheck(task_runner_, false, 0, callback_receiver_.GetCallback()); task_runner_->RunUntilIdle(); } #if defined(GOOGLE_CHROME_BUILD) TEST_P(GoogleUpdateWinTest, AllUpdatesDisabledByPolicy) { // Disable updates altogether. SetDefaultUpdatePolicy(GoogleUpdateSettings::UPDATES_DISABLED); EXPECT_CALL(callback_receiver_, OnUpdateCheckCallback(UPGRADE_ERROR, GOOGLE_UPDATE_DISABLED_BY_POLICY, _, _)); BeginUpdateCheck(task_runner_, false, 0, callback_receiver_.GetCallback()); task_runner_->RunUntilIdle(); } TEST_P(GoogleUpdateWinTest, MultiUpdatesDisabledByPolicy) { // Disable updates altogether. SetAppUpdatePolicy(kChromeBinariesGuid, GoogleUpdateSettings::UPDATES_DISABLED); EXPECT_CALL(callback_receiver_, OnUpdateCheckCallback(UPGRADE_ERROR, GOOGLE_UPDATE_DISABLED_BY_POLICY, _, _)); BeginUpdateCheck(task_runner_, false, 0, callback_receiver_.GetCallback()); task_runner_->RunUntilIdle(); } TEST_P(GoogleUpdateWinTest, AllManualUpdatesDisabledByPolicy) { // Disable updates altogether. SetDefaultUpdatePolicy(GoogleUpdateSettings::AUTO_UPDATES_ONLY); EXPECT_CALL( callback_receiver_, OnUpdateCheckCallback(UPGRADE_ERROR, GOOGLE_UPDATE_DISABLED_BY_POLICY_AUTO_ONLY, _, _)); BeginUpdateCheck(task_runner_, false, 0, callback_receiver_.GetCallback()); task_runner_->RunUntilIdle(); } TEST_P(GoogleUpdateWinTest, MultiManualUpdatesDisabledByPolicy) { // Disable updates altogether. SetAppUpdatePolicy(kChromeBinariesGuid, GoogleUpdateSettings::AUTO_UPDATES_ONLY); EXPECT_CALL( callback_receiver_, OnUpdateCheckCallback(UPGRADE_ERROR, GOOGLE_UPDATE_DISABLED_BY_POLICY_AUTO_ONLY, _, _)); BeginUpdateCheck(task_runner_, false, 0, callback_receiver_.GetCallback()); task_runner_->RunUntilIdle(); } TEST_P(GoogleUpdateWinTest, NoGoogleUpdateForCheck) { // The factory should be called upon: let it fail. EXPECT_CALL(mock_google_update_factory_, Create(_)); // Expect the appropriate error when the on-demand class cannot be created. EXPECT_CALL(callback_receiver_, OnUpdateCheckCallback( UPGRADE_ERROR, GOOGLE_UPDATE_ONDEMAND_CLASS_NOT_FOUND, _, _)); BeginUpdateCheck(task_runner_, false, 0, callback_receiver_.GetCallback()); task_runner_->RunUntilIdle(); } TEST_P(GoogleUpdateWinTest, NoGoogleUpdateForUpgrade) { // The factory should be called upon: let it fail. EXPECT_CALL(mock_google_update_factory_, Create(_)); // Expect the appropriate error when the on-demand class cannot be created. EXPECT_CALL(callback_receiver_, OnUpdateCheckCallback( UPGRADE_ERROR, GOOGLE_UPDATE_ONDEMAND_CLASS_NOT_FOUND, _, _)); BeginUpdateCheck(task_runner_, true, 0, callback_receiver_.GetCallback()); task_runner_->RunUntilIdle(); } TEST_P(GoogleUpdateWinTest, FailUpdateCheck) { // The factory should be called upon: let it return the mock on-demand class. EXPECT_CALL(mock_google_update_factory_, Create(_)) .WillOnce(DoAll(SetArgPointee<0>(mock_on_demand_), Return(S_OK))); // The mock on-demand class should be called. EXPECT_CALL(*mock_on_demand_, CheckForUpdate(StrEq(kChromeBinariesGuid), _)); EXPECT_CALL( callback_receiver_, OnUpdateCheckCallback(UPGRADE_ERROR, GOOGLE_UPDATE_ONDEMAND_CLASS_REPORTED_ERROR, _, _)); BeginUpdateCheck(task_runner_, false, 0, callback_receiver_.GetCallback()); task_runner_->RunUntilIdle(); } TEST_P(GoogleUpdateWinTest, UpdateCheckNoUpdate) { EXPECT_CALL(mock_google_update_factory_, Create(_)) .WillOnce(DoAll(SetArgPointee<0>(mock_on_demand_), Return(S_OK))); mock_on_demand_->OnCheckRunUpToDateSequence(kChromeBinariesGuid); EXPECT_CALL(callback_receiver_, OnUpdateCheckCallback(UPGRADE_ALREADY_UP_TO_DATE, GOOGLE_UPDATE_NO_ERROR, IsEmpty(), _)); BeginUpdateCheck(task_runner_, false, 0, callback_receiver_.GetCallback()); task_runner_->RunUntilIdle(); } TEST_P(GoogleUpdateWinTest, UpdateCheckUpdateAvailable) { EXPECT_CALL(mock_google_update_factory_, Create(_)) .WillOnce(DoAll(SetArgPointee<0>(mock_on_demand_), Return(S_OK))); mock_on_demand_->OnCheckRunUpdateAvailableSequence(kChromeBinariesGuid, new_version_); EXPECT_CALL( callback_receiver_, OnUpdateCheckCallback(UPGRADE_IS_AVAILABLE, GOOGLE_UPDATE_NO_ERROR, IsEmpty(), StrEq(new_version_))); BeginUpdateCheck(task_runner_, false, 0, callback_receiver_.GetCallback()); task_runner_->RunUntilIdle(); } TEST_P(GoogleUpdateWinTest, UpdateInstalled) { EXPECT_CALL(mock_google_update_factory_, Create(_)) .WillOnce(DoAll(SetArgPointee<0>(mock_on_demand_), Return(S_OK))); mock_on_demand_->OnUpdateRunInstallUpdateSequence(kChromeBinariesGuid, new_version_); EXPECT_CALL(callback_receiver_, OnUpdateCheckCallback(UPGRADE_SUCCESSFUL, GOOGLE_UPDATE_NO_ERROR, IsEmpty(), StrEq(new_version_))); BeginUpdateCheck(task_runner_, true, 0, callback_receiver_.GetCallback()); task_runner_->RunUntilIdle(); } TEST_P(GoogleUpdateWinTest, UpdateFailed) { static const base::char16 kError[] = L"It didn't work."; EXPECT_CALL(mock_google_update_factory_, Create(_)) .WillOnce(DoAll(SetArgPointee<0>(mock_on_demand_), Return(S_OK))); mock_on_demand_->OnUpdateRunUpdateErrorSequence(kChromeBinariesGuid, kError); EXPECT_CALL(callback_receiver_, OnUpdateCheckCallback(UPGRADE_ERROR, GOOGLE_UPDATE_ERROR_UPDATING, StrEq(kError), IsEmpty())); BeginUpdateCheck(task_runner_, true, 0, callback_receiver_.GetCallback()); task_runner_->RunUntilIdle(); } #endif // defined(GOOGLE_CHROME_BUILD) INSTANTIATE_TEST_CASE_P(UserLevel, GoogleUpdateWinTest, Values(false)); INSTANTIATE_TEST_CASE_P(SystemLevel, GoogleUpdateWinTest, Values(true));