// 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 <string> #include <utility> #include "base/command_line.h" #include "base/file_util.h" #include "base/strings/string_util.h" #include "base/test/test_reg_util_win.h" #include "base/win/registry.h" #include "chrome/installer/util/google_update_constants.h" #include "chrome/installer/util/install_util.h" #include "chrome/installer/util/product_unittest.h" #include "chrome/installer/util/work_item.h" #include "testing/gmock/include/gmock/gmock.h" using base::win::RegKey; using registry_util::RegistryOverrideManager; 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: }; TEST_F(InstallUtilTest, MakeUninstallCommand) { CommandLine command_line(CommandLine::NO_PROGRAM); std::pair<std::wstring, std::wstring> params[] = { std::make_pair(std::wstring(L""), std::wstring(L"")), std::make_pair(std::wstring(L""), std::wstring(L"--do-something --silly")), std::make_pair(std::wstring(L"spam.exe"), std::wstring(L"")), std::make_pair(std::wstring(L"spam.exe"), std::wstring(L"--do-something --silly")), }; for (int i = 0; i < arraysize(params); ++i) { std::pair<std::wstring, std::wstring>& param = params[i]; InstallUtil::MakeUninstallCommand(param.first, param.second, &command_line); EXPECT_EQ(param.first, command_line.GetProgram().value()); if (param.second.empty()) { EXPECT_TRUE(command_line.GetSwitches().empty()); } else { EXPECT_EQ(2U, command_line.GetSwitches().size()); EXPECT_TRUE(command_line.HasSwitch("do-something")); EXPECT_TRUE(command_line.HasSwitch("silly")); } } } TEST_F(InstallUtilTest, GetCurrentDate) { std::wstring date(InstallUtil::GetCurrentDate()); EXPECT_EQ(8, date.length()); if (date.length() == 8) { // For an invalid date value, SystemTimeToFileTime will fail. // We use this to validate that we have a correct date string. SYSTEMTIME systime = {0}; FILETIME ft = {0}; // Just to make sure our assumption holds. EXPECT_FALSE(SystemTimeToFileTime(&systime, &ft)); // Now fill in the values from our string. systime.wYear = _wtoi(date.substr(0, 4).c_str()); systime.wMonth = _wtoi(date.substr(4, 2).c_str()); systime.wDay = _wtoi(date.substr(6, 2).c_str()); // Check if they make sense. EXPECT_TRUE(SystemTimeToFileTime(&systime, &ft)); } } TEST_F(InstallUtilTest, UpdateInstallerStageAP) { const bool system_level = false; const HKEY root = system_level ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; std::wstring state_key_path(L"PhonyClientState"); // Update the stage when there's no "ap" value. { RegistryOverrideManager override_manager; override_manager.OverrideRegistry(root, L"root_inst_res"); RegKey(root, state_key_path.c_str(), KEY_SET_VALUE); InstallUtil::UpdateInstallerStage(system_level, state_key_path, installer::BUILDING); std::wstring value; EXPECT_EQ(ERROR_SUCCESS, RegKey(root, state_key_path.c_str(), KEY_QUERY_VALUE) .ReadValue(google_update::kRegApField, &value)); EXPECT_EQ(L"-stage:building", value); } // Update the stage when there is an "ap" value. { RegistryOverrideManager override_manager; override_manager.OverrideRegistry(root, L"root_inst_res"); RegKey(root, state_key_path.c_str(), KEY_SET_VALUE) .WriteValue(google_update::kRegApField, L"2.0-dev"); InstallUtil::UpdateInstallerStage(system_level, state_key_path, installer::BUILDING); std::wstring value; EXPECT_EQ(ERROR_SUCCESS, RegKey(root, state_key_path.c_str(), KEY_QUERY_VALUE) .ReadValue(google_update::kRegApField, &value)); EXPECT_EQ(L"2.0-dev-stage:building", value); } // Clear the stage. { RegistryOverrideManager override_manager; override_manager.OverrideRegistry(root, L"root_inst_res"); RegKey(root, state_key_path.c_str(), KEY_SET_VALUE) .WriteValue(google_update::kRegApField, L"2.0-dev-stage:building"); InstallUtil::UpdateInstallerStage(system_level, state_key_path, installer::NO_STAGE); std::wstring value; EXPECT_EQ(ERROR_SUCCESS, RegKey(root, state_key_path.c_str(), KEY_QUERY_VALUE) .ReadValue(google_update::kRegApField, &value)); EXPECT_EQ(L"2.0-dev", value); } } TEST_F(InstallUtilTest, UpdateInstallerStage) { const bool system_level = false; const HKEY root = system_level ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; std::wstring state_key_path(L"PhonyClientState"); // Update the stage when there's no "InstallerExtraCode1" value. { RegistryOverrideManager override_manager; override_manager.OverrideRegistry(root, L"root_inst_res"); RegKey(root, state_key_path.c_str(), KEY_SET_VALUE) .DeleteValue(installer::kInstallerExtraCode1); InstallUtil::UpdateInstallerStage(system_level, state_key_path, installer::BUILDING); DWORD value; EXPECT_EQ(ERROR_SUCCESS, RegKey(root, state_key_path.c_str(), KEY_QUERY_VALUE) .ReadValueDW(installer::kInstallerExtraCode1, &value)); EXPECT_EQ(static_cast<DWORD>(installer::BUILDING), value); } // Update the stage when there is an "InstallerExtraCode1" value. { RegistryOverrideManager override_manager; override_manager.OverrideRegistry(root, L"root_inst_res"); RegKey(root, state_key_path.c_str(), KEY_SET_VALUE) .WriteValue(installer::kInstallerExtraCode1, static_cast<DWORD>(installer::UNPACKING)); InstallUtil::UpdateInstallerStage(system_level, state_key_path, installer::BUILDING); DWORD value; EXPECT_EQ(ERROR_SUCCESS, RegKey(root, state_key_path.c_str(), KEY_QUERY_VALUE) .ReadValueDW(installer::kInstallerExtraCode1, &value)); EXPECT_EQ(static_cast<DWORD>(installer::BUILDING), value); } // Clear the stage. { RegistryOverrideManager override_manager; override_manager.OverrideRegistry(root, L"root_inst_res"); RegKey(root, state_key_path.c_str(), KEY_SET_VALUE) .WriteValue(installer::kInstallerExtraCode1, static_cast<DWORD>(5)); InstallUtil::UpdateInstallerStage(system_level, state_key_path, installer::NO_STAGE); DWORD value; EXPECT_EQ(ERROR_FILE_NOT_FOUND, RegKey(root, state_key_path.c_str(), KEY_QUERY_VALUE) .ReadValueDW(installer::kInstallerExtraCode1, &value)); } } 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"; { RegistryOverrideManager override_manager; override_manager.OverrideRegistry(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_EQ(InstallUtil::NOT_FOUND, InstallUtil::DeleteRegistryKeyIf(root, parent_key_path, child_key_path, WorkItem::kWow64Default, 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_EQ(InstallUtil::NOT_FOUND, InstallUtil::DeleteRegistryKeyIf(root, parent_key_path, child_key_path, WorkItem::kWow64Default, 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_EQ(InstallUtil::NOT_FOUND, InstallUtil::DeleteRegistryKeyIf(root, parent_key_path, child_key_path, WorkItem::kWow64Default, 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_EQ(InstallUtil::NOT_FOUND, InstallUtil::DeleteRegistryKeyIf(root, parent_key_path, child_key_path, WorkItem::kWow64Default, 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_EQ(InstallUtil::DELETED, InstallUtil::DeleteRegistryKeyIf(root, parent_key_path, child_key_path, WorkItem::kWow64Default, value_name, pred)); EXPECT_FALSE(RegKey(root, parent_key_path.c_str(), KEY_QUERY_VALUE).Valid()); } // Default 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(NULL, value)); EXPECT_EQ(InstallUtil::DELETED, InstallUtil::DeleteRegistryKeyIf(root, parent_key_path, child_key_path, WorkItem::kWow64Default, NULL, pred)); EXPECT_FALSE(RegKey(root, parent_key_path.c_str(), KEY_QUERY_VALUE).Valid()); } } } 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"; { RegistryOverrideManager override_manager; override_manager.OverrideRegistry(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_EQ(InstallUtil::NOT_FOUND, InstallUtil::DeleteRegistryValueIf(root, key_path.c_str(), WorkItem::kWow64Default, 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_EQ(InstallUtil::NOT_FOUND, InstallUtil::DeleteRegistryValueIf(root, key_path.c_str(), WorkItem::kWow64Default, 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_EQ(InstallUtil::NOT_FOUND, InstallUtil::DeleteRegistryValueIf(root, key_path.c_str(), WorkItem::kWow64Default, 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).HasValue(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_EQ(InstallUtil::DELETED, InstallUtil::DeleteRegistryValueIf(root, key_path.c_str(), WorkItem::kWow64Default, 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).HasValue(value_name)); } } { RegistryOverrideManager override_manager; override_manager.OverrideRegistry(root, L"root_key"); // Default value matches: delete using empty string. { 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_EQ(InstallUtil::DELETED, InstallUtil::DeleteRegistryValueIf(root, key_path.c_str(), WorkItem::kWow64Default, 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).HasValue(L"")); } } { RegistryOverrideManager override_manager; override_manager.OverrideRegistry(root, L"root_key"); // Default value matches: delete using NULL. { 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_EQ(InstallUtil::DELETED, InstallUtil::DeleteRegistryValueIf(root, key_path.c_str(), WorkItem::kWow64Default, NULL, pred)); EXPECT_TRUE(RegKey(root, key_path.c_str(), KEY_QUERY_VALUE).Valid()); EXPECT_FALSE(RegKey(root, key_path.c_str(), KEY_QUERY_VALUE).HasValue(L"")); } } } 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")); } TEST_F(InstallUtilTest, ProgramCompare) { base::FilePath some_long_dir( test_dir_.path().Append(L"Some Long Directory Name")); base::FilePath expect(some_long_dir.Append(L"file.txt")); base::FilePath expect_upcase(some_long_dir.Append(L"FILE.txt")); base::FilePath other(some_long_dir.Append(L"otherfile.txt")); // Tests where the expected file doesn't exist. // Paths don't match. EXPECT_FALSE(InstallUtil::ProgramCompare(expect).Evaluate( L"\"" + other.value() + L"\"")); // Paths match exactly. EXPECT_TRUE(InstallUtil::ProgramCompare(expect).Evaluate( L"\"" + expect.value() + L"\"")); // Paths differ by case. EXPECT_TRUE(InstallUtil::ProgramCompare(expect).Evaluate( L"\"" + expect_upcase.value() + L"\"")); // Tests where the expected file exists. static const char data[] = "data"; ASSERT_TRUE(base::CreateDirectory(some_long_dir)); ASSERT_NE(-1, base::WriteFile(expect, data, arraysize(data) - 1)); // Paths don't match. EXPECT_FALSE(InstallUtil::ProgramCompare(expect).Evaluate( L"\"" + other.value() + L"\"")); // Paths match exactly. EXPECT_TRUE(InstallUtil::ProgramCompare(expect).Evaluate( L"\"" + expect.value() + L"\"")); // Paths differ by case. EXPECT_TRUE(InstallUtil::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(base::FilePath::CompareEqualIgnoreCase(expect.value(), short_expect)); EXPECT_TRUE(InstallUtil::ProgramCompare(expect).Evaluate( L"\"" + short_expect + L"\"")); }