blob: bd9d48788c89705f137d0da2f3295b067f7bbda4 [file] [log] [blame]
// 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 <windows.h>
#include <atlbase.h>
#include <atlcom.h>
#include <wrl/client.h>
#include <memory>
#include "base/base_paths.h"
#include "base/bind.h"
#include "base/containers/queue.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/threading/thread_task_runner_handle.h"
#include "base/version.h"
#include "base/win/registry.h"
#include "chrome/common/chrome_version.h"
#include "chrome/install_static/test/scoped_install_details.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<UpdateCheckDelegate> 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<UpdateCheckDelegate> 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(
Microsoft::WRL::ComPtr<IGoogleUpdate3Web>* google_update) = 0;
};
class MockCurrentState : public CComObjectRootEx<CComSingleThreadModel>,
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<CComSingleThreadModel>, 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<MockCurrentState>* 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<MockCurrentState>* mock_state = MakeNextState(state);
if (state == STATE_DOWNLOADING) {
const ULONG kTotalBytes = 1024;
ULONG bytes_down = static_cast<double>(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<MockCurrentState>* MakeNextState(CurrentState state) {
CComObject<MockCurrentState>* 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<MockCurrentState>::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.
base::queue<CComObject<MockCurrentState>*> 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<CComSingleThreadModel>,
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<MockApp>* 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<MockApp>* mock_app = nullptr;
EXPECT_EQ(S_OK, CComObject<MockApp>::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<CComSingleThreadModel>,
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<MockAppBundle>* MakeAppBundle() {
CComObject<MockAppBundle>* mock_app_bundle = nullptr;
EXPECT_EQ(S_OK,
CComObject<MockAppBundle>::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(Microsoft::WRL::ComPtr<IGoogleUpdate3Web>*));
// Returns a mock IGoogleUpdate3Web object that will be returned by the
// factory.
CComObject<MockGoogleUpdate>* MakeServerMock() {
CComObject<MockGoogleUpdate>* mock_google_update = nullptr;
EXPECT_EQ(S_OK, CComObject<MockGoogleUpdate>::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<bool> {
public:
static void SetUpTestCase() {
ui::win::CreateATLModuleIfNeeded();
// Configure all mock functions that return HRESULT to return failure.
::testing::DefaultValue<HRESULT>::Set(E_FAIL);
}
static void TearDownTestCase() { ::testing::DefaultValue<HRESULT>::Clear(); }
protected:
GoogleUpdateWinTest()
: task_runner_(new base::TestSimpleTaskRunner()),
task_runner_handle_(task_runner_),
system_level_install_(GetParam()),
scoped_install_details_(system_level_install_, 0) {}
void SetUp() override {
::testing::TestWithParam<bool>::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_));
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.
ASSERT_NO_FATAL_FAILURE(
registry_override_manager_.OverrideRegistry(HKEY_CURRENT_USER));
ASSERT_NO_FATAL_FAILURE(
registry_override_manager_.OverrideRegistry(HKEY_LOCAL_MACHINE));
// Chrome is installed.
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"));
// 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]);
SetUpdateDriverTaskRunnerForTesting(task_runner_.get());
}
// Creates app bundle and app mocks that will be used to simulate Google
// Update.
void MakeGoogleUpdateMocks(CComObject<MockAppBundle>** mock_app_bundle,
CComObject<MockApp>** mock_app) {
CComObject<MockGoogleUpdate>* google_update =
mock_google_update_factory_.MakeServerMock();
CComObject<MockAppBundle>* app_bundle = google_update->MakeAppBundle();
CComObject<MockApp>* app = app_bundle->MakeApp(kChromeGuid);
if (mock_app_bundle)
*mock_app_bundle = app_bundle;
if (mock_app)
*mock_app = app;
}
void TearDown() override {
SetUpdateDriverTaskRunnerForTesting(nullptr);
// Remove the test's IGoogleUpdate on-demand update class factory.
SetGoogleUpdateFactoryForTesting(GoogleUpdate3ClassFactory());
::testing::TestWithParam<bool>::TearDown();
}
static const base::char16 kClients[];
static const base::char16 kClientState[];
static const base::char16 kChromeGuid[];
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
base::ThreadTaskRunnerHandle task_runner_handle_;
bool system_level_install_;
install_static::ScopedInstallDetails scoped_install_details_;
std::unique_ptr<base::ScopedPathOverride> file_exe_override_;
std::unique_ptr<base::ScopedPathOverride> program_files_override_;
std::unique_ptr<base::ScopedPathOverride> program_files_x86_override_;
std::unique_ptr<base::ScopedPathOverride> 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<MockUpdateCheckDelegate> 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<MockGoogleUpdateFactory> 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}";
// 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(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(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(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<MockAppBundle>* 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(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<MockAppBundle>* mock_app_bundle = nullptr;
CComObject<MockApp>* 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(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<MockAppBundle>* mock_app_bundle = nullptr;
CComObject<MockApp>* 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(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<MockAppBundle>* mock_app_bundle = nullptr;
CComObject<MockApp>* 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(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<MockAppBundle>* mock_app_bundle = nullptr;
CComObject<MockApp>* 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(std::string(), false, 0,
mock_update_check_delegate_.AsWeakPtr());
task_runner_->RunUntilIdle();
}
// Test a successful upgrade.
TEST_P(GoogleUpdateWinTest, UpdateInstalled) {
CComObject<MockAppBundle>* mock_app_bundle = nullptr;
CComObject<MockApp>* 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(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<MockAppBundle>* mock_app_bundle = nullptr;
CComObject<MockApp>* 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(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<MockAppBundle>* mock_app_bundle =
mock_google_update_factory_.MakeServerMock()->MakeAppBundle();
// The first attempt will fail in createInstalledApp indicating that an update
// is already in progress.
Sequence bundle_seq;
EXPECT_CALL(*mock_app_bundle, createInstalledApp(StrEq(kChromeGuid)))
.InSequence(bundle_seq)
.WillOnce(Return(GOOPDATE_E_APP_USING_EXTERNAL_UPDATER));
// Expect a retry on the same instance.
EXPECT_CALL(*mock_app_bundle, createInstalledApp(StrEq(kChromeGuid)))
.InSequence(bundle_seq)
.WillOnce(Return(S_OK));
// See MakeApp() for an explanation of this:
CComObject<MockApp>* mock_app = nullptr;
EXPECT_EQ(S_OK, CComObject<MockApp>::CreateInstance(&mock_app));
mock_app->AddRef();
EXPECT_CALL(*mock_app_bundle, get_appWeb(0, _))
.WillOnce(DoAll(SetArgPointee<1>(mock_app), Return(S_OK)));
// 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 the update check to succeed.
EXPECT_CALL(mock_update_check_delegate_,
OnUpdateCheckComplete(IsEmpty())); // new_version
BeginUpdateCheck(std::string(), false, 0,
mock_update_check_delegate_.AsWeakPtr());
task_runner_->RunUntilIdle();
}
TEST_P(GoogleUpdateWinTest, UpdateInstalledMultipleDelegates) {
CComObject<MockAppBundle>* mock_app_bundle = nullptr;
CComObject<MockApp>* 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<MockUpdateCheckDelegate> 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(std::string(), true, 0,
mock_update_check_delegate_.AsWeakPtr());
BeginUpdateCheck(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));