diff options
author | gab <gab@chromium.org> | 2015-07-13 10:55:05 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-07-13 17:56:00 +0000 |
commit | 3861555fdf3bd6238cb177b66a83ac34fe06752e (patch) | |
tree | f4640ecb67400fc247201549775897566339fafc /chrome/installer/setup | |
parent | d46fb2c684affce12b619a432ec0c2ba3b0518cc (diff) | |
download | chromium_src-3861555fdf3bd6238cb177b66a83ac34fe06752e.zip chromium_src-3861555fdf3bd6238cb177b66a83ac34fe06752e.tar.gz chromium_src-3861555fdf3bd6238cb177b66a83ac34fe06752e.tar.bz2 |
Introduce UpdateActiveSetupVersionWorkItem
A cleaner solution coming out of an attempt to support two similar use
cases differently in https://codereview.chromium.org/1213913002/ and
https://codereview.chromium.org/1214163008/.
Actual use cases implemented in a follow-up CL (https://codereview.chromium.org/1223933005/).
BUG=488247, 502363
TEST=setup_unittests.exe --gtest_filter=UpdateActiveSetupVersionWorkItemTest*
Review URL: https://codereview.chromium.org/1223953003
Cr-Commit-Position: refs/heads/master@{#338528}
Diffstat (limited to 'chrome/installer/setup')
4 files changed, 295 insertions, 0 deletions
diff --git a/chrome/installer/setup/BUILD.gn b/chrome/installer/setup/BUILD.gn index 449f16f..5809a9d 100644 --- a/chrome/installer/setup/BUILD.gn +++ b/chrome/installer/setup/BUILD.gn @@ -39,6 +39,9 @@ if (is_win) { "setup_util.cc", "setup_util_unittest.cc", "setup_util_unittest.h", + "update_active_setup_version_work_item.cc", # Move to lib + "update_active_setup_version_work_item.h", # Move to lib + "update_active_setup_version_work_item_unittest.cc", ] deps = [ diff --git a/chrome/installer/setup/update_active_setup_version_work_item.cc b/chrome/installer/setup/update_active_setup_version_work_item.cc new file mode 100644 index 0000000..4d35494 --- /dev/null +++ b/chrome/installer/setup/update_active_setup_version_work_item.cc @@ -0,0 +1,73 @@ +// Copyright 2015 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/update_active_setup_version_work_item.h" + +#include <stdint.h> + +#include <vector> + +#include "base/bind.h" +#include "base/logging.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" + +namespace { + +// The major version and first component of the version identifying the work +// done by setup.exe --configure-user-settings on user login by way of Active +// Setup. Increase this value if the work done when handling Active Setup +// should be executed again for all existing users. +const base::char16 kActiveSetupMajorVersion[] = L"43"; + +} // namespace + +UpdateActiveSetupVersionWorkItem::UpdateActiveSetupVersionWorkItem( + const base::string16& active_setup_path, + Operation operation) + : set_reg_value_work_item_( + HKEY_LOCAL_MACHINE, + active_setup_path, + WorkItem::kWow64Default, + L"Version", + base::Bind( + &UpdateActiveSetupVersionWorkItem::GetUpdatedActiveSetupVersion, + base::Unretained(this))), + operation_(operation) { +} + +bool UpdateActiveSetupVersionWorkItem::Do() { + return set_reg_value_work_item_.Do(); +} + +void UpdateActiveSetupVersionWorkItem::Rollback() { + set_reg_value_work_item_.Rollback(); +} + +base::string16 UpdateActiveSetupVersionWorkItem::GetUpdatedActiveSetupVersion( + const base::string16& existing_version) { + std::vector<base::string16> version_components = base::SplitString( + existing_version, L",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); + + // If |existing_version| was empty or otherwise corrupted, turn it into a + // valid one. + if (version_components.size() != 4U) + version_components.assign(4U, L"0"); + + // Unconditionally update the major version. + version_components[MAJOR] = kActiveSetupMajorVersion; + + if (operation_ == UPDATE_AND_BUMP_OS_UPGRADES_COMPONENT) { + uint32_t previous_value; + if (!base::StringToUint(version_components[OS_UPGRADES], &previous_value)) { + LOG(WARNING) << "Couldn't process previous OS_UPGRADES Active Setup " + "version component: " << version_components[OS_UPGRADES]; + previous_value = 0; + } + version_components[OS_UPGRADES] = base::UintToString16(previous_value + 1); + } + + return JoinString(version_components, L','); +} diff --git a/chrome/installer/setup/update_active_setup_version_work_item.h b/chrome/installer/setup/update_active_setup_version_work_item.h new file mode 100644 index 0000000..03aec44 --- /dev/null +++ b/chrome/installer/setup/update_active_setup_version_work_item.h @@ -0,0 +1,66 @@ +// Copyright 2015 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. + +#ifndef CHROME_INSTALLER_SETUP_UPDATE_ACTIVE_SETUP_VERSION_WORK_ITEM_H_ +#define CHROME_INSTALLER_SETUP_UPDATE_ACTIVE_SETUP_VERSION_WORK_ITEM_H_ + +#include "base/macros.h" +#include "base/strings/string16.h" +#include "chrome/installer/util/set_reg_value_work_item.h" +#include "chrome/installer/util/work_item.h" + +// A WorkItem that updates (or installs if not present) the Active Setup +// "Version" field in the registry. Optionally bumping the OS_UPGRADES component +// on demand. This WorkItem is only viable on machine-wide installs. +class UpdateActiveSetupVersionWorkItem : public WorkItem { + public: + // The operation to be performed by this UpdateActiveSetupVersionWorkItem. + enum Operation { + // Update (or install if not present) the Active Setup "Version" in the + // registry. + UPDATE, + // Also bump the OS_UPGRADES component on top of updating the version + // (will default to 1 if the version was absent or invalid). + UPDATE_AND_BUMP_OS_UPGRADES_COMPONENT, + }; + + // Constructs an UpdateActiveSetupVersionWorkItem that will perform + // |operation| on the |active_setup_path| key in the registry. This key needs + // to exist when this WorkItem is ran. + UpdateActiveSetupVersionWorkItem(const base::string16& active_setup_path, + Operation operation); + + // Overriden from WorkItem. + bool Do() override; + void Rollback() override; + + private: + // The components of the Active Setup Version entry, in order. + enum ActiveSetupVersionComponent { + // The major version. + MAJOR, + // Unused component, always 0 for now. + UNUSED1, + // Number of OS upgrades handled since original install. + OS_UPGRADES, + // Unused component, always 0 for now. + UNUSED2, + }; + + // Returns the updated Active Setup version to be used based on the + // |existing_version|. + base::string16 GetUpdatedActiveSetupVersion( + const base::string16& existing_version); + + // The underlying WorkItem re-used to operate forward and backward on the + // registry. + SetRegValueWorkItem set_reg_value_work_item_; + + // The Operation to be performed by this WorkItem when executed. + const Operation operation_; + + DISALLOW_COPY_AND_ASSIGN(UpdateActiveSetupVersionWorkItem); +}; + +#endif // CHROME_INSTALLER_SETUP_UPDATE_ACTIVE_SETUP_VERSION_WORK_ITEM_H_ diff --git a/chrome/installer/setup/update_active_setup_version_work_item_unittest.cc b/chrome/installer/setup/update_active_setup_version_work_item_unittest.cc new file mode 100644 index 0000000..bf3ca46 --- /dev/null +++ b/chrome/installer/setup/update_active_setup_version_work_item_unittest.cc @@ -0,0 +1,153 @@ +// Copyright 2015 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/update_active_setup_version_work_item.h" + +#include <windows.h> + +#include <ostream> + +#include "base/macros.h" +#include "base/strings/string16.h" +#include "base/test/test_reg_util_win.h" +#include "base/win/registry.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::ValuesIn; + +namespace { + +const HKEY kActiveSetupRoot = HKEY_LOCAL_MACHINE; +const base::char16 kActiveSetupPath[] = L"Active Setup\\test"; + +struct UpdateActiveSetupVersionWorkItemTestCase { + // The initial value to be set in the registry prior to executing the + // UpdateActiveSetupVersionWorkItem. No value will be set if this null. + const wchar_t* initial_value; + + // Whether to ask the UpdateActiveSetupVersionWorkItem to bump the OS_UPGRADES + // component of the Active Setup version. + bool bump_os_upgrades_component; + + // The expected value after executing the UpdateActiveSetupVersionWorkItem. + const wchar_t* expected_result; +} const kUpdateActiveSetupVersionWorkItemTestCases[] = { + // Initial install. + {nullptr, false, L"43,0,0,0"}, + // No-op update. + {L"43.0.0.0", false, L"43,0,0,0"}, + // Update only major component. + {L"24,1,2,3", false, L"43,1,2,3"}, + // Reset from bogus value. + {L"zzz", false, L"43,0,0,0"}, + // Reset from invalid version (too few components). + {L"1,2", false, L"43,0,0,0"}, + // Reset from invalid version (too many components). + {L"43,1,2,3,10", false, L"43,0,0,0"}, + // Reset from empty string. + {L"", false, L"43,0,0,0"}, + + // Same tests with an OS_UPGRADES component bump. + {nullptr, true, L"43,0,1,0"}, + {L"43.0.0.0", true, L"43,0,1,0"}, + {L"24,1,2,3", true, L"43,1,3,3"}, + {L"zzz", true, L"43,0,1,0"}, + {L"1,2", true, L"43,0,1,0"}, + {L"43,1,2,3,10", true, L"43,0,1,0"}, + {L"", true, L"43,0,1,0"}, + // Bumping a negative OS upgrade component should result in it being + // reset and subsequently bumped to 1 as usual. + {L"43,11,-123,33", true, L"43,11,1,33"}, +}; + +// Implements PrintTo in order for gtest to be able to print the problematic +// UpdateActiveSetupVersionWorkItemTestCase on failure. +void PrintTo(const UpdateActiveSetupVersionWorkItemTestCase& test_case, + ::std::ostream* os) { + *os << "Initial value: " + << (test_case.initial_value ? test_case.initial_value : L"(empty)") + << ", bump_os_upgrades_component: " + << test_case.bump_os_upgrades_component + << ", expected result: " << test_case.expected_result; +} + +} // namespace + +class UpdateActiveSetupVersionWorkItemTest + : public testing::TestWithParam<UpdateActiveSetupVersionWorkItemTestCase> { + public: + UpdateActiveSetupVersionWorkItemTest() {} + + void SetUp() override { + registry_override_manager_.OverrideRegistry(kActiveSetupRoot); + } + + private: + registry_util::RegistryOverrideManager registry_override_manager_; + + DISALLOW_COPY_AND_ASSIGN(UpdateActiveSetupVersionWorkItemTest); +}; + +TEST_P(UpdateActiveSetupVersionWorkItemTest, Execute) { + // Get the parametrized |test_case| which defines 5 steps: + // 1) Maybe set an initial Active Setup version in the registry according to + // |test_case.initial_value|. + // 2) Declare the work to be done by the UpdateActiveSetupVersionWorkItem + // based on |test_case.bump_os_upgrades_component|. + // 3) Unconditionally execute the Active Setup work items. + // 4) Verify that the updated Active Setup version is as expected by + // |test_case.expected_result|. + // 5) Rollback and verify that |test_case.initial_value| is back. + const UpdateActiveSetupVersionWorkItemTestCase& test_case = GetParam(); + + base::win::RegKey test_key; + + ASSERT_EQ(ERROR_FILE_NOT_FOUND, + test_key.Open(kActiveSetupRoot, kActiveSetupPath, KEY_READ)); + + UpdateActiveSetupVersionWorkItem active_setup_work_item( + kActiveSetupPath, test_case.bump_os_upgrades_component + ? UpdateActiveSetupVersionWorkItem:: + UPDATE_AND_BUMP_OS_UPGRADES_COMPONENT + : UpdateActiveSetupVersionWorkItem::UPDATE); + + // Create the key and set the |initial_value| *after* the WorkItem to confirm + // that all of the work is done when executing the item, not when creating it. + ASSERT_EQ(ERROR_SUCCESS, + test_key.Create(kActiveSetupRoot, kActiveSetupPath, KEY_SET_VALUE)); + if (test_case.initial_value) { + ASSERT_EQ(ERROR_SUCCESS, + test_key.WriteValue(L"Version", test_case.initial_value)); + } + + EXPECT_TRUE(active_setup_work_item.Do()); + + { + base::string16 version_out; + EXPECT_EQ(ERROR_SUCCESS, test_key.Open(kActiveSetupRoot, kActiveSetupPath, + KEY_QUERY_VALUE)); + EXPECT_EQ(ERROR_SUCCESS, test_key.ReadValue(L"Version", &version_out)); + EXPECT_EQ(test_case.expected_result, version_out); + } + + active_setup_work_item.Rollback(); + + { + EXPECT_EQ(ERROR_SUCCESS, test_key.Open(kActiveSetupRoot, kActiveSetupPath, + KEY_QUERY_VALUE)); + + base::string16 version_out; + LONG read_result = test_key.ReadValue(L"Version", &version_out); + if (test_case.initial_value) { + EXPECT_EQ(ERROR_SUCCESS, read_result); + EXPECT_EQ(test_case.initial_value, version_out); + } else { + EXPECT_EQ(ERROR_FILE_NOT_FOUND, read_result); + } + } +} + +INSTANTIATE_TEST_CASE_P(UpdateActiveSetupVersionWorkItemTestInstance, + UpdateActiveSetupVersionWorkItemTest, + ValuesIn(kUpdateActiveSetupVersionWorkItemTestCases)); |