blob: db88da66409178613e59491cc6913f0efd3cc060 [file] [log] [blame]
// Copyright (c) 2012 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/installer/setup/setup_util_unittest.h"
#include <windows.h>
#include <shlobj.h>
#include <memory>
#include <string>
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/process/kill.h"
#include "base/process/launch.h"
#include "base/process/process_handle.h"
#include "base/strings/string_util.h"
#include "base/test/histogram_tester.h"
#include "base/test/test_reg_util_win.h"
#include "base/test/test_timeouts.h"
#include "base/threading/platform_thread.h"
#include "base/version.h"
#include "base/win/registry.h"
#include "base/win/scoped_handle.h"
#include "base/win/windows_version.h"
#include "chrome/installer/setup/installer_state.h"
#include "chrome/installer/setup/setup_constants.h"
#include "chrome/installer/setup/setup_util.h"
#include "chrome/installer/util/browser_distribution.h"
#include "chrome/installer/util/google_update_constants.h"
#include "chrome/installer/util/install_util.h"
#include "chrome/installer/util/installation_state.h"
#include "chrome/installer/util/updating_app_registration_data.h"
#include "chrome/installer/util/util_constants.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
// The privilege tested in ScopeTokenPrivilege tests below.
// Use SE_RESTORE_NAME as it is one of the many privileges that is available,
// but not enabled by default on processes running at high integrity.
static const wchar_t kTestedPrivilege[] = SE_RESTORE_NAME;
// Returns true if the current process' token has privilege |privilege_name|
// enabled.
bool CurrentProcessHasPrivilege(const wchar_t* privilege_name) {
HANDLE temp_handle;
if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY,
&temp_handle)) {
ADD_FAILURE();
return false;
}
base::win::ScopedHandle token(temp_handle);
// First get the size of the buffer needed for |privileges| below.
DWORD size;
EXPECT_FALSE(::GetTokenInformation(token.Get(), TokenPrivileges, NULL, 0,
&size));
std::unique_ptr<BYTE[]> privileges_bytes(new BYTE[size]);
TOKEN_PRIVILEGES* privileges =
reinterpret_cast<TOKEN_PRIVILEGES*>(privileges_bytes.get());
if (!::GetTokenInformation(token.Get(), TokenPrivileges, privileges, size,
&size)) {
ADD_FAILURE();
return false;
}
// There is no point getting a buffer to store more than |privilege_name|\0 as
// anything longer will obviously not be equal to |privilege_name|.
const DWORD desired_size = static_cast<DWORD>(wcslen(privilege_name));
const DWORD buffer_size = desired_size + 1;
std::unique_ptr<wchar_t[]> name_buffer(new wchar_t[buffer_size]);
for (int i = privileges->PrivilegeCount - 1; i >= 0 ; --i) {
LUID_AND_ATTRIBUTES& luid_and_att = privileges->Privileges[i];
DWORD size = buffer_size;
::LookupPrivilegeName(NULL, &luid_and_att.Luid, name_buffer.get(), &size);
if (size == desired_size &&
wcscmp(name_buffer.get(), privilege_name) == 0) {
return luid_and_att.Attributes == SE_PRIVILEGE_ENABLED;
}
}
return false;
}
} // namespace
// Test that we are parsing Chrome version correctly.
TEST(SetupUtilTest, GetMaxVersionFromArchiveDirTest) {
// Create a version dir
base::ScopedTempDir test_dir;
ASSERT_TRUE(test_dir.CreateUniqueTempDir());
base::FilePath chrome_dir = test_dir.GetPath().AppendASCII("1.0.0.0");
base::CreateDirectory(chrome_dir);
ASSERT_TRUE(base::PathExists(chrome_dir));
std::unique_ptr<base::Version> version(
installer::GetMaxVersionFromArchiveDir(test_dir.GetPath()));
ASSERT_EQ(version->GetString(), "1.0.0.0");
base::DeleteFile(chrome_dir, true);
ASSERT_FALSE(base::PathExists(chrome_dir)) << chrome_dir.value();
ASSERT_TRUE(installer::GetMaxVersionFromArchiveDir(test_dir.GetPath()) ==
NULL);
chrome_dir = test_dir.GetPath().AppendASCII("ABC");
base::CreateDirectory(chrome_dir);
ASSERT_TRUE(base::PathExists(chrome_dir));
ASSERT_TRUE(installer::GetMaxVersionFromArchiveDir(test_dir.GetPath()) ==
NULL);
chrome_dir = test_dir.GetPath().AppendASCII("2.3.4.5");
base::CreateDirectory(chrome_dir);
ASSERT_TRUE(base::PathExists(chrome_dir));
version.reset(installer::GetMaxVersionFromArchiveDir(test_dir.GetPath()));
ASSERT_EQ(version->GetString(), "2.3.4.5");
// Create multiple version dirs, ensure that we select the greatest.
chrome_dir = test_dir.GetPath().AppendASCII("9.9.9.9");
base::CreateDirectory(chrome_dir);
ASSERT_TRUE(base::PathExists(chrome_dir));
chrome_dir = test_dir.GetPath().AppendASCII("1.1.1.1");
base::CreateDirectory(chrome_dir);
ASSERT_TRUE(base::PathExists(chrome_dir));
version.reset(installer::GetMaxVersionFromArchiveDir(test_dir.GetPath()));
ASSERT_EQ(version->GetString(), "9.9.9.9");
}
TEST(SetupUtilTest, DeleteFileFromTempProcess) {
base::ScopedTempDir test_dir;
ASSERT_TRUE(test_dir.CreateUniqueTempDir());
base::FilePath test_file;
base::CreateTemporaryFileInDir(test_dir.GetPath(), &test_file);
ASSERT_TRUE(base::PathExists(test_file));
base::WriteFile(test_file, "foo", 3);
EXPECT_TRUE(installer::DeleteFileFromTempProcess(test_file, 0));
base::PlatformThread::Sleep(TestTimeouts::tiny_timeout() * 3);
EXPECT_FALSE(base::PathExists(test_file)) << test_file.value();
}
// Note: This test is only valid when run at high integrity (i.e. it will fail
// at medium integrity).
TEST(SetupUtilTest, ScopedTokenPrivilegeBasic) {
ASSERT_FALSE(CurrentProcessHasPrivilege(kTestedPrivilege));
if (!::IsUserAnAdmin()) {
LOG(WARNING) << "Skipping SetupUtilTest.ScopedTokenPrivilegeBasic due to "
"not running as admin.";
return;
}
{
installer::ScopedTokenPrivilege test_scoped_privilege(kTestedPrivilege);
ASSERT_TRUE(test_scoped_privilege.is_enabled());
ASSERT_TRUE(CurrentProcessHasPrivilege(kTestedPrivilege));
}
ASSERT_FALSE(CurrentProcessHasPrivilege(kTestedPrivilege));
}
// Note: This test is only valid when run at high integrity (i.e. it will fail
// at medium integrity).
TEST(SetupUtilTest, ScopedTokenPrivilegeAlreadyEnabled) {
ASSERT_FALSE(CurrentProcessHasPrivilege(kTestedPrivilege));
if (!::IsUserAnAdmin()) {
LOG(WARNING) << "Skipping SetupUtilTest.ScopedTokenPrivilegeAlreadyEnabled "
"due to not running as admin.";
return;
}
{
installer::ScopedTokenPrivilege test_scoped_privilege(kTestedPrivilege);
ASSERT_TRUE(test_scoped_privilege.is_enabled());
ASSERT_TRUE(CurrentProcessHasPrivilege(kTestedPrivilege));
{
installer::ScopedTokenPrivilege dup_scoped_privilege(kTestedPrivilege);
ASSERT_TRUE(dup_scoped_privilege.is_enabled());
ASSERT_TRUE(CurrentProcessHasPrivilege(kTestedPrivilege));
}
ASSERT_TRUE(CurrentProcessHasPrivilege(kTestedPrivilege));
}
ASSERT_FALSE(CurrentProcessHasPrivilege(kTestedPrivilege));
}
TEST(SetupUtilTest, GuidToSquid) {
ASSERT_EQ(installer::GuidToSquid(L"EDA620E3-AA98-3846-B81E-3493CB2E0E02"),
L"3E026ADE89AA64838BE14339BCE2E020");
}
TEST(SetupUtilTest, RegisterEventLogProvider) {
registry_util::RegistryOverrideManager registry_override_manager;
registry_override_manager.OverrideRegistry(HKEY_LOCAL_MACHINE);
const base::Version version("1.2.3.4");
const base::FilePath install_directory(
FILE_PATH_LITERAL("c:\\some_path\\test"));
installer::RegisterEventLogProvider(install_directory, version);
// TODO(grt): use install_static::InstallDetails::Get().install_full_name()
// when InstallDetails is initialized in the installer.
base::string16 reg_path(
L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\");
#if defined(GOOGLE_CHROME_BUILD)
reg_path.append(L"Chrome");
if (InstallUtil::IsChromeSxSProcess())
reg_path.append(L" SxS");
#else
reg_path.append(L"Chromium");
#endif
base::win::RegKey key;
ASSERT_EQ(ERROR_SUCCESS,
key.Open(HKEY_LOCAL_MACHINE, reg_path.c_str(), KEY_READ));
EXPECT_TRUE(key.HasValue(L"CategoryCount"));
EXPECT_TRUE(key.HasValue(L"TypesSupported"));
EXPECT_TRUE(key.HasValue(L"CategoryMessageFile"));
EXPECT_TRUE(key.HasValue(L"EventMessageFile"));
EXPECT_TRUE(key.HasValue(L"ParameterMessageFile"));
base::string16 value;
EXPECT_EQ(ERROR_SUCCESS, key.ReadValue(L"CategoryMessageFile", &value));
const base::FilePath expected_directory(
install_directory.AppendASCII(version.GetString()));
const base::FilePath provider_path(value);
EXPECT_EQ(expected_directory, provider_path.DirName());
key.Close();
installer::DeRegisterEventLogProvider();
EXPECT_NE(ERROR_SUCCESS,
key.Open(HKEY_LOCAL_MACHINE, reg_path.c_str(), KEY_READ));
}
const char kAdjustProcessPriority[] = "adjust-process-priority";
PriorityClassChangeResult DoProcessPriorityAdjustment() {
return installer::AdjustProcessPriority() ? PCCR_CHANGED : PCCR_UNCHANGED;
}
namespace {
// A scoper that sets/resets the current process's priority class.
class ScopedPriorityClass {
public:
// Applies |priority_class|, returning an instance if a change was made.
// Otherwise, returns an empty scoped_ptr.
static std::unique_ptr<ScopedPriorityClass> Create(DWORD priority_class);
~ScopedPriorityClass();
private:
explicit ScopedPriorityClass(DWORD original_priority_class);
DWORD original_priority_class_;
DISALLOW_COPY_AND_ASSIGN(ScopedPriorityClass);
};
std::unique_ptr<ScopedPriorityClass> ScopedPriorityClass::Create(
DWORD priority_class) {
HANDLE this_process = ::GetCurrentProcess();
DWORD original_priority_class = ::GetPriorityClass(this_process);
EXPECT_NE(0U, original_priority_class);
if (original_priority_class && original_priority_class != priority_class) {
BOOL result = ::SetPriorityClass(this_process, priority_class);
EXPECT_NE(FALSE, result);
if (result) {
return std::unique_ptr<ScopedPriorityClass>(
new ScopedPriorityClass(original_priority_class));
}
}
return std::unique_ptr<ScopedPriorityClass>();
}
ScopedPriorityClass::ScopedPriorityClass(DWORD original_priority_class)
: original_priority_class_(original_priority_class) {}
ScopedPriorityClass::~ScopedPriorityClass() {
BOOL result = ::SetPriorityClass(::GetCurrentProcess(),
original_priority_class_);
EXPECT_NE(FALSE, result);
}
PriorityClassChangeResult RelaunchAndDoProcessPriorityAdjustment() {
base::CommandLine cmd_line(*base::CommandLine::ForCurrentProcess());
cmd_line.AppendSwitch(kAdjustProcessPriority);
base::Process process = base::LaunchProcess(cmd_line, base::LaunchOptions());
int exit_code = 0;
if (!process.IsValid()) {
ADD_FAILURE() << " to launch subprocess.";
} else if (!process.WaitForExit(&exit_code)) {
ADD_FAILURE() << " to wait for subprocess to exit.";
} else {
return static_cast<PriorityClassChangeResult>(exit_code);
}
return PCCR_UNKNOWN;
}
} // namespace
// Launching a subprocess at normal priority class is a noop.
TEST(SetupUtilTest, AdjustFromNormalPriority) {
ASSERT_EQ(static_cast<DWORD>(NORMAL_PRIORITY_CLASS),
::GetPriorityClass(::GetCurrentProcess()));
EXPECT_EQ(PCCR_UNCHANGED, RelaunchAndDoProcessPriorityAdjustment());
}
// Launching a subprocess below normal priority class drops it to bg mode for
// sufficiently recent operating systems.
TEST(SetupUtilTest, AdjustFromBelowNormalPriority) {
std::unique_ptr<ScopedPriorityClass> below_normal =
ScopedPriorityClass::Create(BELOW_NORMAL_PRIORITY_CLASS);
ASSERT_TRUE(below_normal);
if (base::win::GetVersion() > base::win::VERSION_SERVER_2003)
EXPECT_EQ(PCCR_CHANGED, RelaunchAndDoProcessPriorityAdjustment());
else
EXPECT_EQ(PCCR_UNCHANGED, RelaunchAndDoProcessPriorityAdjustment());
}
TEST(SetupUtilTest, RecordUnPackMetricsTest) {
base::HistogramTester histogram_tester;
std::string unpack_status_metrics_name =
std::string(installer::kUnPackStatusMetricsName) + "_SetupExePatch";
std::string ntstatus_metrics_name =
std::string(installer::kUnPackNTSTATUSMetricsName) + "_SetupExePatch";
histogram_tester.ExpectTotalCount(unpack_status_metrics_name, 0);
RecordUnPackMetrics(UnPackStatus::UNPACK_NO_ERROR, 0,
installer::UnPackConsumer::SETUP_EXE_PATCH);
histogram_tester.ExpectTotalCount(unpack_status_metrics_name, 1);
histogram_tester.ExpectBucketCount(unpack_status_metrics_name, 0, 1);
histogram_tester.ExpectTotalCount(ntstatus_metrics_name, 1);
histogram_tester.ExpectBucketCount(ntstatus_metrics_name, 0, 1);
RecordUnPackMetrics(UnPackStatus::UNPACK_CLOSE_FILE_ERROR, 1,
installer::UnPackConsumer::SETUP_EXE_PATCH);
histogram_tester.ExpectTotalCount(unpack_status_metrics_name, 2);
histogram_tester.ExpectBucketCount(unpack_status_metrics_name, 10, 1);
histogram_tester.ExpectTotalCount(ntstatus_metrics_name, 2);
histogram_tester.ExpectBucketCount(ntstatus_metrics_name, 1, 1);
}
namespace {
// A test fixture that configures an InstallationState and an InstallerState
// with a product being updated.
class FindArchiveToPatchTest : public testing::Test {
protected:
class FakeInstallationState : public installer::InstallationState {
};
class FakeProductState : public installer::ProductState {
public:
static FakeProductState* FromProductState(const ProductState* product) {
return static_cast<FakeProductState*>(const_cast<ProductState*>(product));
}
void set_version(const base::Version& version) {
if (version.IsValid())
version_.reset(new base::Version(version));
else
version_.reset();
}
void set_uninstall_command(const base::CommandLine& uninstall_command) {
uninstall_command_ = uninstall_command;
}
};
FindArchiveToPatchTest() {}
void SetUp() override {
ASSERT_TRUE(test_dir_.CreateUniqueTempDir());
registry_override_manager_.OverrideRegistry(HKEY_CURRENT_USER);
registry_override_manager_.OverrideRegistry(HKEY_LOCAL_MACHINE);
product_version_ = base::Version("30.0.1559.0");
max_version_ = base::Version("47.0.1559.0");
// Install the product according to the version.
original_state_.reset(new FakeInstallationState());
InstallProduct();
// Prepare to update the product in the temp dir.
installer_state_.reset(new installer::InstallerState(
kSystemInstall_ ? installer::InstallerState::SYSTEM_LEVEL :
installer::InstallerState::USER_LEVEL));
installer_state_->AddProductFromState(
*original_state_->GetProductState(kSystemInstall_));
// Create archives in the two version dirs.
ASSERT_TRUE(
base::CreateDirectory(GetProductVersionArchivePath().DirName()));
ASSERT_EQ(1, base::WriteFile(GetProductVersionArchivePath(), "a", 1));
ASSERT_TRUE(
base::CreateDirectory(GetMaxVersionArchivePath().DirName()));
ASSERT_EQ(1, base::WriteFile(GetMaxVersionArchivePath(), "b", 1));
}
void TearDown() override {
original_state_.reset();
}
base::FilePath GetArchivePath(const base::Version& version) const {
return test_dir_.GetPath()
.AppendASCII(version.GetString())
.Append(installer::kInstallerDir)
.Append(installer::kChromeArchive);
}
base::FilePath GetMaxVersionArchivePath() const {
return GetArchivePath(max_version_);
}
base::FilePath GetProductVersionArchivePath() const {
return GetArchivePath(product_version_);
}
void InstallProduct() {
FakeProductState* product = FakeProductState::FromProductState(
original_state_->GetNonVersionedProductState(kSystemInstall_));
product->set_version(product_version_);
base::CommandLine uninstall_command(
test_dir_.GetPath()
.AppendASCII(product_version_.GetString())
.Append(installer::kInstallerDir)
.Append(installer::kSetupExe));
uninstall_command.AppendSwitch(installer::switches::kUninstall);
product->set_uninstall_command(uninstall_command);
}
void UninstallProduct() {
FakeProductState::FromProductState(
original_state_->GetNonVersionedProductState(kSystemInstall_))
->set_version(base::Version());
}
static const bool kSystemInstall_;
base::ScopedTempDir test_dir_;
base::Version product_version_;
base::Version max_version_;
std::unique_ptr<FakeInstallationState> original_state_;
std::unique_ptr<installer::InstallerState> installer_state_;
private:
registry_util::RegistryOverrideManager registry_override_manager_;
DISALLOW_COPY_AND_ASSIGN(FindArchiveToPatchTest);
};
const bool FindArchiveToPatchTest::kSystemInstall_ = false;
} // namespace
// Test that the path to the advertised product version is found.
TEST_F(FindArchiveToPatchTest, ProductVersionFound) {
base::FilePath patch_source(installer::FindArchiveToPatch(
*original_state_, *installer_state_, base::Version()));
EXPECT_EQ(GetProductVersionArchivePath().value(), patch_source.value());
}
// Test that the path to the max version is found if the advertised version is
// missing.
TEST_F(FindArchiveToPatchTest, MaxVersionFound) {
// The patch file is absent.
ASSERT_TRUE(base::DeleteFile(GetProductVersionArchivePath(), false));
base::FilePath patch_source(installer::FindArchiveToPatch(
*original_state_, *installer_state_, base::Version()));
EXPECT_EQ(GetMaxVersionArchivePath().value(), patch_source.value());
// The product doesn't appear to be installed, so the max version is found.
UninstallProduct();
patch_source = installer::FindArchiveToPatch(
*original_state_, *installer_state_, base::Version());
EXPECT_EQ(GetMaxVersionArchivePath().value(), patch_source.value());
}
// Test that an empty path is returned if no version is found.
TEST_F(FindArchiveToPatchTest, NoVersionFound) {
// The product doesn't appear to be installed and no archives are present.
UninstallProduct();
ASSERT_TRUE(base::DeleteFile(GetProductVersionArchivePath(), false));
ASSERT_TRUE(base::DeleteFile(GetMaxVersionArchivePath(), false));
base::FilePath patch_source(installer::FindArchiveToPatch(
*original_state_, *installer_state_, base::Version()));
EXPECT_EQ(base::FilePath::StringType(), patch_source.value());
}
TEST_F(FindArchiveToPatchTest, DesiredVersionFound) {
base::FilePath patch_source1(installer::FindArchiveToPatch(
*original_state_, *installer_state_, product_version_));
EXPECT_EQ(GetProductVersionArchivePath().value(), patch_source1.value());
base::FilePath patch_source2(installer::FindArchiveToPatch(
*original_state_, *installer_state_, max_version_));
EXPECT_EQ(GetMaxVersionArchivePath().value(), patch_source2.value());
}
TEST_F(FindArchiveToPatchTest, DesiredVersionNotFound) {
base::FilePath patch_source(installer::FindArchiveToPatch(
*original_state_, *installer_state_, base::Version("1.2.3.4")));
EXPECT_EQ(base::FilePath().value(), patch_source.value());
}
TEST(SetupUtilTest, ContainsUnsupportedSwitch) {
EXPECT_FALSE(installer::ContainsUnsupportedSwitch(
base::CommandLine::FromString(L"foo.exe")));
EXPECT_FALSE(installer::ContainsUnsupportedSwitch(
base::CommandLine::FromString(L"foo.exe --multi-install --chrome")));
EXPECT_TRUE(installer::ContainsUnsupportedSwitch(
base::CommandLine::FromString(L"foo.exe --chrome-frame")));
}
TEST(SetupUtilTest, GetRegistrationDataCommandKey) {
base::string16 app_guid = L"{AAAAAAAA-BBBB-1111-0123-456789ABCDEF}";
UpdatingAppRegistrationData reg_data(app_guid);
base::string16 key =
installer::GetRegistrationDataCommandKey(reg_data, L"test_name");
EXPECT_TRUE(base::EndsWith(key, app_guid + L"\\Commands\\test_name",
base::CompareCase::SENSITIVE));
}
namespace installer {
class DeleteRegistryKeyPartialTest : public ::testing::Test {
protected:
using RegKey = base::win::RegKey;
void SetUp() override {
_registry_override_manager.OverrideRegistry(root_);
to_preserve_.push_back(L"preSERve1");
to_preserve_.push_back(L"1evRESerp");
}
void CreateSubKeys(bool with_preserves) {
ASSERT_FALSE(RegKey(root_, path_.c_str(), KEY_READ).Valid());
// These subkeys are added such that 1) keys to preserve are intermixed with
// other keys, and 2) the case of the keys to preserve doesn't match the
// values in |to_preserve_|.
ASSERT_EQ(ERROR_SUCCESS, RegKey(root_, path_.c_str(), KEY_WRITE)
.CreateKey(L"0sub", KEY_WRITE));
if (with_preserves) {
ASSERT_EQ(ERROR_SUCCESS, RegKey(root_, path_.c_str(), KEY_WRITE)
.CreateKey(L"1evreserp", KEY_WRITE));
}
ASSERT_EQ(ERROR_SUCCESS, RegKey(root_, path_.c_str(), KEY_WRITE)
.CreateKey(L"asub", KEY_WRITE));
if (with_preserves) {
ASSERT_EQ(ERROR_SUCCESS, RegKey(root_, path_.c_str(), KEY_WRITE)
.CreateKey(L"preserve1", KEY_WRITE));
}
ASSERT_EQ(ERROR_SUCCESS, RegKey(root_, path_.c_str(), KEY_WRITE)
.CreateKey(L"sub1", KEY_WRITE));
}
const HKEY root_ = HKEY_CURRENT_USER;
base::string16 path_ = L"key_path";
std::vector<base::string16> to_preserve_;
private:
registry_util::RegistryOverrideManager _registry_override_manager;
};
TEST_F(DeleteRegistryKeyPartialTest, NoKey) {
DeleteRegistryKeyPartial(root_, L"does_not_exist",
std::vector<base::string16>());
DeleteRegistryKeyPartial(root_, L"does_not_exist", to_preserve_);
}
TEST_F(DeleteRegistryKeyPartialTest, EmptyKey) {
ASSERT_FALSE(RegKey(root_, path_.c_str(), KEY_READ).Valid());
ASSERT_TRUE(RegKey(root_, path_.c_str(), KEY_WRITE).Valid());
DeleteRegistryKeyPartial(root_, path_.c_str(), std::vector<base::string16>());
ASSERT_FALSE(RegKey(root_, path_.c_str(), KEY_READ).Valid());
ASSERT_TRUE(RegKey(root_, path_.c_str(), KEY_WRITE).Valid());
DeleteRegistryKeyPartial(root_, path_.c_str(), to_preserve_);
ASSERT_FALSE(RegKey(root_, path_.c_str(), KEY_READ).Valid());
}
TEST_F(DeleteRegistryKeyPartialTest, NonEmptyKey) {
CreateSubKeys(false); /* !with_preserves */
DeleteRegistryKeyPartial(root_, path_.c_str(), std::vector<base::string16>());
ASSERT_FALSE(RegKey(root_, path_.c_str(), KEY_READ).Valid());
CreateSubKeys(false); /* !with_preserves */
ASSERT_TRUE(RegKey(root_, path_.c_str(), KEY_WRITE).Valid());
DeleteRegistryKeyPartial(root_, path_.c_str(), to_preserve_);
ASSERT_FALSE(RegKey(root_, path_.c_str(), KEY_READ).Valid());
}
TEST_F(DeleteRegistryKeyPartialTest, NonEmptyKeyWithPreserve) {
CreateSubKeys(true); /* with_preserves */
// Put some values into the main key.
{
RegKey key(root_, path_.c_str(), KEY_SET_VALUE);
ASSERT_TRUE(key.Valid());
ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(nullptr, 5U));
ASSERT_EQ(1u, base::win::RegistryValueIterator(root_, path_.c_str())
.ValueCount());
ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(L"foo", L"bar"));
ASSERT_EQ(2u, base::win::RegistryValueIterator(root_, path_.c_str())
.ValueCount());
ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(L"baz", L"huh"));
ASSERT_EQ(3u, base::win::RegistryValueIterator(root_, path_.c_str())
.ValueCount());
}
ASSERT_TRUE(RegKey(root_, path_.c_str(), KEY_WRITE).Valid());
DeleteRegistryKeyPartial(root_, path_.c_str(), to_preserve_);
ASSERT_TRUE(RegKey(root_, path_.c_str(), KEY_READ).Valid());
// Ensure that the preserved subkeys are still present.
{
base::win::RegistryKeyIterator it(root_, path_.c_str());
ASSERT_EQ(to_preserve_.size(), it.SubkeyCount());
for (; it.Valid(); ++it) {
ASSERT_NE(to_preserve_.end(),
std::find_if(to_preserve_.begin(), to_preserve_.end(),
[&it](const base::string16& key_name) {
return base::ToLowerASCII(it.Name()) ==
base::ToLowerASCII(key_name);
}))
<< it.Name();
}
}
// Ensure that all values are absent.
{
base::win::RegistryValueIterator it(root_, path_.c_str());
ASSERT_EQ(0u, it.ValueCount());
}
}
class LegacyCleanupsTest : public ::testing::Test {
protected:
LegacyCleanupsTest() = default;
void SetUp() override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
registry_override_manager_.OverrideRegistry(HKEY_CURRENT_USER);
installer_state_ =
base::MakeUnique<FakeInstallerState>(temp_dir_.GetPath());
// Create the state to be cleared.
ASSERT_TRUE(base::win::RegKey(HKEY_CURRENT_USER, kBinariesClientsKeyPath,
KEY_WRITE | KEY_WOW64_32KEY)
.Valid());
#if defined(GOOGLE_CHROME_BUILD)
ASSERT_TRUE(base::win::RegKey(HKEY_CURRENT_USER, kGCFClientsKeyPath,
KEY_WRITE | KEY_WOW64_32KEY)
.Valid());
ASSERT_TRUE(base::win::RegKey(HKEY_CURRENT_USER, kAppLauncherClientsKeyPath,
KEY_WRITE | KEY_WOW64_32KEY)
.Valid());
ASSERT_GT(base::WriteFile(GetAppHostExePath(), "cha", 3), 0);
ASSERT_TRUE(
base::win::RegKey(HKEY_CURRENT_USER,
GetChromeAppCommandPath(L"install-extension").c_str(),
KEY_WRITE | KEY_WOW64_32KEY)
.Valid());
#endif // GOOGLE_CHROME_BUILD
}
const InstallerState& installer_state() const { return *installer_state_; }
bool HasBinariesVersionKey() const {
return base::win::RegKey(HKEY_CURRENT_USER, kBinariesClientsKeyPath,
KEY_QUERY_VALUE | KEY_WOW64_32KEY)
.Valid();
}
#if defined(GOOGLE_CHROME_BUILD)
bool HasMultiGCFVersionKey() const {
return base::win::RegKey(HKEY_CURRENT_USER, kGCFClientsKeyPath,
KEY_QUERY_VALUE | KEY_WOW64_32KEY)
.Valid();
}
bool HasAppLauncherVersionKey() const {
return base::win::RegKey(HKEY_CURRENT_USER, kAppLauncherClientsKeyPath,
KEY_QUERY_VALUE | KEY_WOW64_32KEY)
.Valid();
}
bool HasAppHostExe() const { return base::PathExists(GetAppHostExePath()); }
bool HasInstallExtensionCommand() const {
return base::win::RegKey(
HKEY_CURRENT_USER,
GetChromeAppCommandPath(L"install-extension").c_str(),
KEY_QUERY_VALUE | KEY_WOW64_32KEY)
.Valid();
}
#endif // GOOGLE_CHROME_BUILD
private:
// An InstallerState for a per-user install of Chrome in a given directory.
class FakeInstallerState : public InstallerState {
public:
explicit FakeInstallerState(const base::FilePath& target_path) {
BrowserDistribution* dist = BrowserDistribution::GetDistribution();
operation_ = InstallerState::SINGLE_INSTALL_OR_UPDATE;
target_path_ = target_path;
state_key_ = dist->GetStateKey();
product_ = base::MakeUnique<Product>(dist);
level_ = InstallerState::USER_LEVEL;
root_key_ = HKEY_CURRENT_USER;
}
};
#if defined(GOOGLE_CHROME_BUILD)
base::FilePath GetAppHostExePath() const {
return installer_state_->target_path().AppendASCII("app_host.exe");
}
base::string16 GetChromeAppCommandPath(const wchar_t* command) const {
return base::string16(
L"SOFTWARE\\Google\\Update\\Clients\\"
L"{8A69D345-D564-463c-AFF1-A69D9E530F96}\\Commands\\") +
command;
}
#endif // GOOGLE_CHROME_BUILD
static const wchar_t kBinariesClientsKeyPath[];
#if defined(GOOGLE_CHROME_BUILD)
static const wchar_t kGCFClientsKeyPath[];
static const wchar_t kAppLauncherClientsKeyPath[];
#endif
base::ScopedTempDir temp_dir_;
registry_util::RegistryOverrideManager registry_override_manager_;
std::unique_ptr<FakeInstallerState> installer_state_;
DISALLOW_COPY_AND_ASSIGN(LegacyCleanupsTest);
};
#if defined(GOOGLE_CHROME_BUILD)
const wchar_t LegacyCleanupsTest::kBinariesClientsKeyPath[] =
L"SOFTWARE\\Google\\Update\\Clients\\"
L"{4DC8B4CA-1BDA-483e-B5FA-D3C12E15B62D}";
const wchar_t LegacyCleanupsTest::kGCFClientsKeyPath[] =
L"SOFTWARE\\Google\\Update\\Clients\\"
L"{8BA986DA-5100-405E-AA35-86F34A02ACBF}";
const wchar_t LegacyCleanupsTest::kAppLauncherClientsKeyPath[] =
L"SOFTWARE\\Google\\Update\\Clients\\"
L"{FDA71E6F-AC4C-4a00-8B70-9958A68906BF}";
#else // GOOGLE_CHROME_BUILD
const wchar_t LegacyCleanupsTest::kBinariesClientsKeyPath[] =
L"SOFTWARE\\Chromium Binaries";
#endif // !GOOGLE_CHROME_BUILD
TEST_F(LegacyCleanupsTest, NoOpOnFailedUpdate) {
DoLegacyCleanups(installer_state(), INSTALL_FAILED);
EXPECT_TRUE(HasBinariesVersionKey());
#if defined(GOOGLE_CHROME_BUILD)
EXPECT_TRUE(HasMultiGCFVersionKey());
EXPECT_TRUE(HasAppLauncherVersionKey());
EXPECT_TRUE(HasAppHostExe());
EXPECT_TRUE(HasInstallExtensionCommand());
#endif // GOOGLE_CHROME_BUILD
}
TEST_F(LegacyCleanupsTest, Do) {
DoLegacyCleanups(installer_state(), NEW_VERSION_UPDATED);
EXPECT_FALSE(HasBinariesVersionKey());
#if defined(GOOGLE_CHROME_BUILD)
EXPECT_FALSE(HasMultiGCFVersionKey());
EXPECT_FALSE(HasAppLauncherVersionKey());
EXPECT_FALSE(HasAppHostExe());
EXPECT_FALSE(HasInstallExtensionCommand());
#endif // GOOGLE_CHROME_BUILD
}
} // namespace installer