// 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/bind.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/path_service.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 "base/win/scoped_comptr.h" #include "chrome/common/chrome_version.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" using ::testing::DoAll; using ::testing::HasSubstr; using ::testing::InSequence; using ::testing::IsEmpty; using ::testing::Return; using ::testing::Sequence; using ::testing::SetArgPointee; using ::testing::StrEq; using ::testing::StrictMock; using ::testing::Values; using ::testing::_; namespace { class MockUpdateCheckDelegate : public UpdateCheckDelegate { public: MockUpdateCheckDelegate() : weak_ptr_factory_(this) {} base::WeakPtr AsWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); } MOCK_METHOD1(OnUpdateCheckComplete, void(const base::string16&)); MOCK_METHOD2(OnUpgradeProgress, void(int, const base::string16&)); MOCK_METHOD1(OnUpgradeComplete, void(const base::string16&)); MOCK_METHOD3(OnError, void(GoogleUpdateErrorCode, const base::string16&, const base::string16&)); private: base::WeakPtrFactory weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(MockUpdateCheckDelegate); }; // An interface that exposes a factory method for creating an IGoogleUpdate3Web // instance. class GoogleUpdateFactory { public: virtual ~GoogleUpdateFactory() {} virtual HRESULT Create( base::win::ScopedComPtr* google_update) = 0; }; class MockCurrentState : public CComObjectRootEx, public ICurrentState { public: BEGIN_COM_MAP(MockCurrentState) COM_INTERFACE_ENTRY(ICurrentState) END_COM_MAP() MockCurrentState() {} // Adds an expectation for get_completionMessage that will return the given // message any number of times. void ExpectCompletionMessage(const base::string16& completion_message) { completion_message_ = completion_message; EXPECT_CALL(*this, get_completionMessage(_)) .WillRepeatedly( ::testing::Invoke(this, &MockCurrentState::GetCompletionMessage)); } HRESULT GetCompletionMessage(BSTR* completion_message) { *completion_message = SysAllocString(completion_message_.c_str()); return S_OK; } // Adds an expectation for get_availableVersion that will return the given // version any number of times. void ExpectAvailableVersion(const base::string16& available_version) { available_version_ = available_version; EXPECT_CALL(*this, get_availableVersion(_)) .WillRepeatedly( ::testing::Invoke(this, &MockCurrentState::GetAvailableVersion)); } HRESULT GetAvailableVersion(BSTR* available_version) { *available_version = SysAllocString(available_version_.c_str()); return S_OK; } // ICurrentState: MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, get_stateValue, HRESULT(LONG *)); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, get_availableVersion, HRESULT(BSTR *)); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, get_bytesDownloaded, HRESULT(ULONG *)); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, get_totalBytesToDownload, HRESULT(ULONG *)); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, get_downloadTimeRemainingMs, HRESULT(LONG *)); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, get_nextRetryTime, HRESULT(ULONGLONG *)); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, get_installProgress, HRESULT(LONG *)); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, get_installTimeRemainingMs, HRESULT(LONG *)); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, get_isCanceled, HRESULT(VARIANT_BOOL *)); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, get_errorCode, HRESULT(LONG *)); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, get_extraCode1, HRESULT(LONG *)); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, get_completionMessage, HRESULT(BSTR *)); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, get_installerResultCode, HRESULT(LONG *)); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, get_installerResultExtraCode1, HRESULT(LONG *)); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, get_postInstallLaunchCommandLine, HRESULT(BSTR *)); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, get_postInstallUrl, HRESULT(BSTR *)); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, get_postInstallAction, HRESULT(LONG *)); // IDispatch: MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, GetTypeInfoCount, HRESULT(UINT *)); MOCK_METHOD3_WITH_CALLTYPE(STDMETHODCALLTYPE, GetTypeInfo, HRESULT(UINT, LCID, ITypeInfo **)); MOCK_METHOD5_WITH_CALLTYPE(STDMETHODCALLTYPE, GetIDsOfNames, HRESULT(REFIID, LPOLESTR *, UINT, LCID, DISPID *)); MOCK_METHOD8_WITH_CALLTYPE(STDMETHODCALLTYPE, Invoke, HRESULT(DISPID, REFIID, LCID, WORD, DISPPARAMS *, VARIANT *, EXCEPINFO *, UINT *)); private: base::string16 completion_message_; base::string16 available_version_; DISALLOW_COPY_AND_ASSIGN(MockCurrentState); }; // A mock IAppWeb that can run callers of get_currentState through a sequence of // pre-programmed states registered via the various Push*State methods. class MockApp : public CComObjectRootEx, public IAppWeb { public: BEGIN_COM_MAP(MockApp) COM_INTERFACE_ENTRY(IAppWeb) END_COM_MAP() MockApp() { // Connect get_currentState so that each call will go to GetNextState. ON_CALL(*this, get_currentState(_)) .WillByDefault(::testing::Invoke(this, &MockApp::GetNextState)); } // IAppWeb: MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, get_appId, HRESULT(BSTR *)); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, get_currentVersionWeb, HRESULT(IDispatch **)); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, get_nextVersionWeb, HRESULT(IDispatch **)); MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, get_command, HRESULT(BSTR, IDispatch **)); MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, cancel, HRESULT()); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, get_currentState, HRESULT(IDispatch **)); MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, launch, HRESULT()); MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, uninstall, HRESULT()); // IDispatch: MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, GetTypeInfoCount, HRESULT(UINT *)); MOCK_METHOD3_WITH_CALLTYPE(STDMETHODCALLTYPE, GetTypeInfo, HRESULT(UINT, LCID, ITypeInfo **)); MOCK_METHOD5_WITH_CALLTYPE(STDMETHODCALLTYPE, GetIDsOfNames, HRESULT(REFIID, LPOLESTR *, UINT, LCID, DISPID *)); MOCK_METHOD8_WITH_CALLTYPE(STDMETHODCALLTYPE, Invoke, HRESULT(DISPID, REFIID, LCID, WORD, DISPPARAMS *, VARIANT *, EXCEPINFO *, UINT *)); // Adds a MockCurrentState to the back of the sequence to be returned by the // mock IAppWeb. void PushState(CurrentState state) { MakeNextState(state); } // Adds a MockCurrentState to the back of the sequence to be returned by the // mock IAppWeb for an ERROR state. void PushErrorState(LONG error_code, const base::string16& completion_message, LONG installer_result_code) { CComObject* mock_state = MakeNextState(STATE_ERROR); EXPECT_CALL(*mock_state, get_errorCode(_)) .WillRepeatedly(DoAll(SetArgPointee<0>(error_code), Return(S_OK))); mock_state->ExpectCompletionMessage(completion_message); if (installer_result_code != -1) { EXPECT_CALL(*mock_state, get_installerResultCode(_)) .WillRepeatedly(DoAll(SetArgPointee<0>(installer_result_code), Return(S_OK))); } } // Adds a MockCurrentState to the back of the sequence to be returned by the // mock IAppWeb for an UPDATE_AVAILABLE state. void PushUpdateAvailableState(const base::string16& new_version) { MakeNextState(STATE_UPDATE_AVAILABLE)->ExpectAvailableVersion(new_version); } // Adds a MockCurrentState to the back of the sequence to be returned by the // mock IAppWeb for a DOWNLOADING or INSTALLING state. void PushProgressiveState(CurrentState state, int progress) { CComObject* mock_state = MakeNextState(state); if (state == STATE_DOWNLOADING) { const ULONG kTotalBytes = 1024; ULONG bytes_down = static_cast(kTotalBytes) * progress / 100.0; EXPECT_CALL(*mock_state, get_totalBytesToDownload(_)) .WillRepeatedly(DoAll(SetArgPointee<0>(kTotalBytes), Return(S_OK))); EXPECT_CALL(*mock_state, get_bytesDownloaded(_)) .WillRepeatedly(DoAll(SetArgPointee<0>(bytes_down), Return(S_OK))); } else if (state == STATE_INSTALLING) { EXPECT_CALL(*mock_state, get_installProgress(_)) .WillRepeatedly(DoAll(SetArgPointee<0>(progress), Return(S_OK))); } else { ADD_FAILURE() << "unsupported state " << state; } } private: // Returns a new MockCurrentState that will be returned by the mock IAppWeb's // get_currentState method. CComObject* MakeNextState(CurrentState state) { CComObject* mock_state = nullptr; // The new object's refcount is held at zero until it is released from the // simulator in GetNextState. EXPECT_EQ(S_OK, CComObject::CreateInstance(&mock_state)); EXPECT_CALL(*mock_state, get_stateValue(_)) .WillRepeatedly(DoAll(SetArgPointee<0>(state), Return(S_OK))); states_.push(mock_state); // Tell the app to expect this state. EXPECT_CALL(*this, get_currentState(_)).InSequence(state_sequence_); return mock_state; } // An implementation of IAppWeb::get_currentState that advances the // IGoogleUpdate3Web simulator through a series of states. HRESULT GetNextState(IDispatch** current_state) { EXPECT_FALSE(states_.empty()); *current_state = states_.front(); // Give a reference to the caller. (*current_state)->AddRef(); states_.pop(); return S_OK; } // The states returned by the MockApp when probed. std::queue*> states_; // A gmock sequence under which a series of get_CurrentState expectations are // evaluated. Sequence state_sequence_; DISALLOW_COPY_AND_ASSIGN(MockApp); }; // A mock IAppBundleWeb that can handle a single call to createInstalledApp // followed by get_appWeb. class MockAppBundle : public CComObjectRootEx, public IAppBundleWeb { public: BEGIN_COM_MAP(MockAppBundle) COM_INTERFACE_ENTRY(IAppBundleWeb) END_COM_MAP() MockAppBundle() {} // IAppBundleWeb: MOCK_METHOD4_WITH_CALLTYPE(STDMETHODCALLTYPE, createApp, HRESULT(BSTR, BSTR, BSTR, BSTR)); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, createInstalledApp, HRESULT(BSTR)); MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, createAllInstalledApps, HRESULT()); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, get_displayLanguage, HRESULT(BSTR *)); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, put_displayLanguage, HRESULT(BSTR)); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, put_parentHWND, HRESULT(ULONG_PTR)); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, get_length, HRESULT(int *)); MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, get_appWeb, HRESULT(int, IDispatch **)); MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, initialize, HRESULT()); MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, checkForUpdate, HRESULT()); MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, download, HRESULT()); MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, install, HRESULT()); MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, pause, HRESULT()); MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, resume, HRESULT()); MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, cancel, HRESULT()); MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, downloadPackage, HRESULT(BSTR, BSTR)); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, get_currentState, HRESULT(VARIANT *)); // IDispatch: MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, GetTypeInfoCount, HRESULT(UINT *)); MOCK_METHOD3_WITH_CALLTYPE(STDMETHODCALLTYPE, GetTypeInfo, HRESULT(UINT, LCID, ITypeInfo **)); MOCK_METHOD5_WITH_CALLTYPE(STDMETHODCALLTYPE, GetIDsOfNames, HRESULT(REFIID, LPOLESTR *, UINT, LCID, DISPID *)); MOCK_METHOD8_WITH_CALLTYPE(STDMETHODCALLTYPE, Invoke, HRESULT(DISPID, REFIID, LCID, WORD, DISPPARAMS *, VARIANT *, EXCEPINFO *, UINT *)); // Returns a MockApp for the given |app_guid| that will be returned by this // instance's get_appWeb method. The returned instance is only valid for use // in setting up expectations until a consumer obtains it via get_appWeb, at // which time it is owned by the consumer. CComObject* MakeApp(const base::char16* app_guid) { // The bundle will be called on to create the installed app. EXPECT_CALL(*this, createInstalledApp(StrEq(app_guid))) .WillOnce(Return(S_OK)); CComObject* mock_app = nullptr; EXPECT_EQ(S_OK, CComObject::CreateInstance(&mock_app)); // Give mock_app_bundle a ref to the app which it will return when asked. // Note: to support multiple apps, get_appWeb expectations should use // successive indices. mock_app->AddRef(); EXPECT_CALL(*this, get_appWeb(0, _)) .WillOnce(DoAll(SetArgPointee<1>(mock_app), Return(S_OK))); return mock_app; } private: DISALLOW_COPY_AND_ASSIGN(MockAppBundle); }; // A mock IGoogleUpdate3Web that can handle a call to initialize and // createAppBundleWeb by consumers. class MockGoogleUpdate : public CComObjectRootEx, public IGoogleUpdate3Web { public: BEGIN_COM_MAP(MockGoogleUpdate) COM_INTERFACE_ENTRY(IGoogleUpdate3Web) END_COM_MAP() MockGoogleUpdate() {} // IGoogleUpdate3Web: MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, createAppBundleWeb, HRESULT(IDispatch**)); // IDispatch: MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, GetTypeInfoCount, HRESULT(UINT *)); MOCK_METHOD3_WITH_CALLTYPE(STDMETHODCALLTYPE, GetTypeInfo, HRESULT(UINT, LCID, ITypeInfo **)); MOCK_METHOD5_WITH_CALLTYPE(STDMETHODCALLTYPE, GetIDsOfNames, HRESULT(REFIID, LPOLESTR *, UINT, LCID, DISPID *)); MOCK_METHOD8_WITH_CALLTYPE(STDMETHODCALLTYPE, Invoke, HRESULT(DISPID, REFIID, LCID, WORD, DISPPARAMS *, VARIANT *, EXCEPINFO *, UINT *)); // Returns a MockAppBundle that will be returned by this instance's // createAppBundleWeb method. The returned instance is only valid for use in // setting up expectations until a consumer obtains it via createAppBundleWeb, // at which time it is owned by the consumer. CComObject* MakeAppBundle() { CComObject* mock_app_bundle = nullptr; EXPECT_EQ(S_OK, CComObject::CreateInstance(&mock_app_bundle)); EXPECT_CALL(*mock_app_bundle, initialize()) .WillOnce(Return(S_OK)); // Give this instance a ref to the bundle which it will return when created. mock_app_bundle->AddRef(); EXPECT_CALL(*this, createAppBundleWeb(_)) .WillOnce(DoAll(SetArgPointee<0>(mock_app_bundle), Return(S_OK))); return mock_app_bundle; } private: DISALLOW_COPY_AND_ASSIGN(MockGoogleUpdate); }; // A mock factory for creating an IGoogleUpdate3Web instance. class MockGoogleUpdateFactory : public GoogleUpdateFactory { public: MockGoogleUpdateFactory() {} MOCK_METHOD1(Create, HRESULT(base::win::ScopedComPtr*)); // Returns a mock IGoogleUpdate3Web object that will be returned by the // factory. CComObject* MakeServerMock() { CComObject* mock_google_update = nullptr; EXPECT_EQ(S_OK, CComObject::CreateInstance( &mock_google_update)); // Give the factory this updater. Do not add a ref, as the factory will add // one when it hands out its instance. EXPECT_CALL(*this, Create(_)) .InSequence(sequence_) .WillOnce(DoAll(SetArgPointee<0>(mock_google_update), Return(S_OK))); return mock_google_update; } private: Sequence sequence_; DISALLOW_COPY_AND_ASSIGN(MockGoogleUpdateFactory); }; } // namespace // A test fixture that can simulate the IGoogleUpdate3Web API via Google Mock. // Individual tests must wire up the factories by a call to one of the // PrepareSimulator methods. The family of Push*State methods are then used to // configure the set of states to be simulated. class GoogleUpdateWinTest : public ::testing::TestWithParam { public: static void SetUpTestCase() { ui::win::CreateATLModuleIfNeeded(); // Configure all mock functions that return HRESULT to return failure. ::testing::DefaultValue::Set(E_FAIL); } static void TearDownTestCase() { ::testing::DefaultValue::Clear(); } protected: GoogleUpdateWinTest() : task_runner_(new base::TestSimpleTaskRunner()), task_runner_handle_(task_runner_), system_level_install_(GetParam()) {} 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_install_, 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_install_ ? 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 IGoogleUpdate3Web class factory so that this test can provide // a mocked-out instance. SetGoogleUpdateFactoryForTesting( base::Bind(&GoogleUpdateFactory::Create, base::Unretained(&mock_google_update_factory_))); // 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]); } // Creates app bundle and app mocks that will be used to simulate Google // Update. void MakeGoogleUpdateMocks(CComObject** mock_app_bundle, CComObject** mock_app) { CComObject* google_update = mock_google_update_factory_.MakeServerMock(); CComObject* app_bundle = google_update->MakeAppBundle(); CComObject* app = app_bundle->MakeApp(kChromeBinariesGuid); if (mock_app_bundle) *mock_app_bundle = app_bundle; if (mock_app) *mock_app = app; } void TearDown() override { // Remove the test's IGoogleUpdate on-demand update class factory. SetGoogleUpdateFactoryForTesting(GoogleUpdate3ClassFactory()); ::testing::TestWithParam::TearDown(); } 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_install_; 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_; // A mock object, the OnUpdateCheckCallback method of which will be invoked // each time the update check machinery invokes the given UpdateCheckCallback. StrictMock mock_update_check_delegate_; // A mock object that provides a GoogleUpdate3ClassFactory by which the test // fixture's IGoogleUpdate3Web simulator is provided to the update check // machinery. StrictMock mock_google_update_factory_; // The new version that the fixture will pretend is available. base::string16 new_version_; private: DISALLOW_COPY_AND_ASSIGN(GoogleUpdateWinTest); }; // static 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(mock_update_check_delegate_, OnError(CANNOT_UPGRADE_CHROME_IN_THIS_DIRECTORY, _, _)); BeginUpdateCheck(task_runner_, std::string(), false, 0, mock_update_check_delegate_.AsWeakPtr()); task_runner_->RunUntilIdle(); } // Test the case where the GoogleUpdate class can't be created for an update // check. 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(mock_update_check_delegate_, OnError(GOOGLE_UPDATE_ONDEMAND_CLASS_NOT_FOUND, _, _)); BeginUpdateCheck(task_runner_, std::string(), false, 0, mock_update_check_delegate_.AsWeakPtr()); task_runner_->RunUntilIdle(); } // Test the case where the GoogleUpdate class can't be created for an upgrade. 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(mock_update_check_delegate_, OnError(GOOGLE_UPDATE_ONDEMAND_CLASS_NOT_FOUND, _, _)); BeginUpdateCheck(task_runner_, std::string(), true, 0, mock_update_check_delegate_.AsWeakPtr()); task_runner_->RunUntilIdle(); } // Test the case where the GoogleUpdate class returns an error when an update // check is started. TEST_P(GoogleUpdateWinTest, FailUpdateCheck) { CComObject* mock_app_bundle = nullptr; MakeGoogleUpdateMocks(&mock_app_bundle, nullptr); // checkForUpdate will fail. EXPECT_CALL(*mock_app_bundle, checkForUpdate()) .WillOnce(Return(E_FAIL)); EXPECT_CALL(mock_update_check_delegate_, OnError(GOOGLE_UPDATE_ONDEMAND_CLASS_REPORTED_ERROR, _, _)); BeginUpdateCheck(task_runner_, std::string(), false, 0, mock_update_check_delegate_.AsWeakPtr()); task_runner_->RunUntilIdle(); } // Test the case where the GoogleUpdate class reports that updates are disabled // by Group Policy. TEST_P(GoogleUpdateWinTest, UpdatesDisabledByPolicy) { static const HRESULT GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY = 0x80040813; CComObject* mock_app_bundle = nullptr; CComObject* mock_app = nullptr; MakeGoogleUpdateMocks(&mock_app_bundle, &mock_app); // Expect the bundle to be called on to start the update. EXPECT_CALL(*mock_app_bundle, checkForUpdate()) .WillOnce(Return(S_OK)); mock_app->PushState(STATE_INIT); mock_app->PushState(STATE_CHECKING_FOR_UPDATE); mock_app->PushErrorState(GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY, L"disabled by policy", -1); EXPECT_CALL(mock_update_check_delegate_, OnError(GOOGLE_UPDATE_DISABLED_BY_POLICY, _, _)); BeginUpdateCheck(task_runner_, std::string(), false, 0, mock_update_check_delegate_.AsWeakPtr()); task_runner_->RunUntilIdle(); } // Test the case where the GoogleUpdate class reports that manual updates are // disabled by Group Policy, but that automatic updates are enabled. TEST_P(GoogleUpdateWinTest, ManualUpdatesDisabledByPolicy) { static const HRESULT GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY_MANUAL = 0x8004081f; CComObject* mock_app_bundle = nullptr; CComObject* mock_app = nullptr; MakeGoogleUpdateMocks(&mock_app_bundle, &mock_app); // Expect the bundle to be called on to start the update. EXPECT_CALL(*mock_app_bundle, checkForUpdate()) .WillOnce(Return(S_OK)); mock_app->PushState(STATE_INIT); mock_app->PushState(STATE_CHECKING_FOR_UPDATE); mock_app->PushErrorState(GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY_MANUAL, L"manual updates disabled by policy", -1); EXPECT_CALL(mock_update_check_delegate_, OnError(GOOGLE_UPDATE_DISABLED_BY_POLICY_AUTO_ONLY, _, _)); BeginUpdateCheck(task_runner_, std::string(), false, 0, mock_update_check_delegate_.AsWeakPtr()); task_runner_->RunUntilIdle(); } // Test an update check where no update is available. TEST_P(GoogleUpdateWinTest, UpdateCheckNoUpdate) { CComObject* mock_app_bundle = nullptr; CComObject* mock_app = nullptr; MakeGoogleUpdateMocks(&mock_app_bundle, &mock_app); // Expect the bundle to be called on to start the update. EXPECT_CALL(*mock_app_bundle, checkForUpdate()) .WillOnce(Return(S_OK)); mock_app->PushState(STATE_INIT); mock_app->PushState(STATE_CHECKING_FOR_UPDATE); mock_app->PushState(STATE_NO_UPDATE); EXPECT_CALL(mock_update_check_delegate_, OnUpdateCheckComplete(IsEmpty())); // new_version BeginUpdateCheck(task_runner_, std::string(), false, 0, mock_update_check_delegate_.AsWeakPtr()); task_runner_->RunUntilIdle(); } // Test an update check where an update is available. TEST_P(GoogleUpdateWinTest, UpdateCheckUpdateAvailable) { CComObject* mock_app_bundle = nullptr; CComObject* mock_app = nullptr; MakeGoogleUpdateMocks(&mock_app_bundle, &mock_app); // Expect the bundle to be called on to start the update. EXPECT_CALL(*mock_app_bundle, checkForUpdate()) .WillOnce(Return(S_OK)); mock_app->PushState(STATE_INIT); mock_app->PushState(STATE_CHECKING_FOR_UPDATE); mock_app->PushUpdateAvailableState(new_version_); EXPECT_CALL(mock_update_check_delegate_, OnUpdateCheckComplete(StrEq(new_version_))); BeginUpdateCheck(task_runner_, std::string(), false, 0, mock_update_check_delegate_.AsWeakPtr()); task_runner_->RunUntilIdle(); } // Test a successful upgrade. TEST_P(GoogleUpdateWinTest, UpdateInstalled) { CComObject* mock_app_bundle = nullptr; CComObject* mock_app = nullptr; MakeGoogleUpdateMocks(&mock_app_bundle, &mock_app); // Expect the bundle to be called on to start the update. EXPECT_CALL(*mock_app_bundle, checkForUpdate()) .WillOnce(Return(S_OK)); // Expect the bundle to be called on to start the install. EXPECT_CALL(*mock_app_bundle, install()) .WillOnce(Return(S_OK)); mock_app->PushState(STATE_INIT); mock_app->PushState(STATE_CHECKING_FOR_UPDATE); mock_app->PushUpdateAvailableState(new_version_); mock_app->PushState(STATE_WAITING_TO_DOWNLOAD); mock_app->PushProgressiveState(STATE_DOWNLOADING, 0); mock_app->PushProgressiveState(STATE_DOWNLOADING, 25); mock_app->PushProgressiveState(STATE_DOWNLOADING, 25); mock_app->PushProgressiveState(STATE_DOWNLOADING, 75); mock_app->PushState(STATE_WAITING_TO_INSTALL); mock_app->PushProgressiveState(STATE_INSTALLING, 50); mock_app->PushState(STATE_INSTALL_COMPLETE); { InSequence callback_sequence; EXPECT_CALL(mock_update_check_delegate_, OnUpgradeProgress(0, StrEq(new_version_))); EXPECT_CALL(mock_update_check_delegate_, OnUpgradeProgress(12, StrEq(new_version_))); EXPECT_CALL(mock_update_check_delegate_, OnUpgradeProgress(37, StrEq(new_version_))); EXPECT_CALL(mock_update_check_delegate_, OnUpgradeProgress(50, StrEq(new_version_))); EXPECT_CALL(mock_update_check_delegate_, OnUpgradeProgress(75, StrEq(new_version_))); EXPECT_CALL(mock_update_check_delegate_, OnUpgradeComplete(StrEq(new_version_))); } BeginUpdateCheck(task_runner_, std::string(), true, 0, mock_update_check_delegate_.AsWeakPtr()); task_runner_->RunUntilIdle(); } // Test a failed upgrade where Google Update reports that the installer failed. TEST_P(GoogleUpdateWinTest, UpdateFailed) { const base::string16 error(L"It didn't work."); static const HRESULT GOOPDATEINSTALL_E_INSTALLER_FAILED = 0x80040902; static const int kInstallerError = 12; CComObject* mock_app_bundle = nullptr; CComObject* mock_app = nullptr; MakeGoogleUpdateMocks(&mock_app_bundle, &mock_app); // Expect the bundle to be called on to start the update. EXPECT_CALL(*mock_app_bundle, checkForUpdate()) .WillOnce(Return(S_OK)); // Expect the bundle to be called on to start the install. EXPECT_CALL(*mock_app_bundle, install()) .WillOnce(Return(S_OK)); mock_app->PushState(STATE_INIT); mock_app->PushState(STATE_CHECKING_FOR_UPDATE); mock_app->PushUpdateAvailableState(new_version_); mock_app->PushState(STATE_WAITING_TO_DOWNLOAD); mock_app->PushProgressiveState(STATE_DOWNLOADING, 0); mock_app->PushProgressiveState(STATE_DOWNLOADING, 25); mock_app->PushProgressiveState(STATE_DOWNLOADING, 25); mock_app->PushProgressiveState(STATE_DOWNLOADING, 75); mock_app->PushState(STATE_WAITING_TO_INSTALL); mock_app->PushProgressiveState(STATE_INSTALLING, 50); mock_app->PushErrorState(GOOPDATEINSTALL_E_INSTALLER_FAILED, error, kInstallerError); { InSequence callback_sequence; EXPECT_CALL(mock_update_check_delegate_, OnUpgradeProgress(0, StrEq(new_version_))); EXPECT_CALL(mock_update_check_delegate_, OnUpgradeProgress(12, StrEq(new_version_))); EXPECT_CALL(mock_update_check_delegate_, OnUpgradeProgress(37, StrEq(new_version_))); EXPECT_CALL(mock_update_check_delegate_, OnUpgradeProgress(50, StrEq(new_version_))); EXPECT_CALL(mock_update_check_delegate_, OnUpgradeProgress(75, StrEq(new_version_))); EXPECT_CALL(mock_update_check_delegate_, OnError(GOOGLE_UPDATE_ERROR_UPDATING, HasSubstr(error), StrEq(new_version_))); } BeginUpdateCheck(task_runner_, std::string(), true, 0, mock_update_check_delegate_.AsWeakPtr()); task_runner_->RunUntilIdle(); } // Test that a retry after a USING_EXTERNAL_UPDATER failure succeeds. TEST_P(GoogleUpdateWinTest, RetryAfterExternalUpdaterError) { static const HRESULT GOOPDATE_E_APP_USING_EXTERNAL_UPDATER = 0xa043081d; CComObject* mock_app_bundle1 = mock_google_update_factory_.MakeServerMock()->MakeAppBundle(); // The first attempt will fail in createInstalledApp indicating that an update // is already in progress. EXPECT_CALL(*mock_app_bundle1, createInstalledApp(StrEq(kChromeBinariesGuid))) .WillOnce(Return(GOOPDATE_E_APP_USING_EXTERNAL_UPDATER)); // Retry with a second instance. CComObject* mock_app_bundle2 = nullptr; CComObject* mock_app = nullptr; MakeGoogleUpdateMocks(&mock_app_bundle2, &mock_app); // Expect the bundle to be called on to start the update. EXPECT_CALL(*mock_app_bundle2, checkForUpdate()) .WillOnce(Return(S_OK)); mock_app->PushState(STATE_INIT); mock_app->PushState(STATE_CHECKING_FOR_UPDATE); mock_app->PushState(STATE_NO_UPDATE); // Until http://crbug.com/504516 is fixed, we expect the first update check // to fail. EXPECT_CALL(mock_update_check_delegate_, OnError(GOOGLE_UPDATE_ONDEMAND_CLASS_REPORTED_ERROR, _, _)); // Run the first update check. When http://crbug.com/504516 is fixed, this // update check will be subsumed in the one below. BeginUpdateCheck(task_runner_, std::string(), true, 0, mock_update_check_delegate_.AsWeakPtr()); task_runner_->RunUntilIdle(); // Expect a second update check to succeed. When http://crbug.com/504516 is // fixed, this will be the one and only update check. EXPECT_CALL(mock_update_check_delegate_, OnUpdateCheckComplete(IsEmpty())); // new_version BeginUpdateCheck(task_runner_, std::string(), false, 0, mock_update_check_delegate_.AsWeakPtr()); task_runner_->RunUntilIdle(); } TEST_P(GoogleUpdateWinTest, UpdateInstalledMultipleDelegates) { CComObject* mock_app_bundle = nullptr; CComObject* mock_app = nullptr; MakeGoogleUpdateMocks(&mock_app_bundle, &mock_app); // Expect the bundle to be called on to start the update. EXPECT_CALL(*mock_app_bundle, checkForUpdate()).WillOnce(Return(S_OK)); // Expect the bundle to be called on to start the install. EXPECT_CALL(*mock_app_bundle, install()).WillOnce(Return(S_OK)); mock_app->PushState(STATE_INIT); mock_app->PushState(STATE_CHECKING_FOR_UPDATE); mock_app->PushUpdateAvailableState(new_version_); mock_app->PushState(STATE_WAITING_TO_DOWNLOAD); mock_app->PushProgressiveState(STATE_DOWNLOADING, 0); mock_app->PushProgressiveState(STATE_DOWNLOADING, 25); mock_app->PushProgressiveState(STATE_DOWNLOADING, 25); mock_app->PushProgressiveState(STATE_DOWNLOADING, 75); mock_app->PushState(STATE_WAITING_TO_INSTALL); mock_app->PushProgressiveState(STATE_INSTALLING, 50); mock_app->PushState(STATE_INSTALL_COMPLETE); StrictMock mock_update_check_delegate_2; { InSequence callback_sequence; EXPECT_CALL(mock_update_check_delegate_, OnUpgradeProgress(0, StrEq(new_version_))); EXPECT_CALL(mock_update_check_delegate_2, OnUpgradeProgress(0, StrEq(new_version_))); EXPECT_CALL(mock_update_check_delegate_, OnUpgradeProgress(12, StrEq(new_version_))); EXPECT_CALL(mock_update_check_delegate_2, OnUpgradeProgress(12, StrEq(new_version_))); EXPECT_CALL(mock_update_check_delegate_, OnUpgradeProgress(37, StrEq(new_version_))); EXPECT_CALL(mock_update_check_delegate_2, OnUpgradeProgress(37, StrEq(new_version_))); EXPECT_CALL(mock_update_check_delegate_, OnUpgradeProgress(50, StrEq(new_version_))); EXPECT_CALL(mock_update_check_delegate_2, OnUpgradeProgress(50, StrEq(new_version_))); EXPECT_CALL(mock_update_check_delegate_, OnUpgradeProgress(75, StrEq(new_version_))); EXPECT_CALL(mock_update_check_delegate_2, OnUpgradeProgress(75, StrEq(new_version_))); EXPECT_CALL(mock_update_check_delegate_, OnUpgradeComplete(StrEq(new_version_))); EXPECT_CALL(mock_update_check_delegate_2, OnUpgradeComplete(StrEq(new_version_))); } BeginUpdateCheck(task_runner_, std::string(), true, 0, mock_update_check_delegate_.AsWeakPtr()); BeginUpdateCheck(task_runner_, std::string(), true, 0, mock_update_check_delegate_2.AsWeakPtr()); task_runner_->RunUntilIdle(); } INSTANTIATE_TEST_CASE_P(UserLevel, GoogleUpdateWinTest, Values(false)); INSTANTIATE_TEST_CASE_P(SystemLevel, GoogleUpdateWinTest, Values(true));