diff options
author | grt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-30 19:07:35 +0000 |
---|---|---|
committer | grt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-30 19:07:35 +0000 |
commit | 08ded4d4482f1f8e11040cc7116230626ae52e7e (patch) | |
tree | 4cb66596cb4a06dbe06fe2393cf90a5ad9815b60 /chrome/installer | |
parent | c6d9422188208e9034891c3cb46d37171e33208c (diff) | |
download | chromium_src-08ded4d4482f1f8e11040cc7116230626ae52e7e.zip chromium_src-08ded4d4482f1f8e11040cc7116230626ae52e7e.tar.gz chromium_src-08ded4d4482f1f8e11040cc7116230626ae52e7e.tar.bz2 |
On uninstall, remove the registry keys that Chrome lays down to make itself the default browser on pre-Vista systems (and/or for software running on Vista that checks legacy registry keys).
BUG=67355
TEST=install Chrome, making it the default browser. uninstall it and check the box to make Firefox or IE the new default. try following a link in some application which should result in a browser starting up. notice that the chosen default browser starts.
Review URL: http://codereview.chromium.org/6746006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@79875 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/installer')
-rw-r--r-- | chrome/installer/setup/setup_main.cc | 2 | ||||
-rw-r--r-- | chrome/installer/setup/setup_util.cc | 86 | ||||
-rw-r--r-- | chrome/installer/setup/setup_util.h | 31 | ||||
-rw-r--r-- | chrome/installer/setup/setup_util_unittest.cc | 46 | ||||
-rw-r--r-- | chrome/installer/setup/uninstall.cc | 67 | ||||
-rw-r--r-- | chrome/installer/setup/uninstall.h | 15 | ||||
-rw-r--r-- | chrome/installer/util/install_util.cc | 50 | ||||
-rw-r--r-- | chrome/installer/util/install_util.h | 42 | ||||
-rw-r--r-- | chrome/installer/util/install_util_unittest.cc | 181 |
9 files changed, 490 insertions, 30 deletions
diff --git a/chrome/installer/setup/setup_main.cc b/chrome/installer/setup/setup_main.cc index 00188d4..65d8a3ee 100644 --- a/chrome/installer/setup/setup_main.cc +++ b/chrome/installer/setup/setup_main.cc @@ -875,7 +875,7 @@ bool HandleNonInstallCmdLineOptions(const InstallationState& original_state, DCHECK(chrome_install); if (chrome_install) { installer::DeleteChromeRegistrationKeys(chrome_install->distribution(), - HKEY_LOCAL_MACHINE, suffix, tmp); + HKEY_LOCAL_MACHINE, suffix, installer_state->target_path(), &tmp); } *exit_code = tmp; } else if (cmd_line.HasSwitch(installer::switches::kInactiveUserToast)) { diff --git a/chrome/installer/setup/setup_util.cc b/chrome/installer/setup/setup_util.cc index 9395c28..dbe895c 100644 --- a/chrome/installer/setup/setup_util.cc +++ b/chrome/installer/setup/setup_util.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. // @@ -14,10 +14,12 @@ #include "courgette/courgette.h" #include "third_party/bspatch/mbspatch.h" -int installer::ApplyDiffPatch(const FilePath& src, - const FilePath& patch, - const FilePath& dest, - const InstallerState* installer_state) { +namespace installer { + +int ApplyDiffPatch(const FilePath& src, + const FilePath& patch, + const FilePath& dest, + const InstallerState* installer_state) { VLOG(1) << "Applying patch " << patch.value() << " to file " << src.value() << " and generating file " << dest.value(); @@ -42,7 +44,7 @@ int installer::ApplyDiffPatch(const FilePath& src, dest.value().c_str()); } -Version* installer::GetMaxVersionFromArchiveDir(const FilePath& chrome_path) { +Version* GetMaxVersionFromArchiveDir(const FilePath& chrome_path) { VLOG(1) << "Looking for Chrome version folder under " << chrome_path.value(); Version* version = NULL; file_util::FileEnumerator version_enum(chrome_path, false, @@ -69,3 +71,75 @@ Version* installer::GetMaxVersionFromArchiveDir(const FilePath& chrome_path) { return (version_found ? max_version.release() : NULL); } + +// Open |path| with minimal access to obtain information about it, returning +// true and populating |handle| on success. +// static +bool ProgramCompare::OpenForInfo(const FilePath& path, + base::win::ScopedHandle* handle) { + DCHECK(handle); + handle->Set(base::CreatePlatformFile(path, base::PLATFORM_FILE_OPEN, NULL, + NULL)); + return handle->IsValid(); +} + +// Populate |info| for |handle|, returning true on success. +// static +bool ProgramCompare::GetInfo(const base::win::ScopedHandle& handle, + BY_HANDLE_FILE_INFORMATION* info) { + DCHECK(handle.IsValid()); + return GetFileInformationByHandle( + const_cast<base::win::ScopedHandle&>(handle), info) != 0; +} + +ProgramCompare::ProgramCompare(const FilePath& path_to_match) + : path_to_match_(path_to_match), + file_handle_(base::kInvalidPlatformFileValue), + file_info_() { + DCHECK(!path_to_match_.empty()); + if (!OpenForInfo(path_to_match_, &file_handle_)) { + PLOG(WARNING) << "Failed opening " << path_to_match_.value() + << "; falling back to path string comparisons."; + } else if (!GetInfo(file_handle_, &file_info_)) { + PLOG(WARNING) << "Failed getting information for " + << path_to_match_.value() + << "; falling back to path string comparisons."; + file_handle_.Close(); + } +} + +ProgramCompare::~ProgramCompare() { +} + +bool ProgramCompare::Evaluate(const std::wstring& value) const { + // Suss out the exe portion of the value, which is expected to be a command + // line kinda (or exactly) like: + // "c:\foo\bar\chrome.exe" -- "%1" + FilePath program(CommandLine::FromString(value).GetProgram()); + if (program.empty()) { + LOG(WARNING) << "Failed to parse an executable name from command line: \"" + << value << "\""; + return false; + } + + // Try the simple thing first: do the paths happen to match? + if (FilePath::CompareEqualIgnoreCase(path_to_match_.value(), program.value())) + return true; + + // If the paths don't match and we couldn't open the expected file, we've done + // our best. + if (!file_handle_.IsValid()) + return false; + + // Open the program and see if it references the expected file. + base::win::ScopedHandle handle; + BY_HANDLE_FILE_INFORMATION info = {}; + + return (OpenForInfo(program, &handle) && + GetInfo(handle, &info) && + info.dwVolumeSerialNumber == file_info_.dwVolumeSerialNumber && + info.nFileIndexHigh == file_info_.nFileIndexHigh && + info.nFileIndexLow == file_info_.nFileIndexLow); +} + +} // namespace installer diff --git a/chrome/installer/setup/setup_util.h b/chrome/installer/setup/setup_util.h index 267235f..d9dceae 100644 --- a/chrome/installer/setup/setup_util.h +++ b/chrome/installer/setup/setup_util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. // @@ -8,9 +8,10 @@ #define CHROME_INSTALLER_SETUP_SETUP_UTIL_H_ #pragma once +#include "base/file_path.h" #include "base/version.h" - -class FilePath; +#include "base/win/scoped_handle.h" +#include "chrome/installer/util/install_util.h" namespace installer { @@ -30,6 +31,30 @@ int ApplyDiffPatch(const FilePath& src, // Returns the maximum version found or NULL if no version is found. Version* GetMaxVersionFromArchiveDir(const FilePath& chrome_path); +// A predicate that compares the program portion of a command line with a given +// file path. First, the file paths are compared directly. If they do not +// match, the filesystem is consulted to determine if the paths reference the +// same file. +class ProgramCompare : public InstallUtil::RegistryValuePredicate { + public: + explicit ProgramCompare(const FilePath& path_to_match); + virtual ~ProgramCompare(); + virtual bool Evaluate(const std::wstring& value) const OVERRIDE; + + protected: + static bool OpenForInfo(const FilePath& path, + base::win::ScopedHandle* handle); + static bool GetInfo(const base::win::ScopedHandle& handle, + BY_HANDLE_FILE_INFORMATION* info); + + FilePath path_to_match_; + base::win::ScopedHandle file_handle_; + BY_HANDLE_FILE_INFORMATION file_info_; + + private: + DISALLOW_COPY_AND_ASSIGN(ProgramCompare); +}; // class ProgramCompare + } // namespace installer #endif // CHROME_INSTALLER_SETUP_SETUP_UTIL_H_ diff --git a/chrome/installer/setup/setup_util_unittest.cc b/chrome/installer/setup/setup_util_unittest.cc index 04694c5..8dacb9a 100644 --- a/chrome/installer/setup/setup_util_unittest.cc +++ b/chrome/installer/setup/setup_util_unittest.cc @@ -8,6 +8,7 @@ #include "base/file_util.h" #include "base/memory/scoped_temp_dir.h" #include "base/path_service.h" +#include "base/string_util.h" #include "chrome/common/chrome_paths.h" #include "chrome/installer/setup/setup_util.h" #include "chrome/installer/util/master_preferences.h" @@ -93,3 +94,48 @@ TEST_F(SetupUtilTest, GetMaxVersionFromArchiveDirTest) { version.reset(installer::GetMaxVersionFromArchiveDir(test_dir_.path())); ASSERT_EQ(version->GetString(), "9.9.9.9"); } + +TEST_F(SetupUtilTest, ProgramCompare) { + using installer::ProgramCompare; + ScopedTempDir temp_dir; + + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + + FilePath some_long_dir(temp_dir.path().Append(L"Some Long Directory Name")); + FilePath expect(some_long_dir.Append(L"file.txt")); + FilePath expect_upcase(some_long_dir.Append(L"FILE.txt")); + FilePath other(some_long_dir.Append(L"otherfile.txt")); + + // Tests where the expected file doesn't exist. + + // Paths don't match. + EXPECT_FALSE(ProgramCompare(expect).Evaluate(L"\"" + other.value() + L"\"")); + // Paths match exactly. + EXPECT_TRUE(ProgramCompare(expect).Evaluate(L"\"" + expect.value() + L"\"")); + // Paths differ by case. + EXPECT_TRUE(ProgramCompare(expect).Evaluate( + L"\"" + expect_upcase.value() + L"\"")); + + // Tests where the expected file exists. + static const char data[] = "data"; + ASSERT_TRUE(file_util::CreateDirectory(some_long_dir)); + ASSERT_NE(-1, file_util::WriteFile(expect, data, arraysize(data) - 1)); + // Paths don't match. + EXPECT_FALSE(ProgramCompare(expect).Evaluate(L"\"" + other.value() + L"\"")); + // Paths match exactly. + EXPECT_TRUE(ProgramCompare(expect).Evaluate(L"\"" + expect.value() + L"\"")); + // Paths differ by case. + EXPECT_TRUE(ProgramCompare(expect).Evaluate( + L"\"" + expect_upcase.value() + L"\"")); + + // Test where strings don't match, but the same file is indicated. + std::wstring short_expect; + DWORD short_len = GetShortPathName(expect.value().c_str(), + WriteInto(&short_expect, MAX_PATH), + MAX_PATH); + ASSERT_NE(static_cast<DWORD>(0), short_len); + ASSERT_GT(static_cast<DWORD>(MAX_PATH), short_len); + short_expect.resize(short_len); + ASSERT_FALSE(FilePath::CompareEqualIgnoreCase(expect.value(), short_expect)); + EXPECT_TRUE(ProgramCompare(expect).Evaluate(L"\"" + short_expect + L"\"")); +} diff --git a/chrome/installer/setup/uninstall.cc b/chrome/installer/setup/uninstall.cc index 5d4f129..e145a8c 100644 --- a/chrome/installer/setup/uninstall.cc +++ b/chrome/installer/setup/uninstall.cc @@ -19,6 +19,7 @@ #include "chrome/installer/setup/install.h" #include "chrome/installer/setup/install_worker.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/channel_info.h" #include "chrome/installer/util/delete_after_reboot_helper.h" @@ -485,7 +486,9 @@ bool ShouldDeleteProfile(const InstallerState& installer_state, bool DeleteChromeRegistrationKeys(BrowserDistribution* dist, HKEY root, const std::wstring& browser_entry_suffix, - InstallStatus& exit_code) { + const FilePath& target_path, + InstallStatus* exit_code) { + DCHECK(exit_code); if (!dist->CanSetAsDefault()) { // We should have never set those keys. return true; @@ -526,15 +529,50 @@ bool DeleteChromeRegistrationKeys(BrowserDistribution* dist, HKEY root, InstallUtil::DeleteRegistryKey(root, app_path_key); // Cleanup OpenWithList + std::wstring open_with_key; for (int i = 0; ShellUtil::kFileAssociations[i] != NULL; i++) { - std::wstring open_with_key(ShellUtil::kRegClasses); + open_with_key.assign(ShellUtil::kRegClasses); file_util::AppendToPath(&open_with_key, ShellUtil::kFileAssociations[i]); file_util::AppendToPath(&open_with_key, L"OpenWithList"); file_util::AppendToPath(&open_with_key, installer::kChromeExe); InstallUtil::DeleteRegistryKey(root, open_with_key); } - exit_code = installer::UNINSTALL_SUCCESSFUL; + // Cleanup in case Chrome had been made the default browser. + + // Delete the default value of SOFTWARE\Clients\StartMenuInternet if it + // references this Chrome. + InstallUtil::DeleteRegistryValueIf( + root, ShellUtil::kRegStartMenuInternet, L"", + InstallUtil::ValueEquals(dist->GetApplicationName() + + browser_entry_suffix)); + + // Delete each protocol association if it references this Chrome. + ProgramCompare open_command_pred(target_path.Append(kChromeExe)); + std::wstring parent_key(ShellUtil::kRegClasses); + const std::wstring::size_type base_length = parent_key.size(); + std::wstring child_key; + for (const wchar_t* const* proto = &ShellUtil::kProtocolAssociations[0]; + *proto != NULL; ++proto) { + parent_key.resize(base_length); + file_util::AppendToPath(&parent_key, *proto); + child_key.assign(parent_key).append(ShellUtil::kRegShellOpen); + InstallUtil::DeleteRegistryKeyIf(root, parent_key, child_key, L"", + open_command_pred); + } + + // Delete each filetype association if it references this Chrome. + InstallUtil::ValueEquals prog_id_pred(ShellUtil::kChromeHTMLProgId + + browser_entry_suffix); + for (const wchar_t* const* filetype = &ShellUtil::kFileAssociations[0]; + *filetype != NULL; ++filetype) { + parent_key.resize(base_length); + file_util::AppendToPath(&parent_key, *filetype); + InstallUtil::DeleteRegistryKeyIf(root, parent_key, parent_key, L"", + prog_id_pred); + } + + *exit_code = installer::UNINSTALL_SUCCESSFUL; return true; } @@ -670,8 +708,20 @@ InstallStatus UninstallProduct(const InstallationState& original_state, product.SetMsiMarker(installer_state.system_install(), false); // Remove all Chrome registration keys. + // Registration data is put in HKCU for both system level and user level + // installs. InstallStatus ret = installer::UNKNOWN_STATUS; - DeleteChromeRegistrationKeys(product.distribution(), reg_root, suffix, ret); + DeleteChromeRegistrationKeys(product.distribution(), HKEY_CURRENT_USER, + suffix, installer_state.target_path(), &ret); + + // Registration data is put in HKLM for system level and possibly user level + // installs (when Chrome is made the default browser at install-time). + if (installer_state.system_install() || remove_all && + (!suffix.empty() || CurrentUserHasDefaultBrowser(installer_state, + product))) { + DeleteChromeRegistrationKeys(product.distribution(), HKEY_LOCAL_MACHINE, + suffix, installer_state.target_path(), &ret); + } if (!is_chrome) { ProcessChromeFrameWorkItems(original_state, installer_state, setup_path, @@ -681,15 +731,6 @@ InstallStatus UninstallProduct(const InstallationState& original_state, if (installer_state.is_multi_install()) ProcessGoogleUpdateItems(original_state, installer_state, product); - // For user level install also we end up creating some keys in HKLM if user - // sets Chrome as default browser. So delete those as well (needs admin). - if (remove_all && !installer_state.system_install() && - (!suffix.empty() || CurrentUserHasDefaultBrowser(installer_state, - product))) { - DeleteChromeRegistrationKeys(product.distribution(), HKEY_LOCAL_MACHINE, - suffix, ret); - } - ProcessQuickEnableWorkItems(installer_state, original_state); // Get the state of the installed product (if any) diff --git a/chrome/installer/setup/uninstall.h b/chrome/installer/setup/uninstall.h index a3f1ec1..49fd352 100644 --- a/chrome/installer/setup/uninstall.h +++ b/chrome/installer/setup/uninstall.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. // @@ -12,14 +12,18 @@ #include <string> -#include "base/command_line.h" -#include "chrome/installer/util/product.h" #include "chrome/installer/util/util_constants.h" +class BrowserDistribution; +class CommandLine; +class FilePath; +class InstallUtil; + namespace installer { class InstallationState; class InstallerState; +class Product; // This function removes all Chrome registration related keys. It returns true // if successful, otherwise false. The error code is set in |exit_code|. @@ -27,7 +31,8 @@ class InstallerState; // suffix for default browser entry name in the registry (optional). bool DeleteChromeRegistrationKeys(BrowserDistribution* dist, HKEY root, const std::wstring& browser_entry_suffix, - InstallStatus& exit_code); + const FilePath& target_path, + InstallStatus* exit_code); // Removes any legacy registry keys from earlier versions of Chrome that are no // longer needed. This is used during autoupdate since we don't do full @@ -56,6 +61,6 @@ installer::InstallStatus UninstallProduct( bool force_uninstall, const CommandLine& cmd_line); -} // namespace installer_setup +} // namespace installer #endif // CHROME_INSTALLER_SETUP_UNINSTALL_H_ diff --git a/chrome/installer/util/install_util.cc b/chrome/installer/util/install_util.cc index 2e2f564..999d84a 100644 --- a/chrome/installer/util/install_util.cc +++ b/chrome/installer/util/install_util.cc @@ -294,6 +294,56 @@ bool InstallUtil::DeleteRegistryValue(HKEY reg_root, } // static +bool InstallUtil::DeleteRegistryKeyIf( + HKEY root_key, + const std::wstring& key_to_delete_path, + const std::wstring& key_to_test_path, + const wchar_t* value_name, + const RegistryValuePredicate& predicate) { + DCHECK(root_key); + DCHECK(value_name); + RegKey key; + std::wstring actual_value; + if (key.Open(root_key, key_to_test_path.c_str(), + KEY_QUERY_VALUE) == ERROR_SUCCESS && + key.ReadValue(value_name, &actual_value) == ERROR_SUCCESS && + predicate.Evaluate(actual_value)) { + key.Close(); + return DeleteRegistryKey(root_key, key_to_delete_path); + } + return true; +} + +// static +bool InstallUtil::DeleteRegistryValueIf( + HKEY root_key, + const wchar_t* key_path, + const wchar_t* value_name, + const RegistryValuePredicate& predicate) { + DCHECK(root_key); + DCHECK(key_path); + DCHECK(value_name); + RegKey key; + std::wstring actual_value; + if (key.Open(root_key, key_path, + KEY_QUERY_VALUE | KEY_SET_VALUE) == ERROR_SUCCESS && + key.ReadValue(value_name, &actual_value) == ERROR_SUCCESS && + predicate.Evaluate(actual_value)) { + LONG result = key.DeleteValue(value_name); + if (result != ERROR_SUCCESS) { + LOG(ERROR) << "Failed to delete registry value: " << value_name + << " error: " << result; + return false; + } + } + return true; +} + +bool InstallUtil::ValueEquals::Evaluate(const std::wstring& value) const { + return value == value_to_match_; +} + +// static int InstallUtil::GetInstallReturnCode(installer::InstallStatus status) { switch (status) { case installer::FIRST_INSTALL_SUCCESS: diff --git a/chrome/installer/util/install_util.h b/chrome/installer/util/install_util.h index 4b525b2..5592b40 100644 --- a/chrome/installer/util/install_util.h +++ b/chrome/installer/util/install_util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. // @@ -108,6 +108,46 @@ class InstallUtil { static bool DeleteRegistryValue(HKEY reg_root, const std::wstring& key_path, const std::wstring& value_name); + // An interface to a predicate function for use by DeleteRegistryKeyIf and + // DeleteRegistryValueIf. + class RegistryValuePredicate { + public: + virtual ~RegistryValuePredicate() { } + virtual bool Evaluate(const std::wstring& value) const = 0; + }; + + // Deletes the key |key_to_delete_path| under |root_key| iff the value + // |value_name| in the key |key_to_test_path| under |root_key| satisfies + // |predicate|. |value_name| must be an empty string to test the key's + // default value. Returns false if the value satisfied the predicate but + // could not be deleted, otherwise returns true. + static bool DeleteRegistryKeyIf(HKEY root_key, + const std::wstring& key_to_delete_path, + const std::wstring& key_to_test_path, + const wchar_t* value_name, + const RegistryValuePredicate& predicate); + + // Deletes the value |value_name| in the key |key_path| under |root_key| iff + // its current value satisfies |predicate|. |value_name| must be an empty + // string to test the key's default value. Returns false if the value + // satisfied the predicate but could not be deleted, otherwise returns true. + static bool DeleteRegistryValueIf(HKEY root_key, + const wchar_t* key_path, + const wchar_t* value_name, + const RegistryValuePredicate& predicate); + + // A predicate that performs a case-sensitive string comparison. + class ValueEquals : public RegistryValuePredicate { + public: + explicit ValueEquals(const std::wstring& value_to_match) + : value_to_match_(value_to_match) { } + virtual bool Evaluate(const std::wstring& value) const OVERRIDE; + protected: + std::wstring value_to_match_; + private: + DISALLOW_COPY_AND_ASSIGN(ValueEquals); + }; + // Returns zero on install success, or an InstallStatus value otherwise. static int GetInstallReturnCode(installer::InstallStatus install_status); diff --git a/chrome/installer/util/install_util_unittest.cc b/chrome/installer/util/install_util_unittest.cc index f67d689..f05a9dd 100644 --- a/chrome/installer/util/install_util_unittest.cc +++ b/chrome/installer/util/install_util_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -14,11 +14,20 @@ #include "chrome/installer/util/install_util.h" #include "chrome/installer/util/master_preferences.h" #include "chrome/installer/util/product_unittest.h" +#include "testing/gmock/include/gmock/gmock.h" using base::win::RegKey; using installer::InstallationState; using installer::InstallerState; using installer::MasterPreferences; +using ::testing::_; +using ::testing::Return; +using ::testing::StrEq; + +class MockRegistryValuePredicate : public InstallUtil::RegistryValuePredicate { + public: + MOCK_CONST_METHOD1(Evaluate, bool(const std::wstring&)); +}; class InstallUtilTest : public TestWithTempDirAndDeleteTempOverrideKeys { protected: @@ -172,3 +181,173 @@ TEST_F(InstallUtilTest, UpdateInstallerStage) { } TempRegKeyOverride::DeleteAllTempKeys(); } + +TEST_F(InstallUtilTest, DeleteRegistryKeyIf) { + const HKEY root = HKEY_CURRENT_USER; + std::wstring parent_key_path(L"SomeKey\\ToDelete"); + std::wstring child_key_path(parent_key_path); + child_key_path.append(L"\\ChildKey\\WithAValue"); + const wchar_t value_name[] = L"some_value_name"; + const wchar_t value[] = L"hi mom"; + + { + TempRegKeyOverride override(root, L"root_key"); + // Nothing to delete if the keys aren't even there. + { + MockRegistryValuePredicate pred; + + EXPECT_CALL(pred, Evaluate(_)).Times(0); + ASSERT_FALSE(RegKey(root, parent_key_path.c_str(), + KEY_QUERY_VALUE).Valid()); + EXPECT_TRUE(InstallUtil::DeleteRegistryKeyIf( + root, parent_key_path, child_key_path, value_name, pred)); + EXPECT_FALSE(RegKey(root, parent_key_path.c_str(), + KEY_QUERY_VALUE).Valid()); + } + + // Parent exists, but not child: no delete. + { + MockRegistryValuePredicate pred; + + EXPECT_CALL(pred, Evaluate(_)).Times(0); + ASSERT_TRUE(RegKey(root, parent_key_path.c_str(), KEY_SET_VALUE).Valid()); + EXPECT_TRUE(InstallUtil::DeleteRegistryKeyIf( + root, parent_key_path, child_key_path, value_name, pred)); + EXPECT_TRUE(RegKey(root, parent_key_path.c_str(), + KEY_QUERY_VALUE).Valid()); + } + + // Child exists, but no value: no delete. + { + MockRegistryValuePredicate pred; + + EXPECT_CALL(pred, Evaluate(_)).Times(0); + ASSERT_TRUE(RegKey(root, child_key_path.c_str(), KEY_SET_VALUE).Valid()); + EXPECT_TRUE(InstallUtil::DeleteRegistryKeyIf( + root, parent_key_path, child_key_path, value_name, pred)); + EXPECT_TRUE(RegKey(root, parent_key_path.c_str(), + KEY_QUERY_VALUE).Valid()); + } + + // Value exists, but doesn't match: no delete. + { + MockRegistryValuePredicate pred; + + EXPECT_CALL(pred, Evaluate(StrEq(L"foosball!"))).WillOnce(Return(false)); + ASSERT_EQ(ERROR_SUCCESS, + RegKey(root, child_key_path.c_str(), + KEY_SET_VALUE).WriteValue(value_name, L"foosball!")); + EXPECT_TRUE(InstallUtil::DeleteRegistryKeyIf( + root, parent_key_path, child_key_path, value_name, pred)); + EXPECT_TRUE(RegKey(root, parent_key_path.c_str(), + KEY_QUERY_VALUE).Valid()); + } + + // Value exists, and matches: delete. + { + MockRegistryValuePredicate pred; + + EXPECT_CALL(pred, Evaluate(StrEq(value))).WillOnce(Return(true)); + ASSERT_EQ(ERROR_SUCCESS, + RegKey(root, child_key_path.c_str(), + KEY_SET_VALUE).WriteValue(value_name, value)); + EXPECT_TRUE(InstallUtil::DeleteRegistryKeyIf( + root, parent_key_path, child_key_path, value_name, pred)); + EXPECT_FALSE(RegKey(root, parent_key_path.c_str(), + KEY_QUERY_VALUE).Valid()); + } + } + TempRegKeyOverride::DeleteAllTempKeys(); +} + +TEST_F(InstallUtilTest, DeleteRegistryValueIf) { + const HKEY root = HKEY_CURRENT_USER; + std::wstring key_path(L"SomeKey\\ToDelete"); + const wchar_t value_name[] = L"some_value_name"; + const wchar_t value[] = L"hi mom"; + + { + TempRegKeyOverride override(root, L"root_key"); + // Nothing to delete if the key isn't even there. + { + MockRegistryValuePredicate pred; + + EXPECT_CALL(pred, Evaluate(_)).Times(0); + ASSERT_FALSE(RegKey(root, key_path.c_str(), KEY_QUERY_VALUE).Valid()); + EXPECT_TRUE(InstallUtil::DeleteRegistryValueIf( + root, key_path.c_str(), value_name, pred)); + EXPECT_FALSE(RegKey(root, key_path.c_str(), KEY_QUERY_VALUE).Valid()); + } + + // Key exists, but no value: no delete. + { + MockRegistryValuePredicate pred; + + EXPECT_CALL(pred, Evaluate(_)).Times(0); + ASSERT_TRUE(RegKey(root, key_path.c_str(), KEY_SET_VALUE).Valid()); + EXPECT_TRUE(InstallUtil::DeleteRegistryValueIf( + root, key_path.c_str(), value_name, pred)); + EXPECT_TRUE(RegKey(root, key_path.c_str(), KEY_QUERY_VALUE).Valid()); + } + + // Value exists, but doesn't match: no delete. + { + MockRegistryValuePredicate pred; + + EXPECT_CALL(pred, Evaluate(StrEq(L"foosball!"))).WillOnce(Return(false)); + ASSERT_EQ(ERROR_SUCCESS, + RegKey(root, key_path.c_str(), + KEY_SET_VALUE).WriteValue(value_name, L"foosball!")); + EXPECT_TRUE(InstallUtil::DeleteRegistryValueIf( + root, key_path.c_str(), value_name, pred)); + EXPECT_TRUE(RegKey(root, key_path.c_str(), KEY_QUERY_VALUE).Valid()); + EXPECT_TRUE(RegKey(root, key_path.c_str(), + KEY_QUERY_VALUE).ValueExists(value_name)); + } + + // Value exists, and matches: delete. + { + MockRegistryValuePredicate pred; + + EXPECT_CALL(pred, Evaluate(StrEq(value))).WillOnce(Return(true)); + ASSERT_EQ(ERROR_SUCCESS, + RegKey(root, key_path.c_str(), + KEY_SET_VALUE).WriteValue(value_name, value)); + EXPECT_TRUE(InstallUtil::DeleteRegistryValueIf( + root, key_path.c_str(), value_name, pred)); + EXPECT_TRUE(RegKey(root, key_path.c_str(), KEY_QUERY_VALUE).Valid()); + EXPECT_FALSE(RegKey(root, key_path.c_str(), + KEY_QUERY_VALUE).ValueExists(value_name)); + } + } + TempRegKeyOverride::DeleteAllTempKeys(); + + { + TempRegKeyOverride override(root, L"root_key"); + // Default value matches: delete. + { + MockRegistryValuePredicate pred; + + EXPECT_CALL(pred, Evaluate(StrEq(value))).WillOnce(Return(true)); + ASSERT_EQ(ERROR_SUCCESS, + RegKey(root, key_path.c_str(), + KEY_SET_VALUE).WriteValue(L"", value)); + EXPECT_TRUE(InstallUtil::DeleteRegistryValueIf( + root, key_path.c_str(), L"", pred)); + EXPECT_TRUE(RegKey(root, key_path.c_str(), KEY_QUERY_VALUE).Valid()); + EXPECT_FALSE(RegKey(root, key_path.c_str(), + KEY_QUERY_VALUE).ValueExists(L"")); + } + } + TempRegKeyOverride::DeleteAllTempKeys(); +} + +TEST_F(InstallUtilTest, ValueEquals) { + InstallUtil::ValueEquals pred(L"howdy"); + + EXPECT_FALSE(pred.Evaluate(L"")); + EXPECT_FALSE(pred.Evaluate(L"Howdy")); + EXPECT_FALSE(pred.Evaluate(L"howdy!")); + EXPECT_FALSE(pred.Evaluate(L"!howdy")); + EXPECT_TRUE(pred.Evaluate(L"howdy")); +} |