1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
|
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/downgrade/user_data_downgrade.h"
#include <memory>
#include <string>
#include <string_view>
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/test_reg_util_win.h"
#include "base/threading/thread_restrictions.h"
#include "base/version.h"
#include "base/win/registry.h"
#include "chrome/browser/chrome_browser_main.h"
#include "chrome/browser/chrome_browser_main_extra_parts.h"
#include "chrome/browser/first_run/scoped_relaunch_chrome_browser_override.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_result_codes.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/install_static/install_util.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "components/version_info/version_info.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace downgrade {
// A basic test fixture for User Data downgrade tests that writes a test file
// and a "Last Version" file into User Data in the pre-relaunch case. The former
// is used to validate move-and-delete processing on downgrade, while the latter
// is to simulate a previous run of a higher version of the browser. The fixture
// is expected to be used in a PRE_ and a regular test, with IsPreTest used to
// distinguish these cases at runtime.
class UserDataDowngradeBrowserTestBase : public InProcessBrowserTest {
public:
UserDataDowngradeBrowserTestBase(const UserDataDowngradeBrowserTestBase&) =
delete;
UserDataDowngradeBrowserTestBase& operator=(
const UserDataDowngradeBrowserTestBase&) = delete;
protected:
// Returns true if the PRE_ test is running, meaning that the test is in the
// "before relaunch" stage.
static bool IsPreTest() {
const std::string_view test_name(
::testing::UnitTest::GetInstance()->current_test_info()->name());
return test_name.find("PRE_") != std::string_view::npos;
}
// Returns the next Chrome milestone version.
static std::string GetNextChromeVersion() {
return base::NumberToString(version_info::GetVersion().components()[0] + 1);
}
UserDataDowngradeBrowserTestBase()
: root_key_(install_static::IsSystemInstall() ? HKEY_LOCAL_MACHINE
: HKEY_CURRENT_USER) {}
// Returns the registry hive into which the browser is registered.
HKEY root_key() const { return root_key_; }
// Returns the path to the User Data dir.
const base::FilePath& user_data_dir() const { return user_data_dir_; }
// Returns the destination path to which User Data may be or was moved.
const base::FilePath& moved_user_data_dir() const {
return moved_user_data_dir_;
}
// Returns the path to some generated file in User Data.
const base::FilePath& other_file() const { return other_file_; }
// InProcessBrowserTest:
void SetUp() override {
ASSERT_NO_FATAL_FAILURE(
registry_override_manager_.OverrideRegistry(root_key_));
ASSERT_TRUE(base::win::RegKey(
root_key_, install_static::GetClientStateKeyPath().c_str(),
KEY_SET_VALUE | KEY_WOW64_32KEY)
.Valid());
InProcessBrowserTest::SetUp();
}
bool SetUpUserDataDirectory() override {
if (!base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir_))
return false;
other_file_ = user_data_dir_.Append(FILE_PATH_LITERAL("Other File"));
moved_user_data_dir_ = user_data_dir_.Append(user_data_dir_.BaseName())
.AddExtension(kDowngradeDeleteSuffix);
if (IsPreTest()) {
// Create some "other file" to be convinced that stuff is moved.
if (!base::WriteFile(other_file_, "data"))
return false;
// Pretend that a higher version of Chrome previously wrote User Data.
const std::string last_version = GetNextChromeVersion();
base::WriteFile(user_data_dir_.Append(kDowngradeLastVersionFile),
last_version);
}
return true;
}
private:
// The registry hive into which the browser is/will be registered.
const HKEY root_key_;
// The location of User Data.
base::FilePath user_data_dir_;
// The location into which the contents of User Data may be moved in case of
// downgrade.
base::FilePath moved_user_data_dir_;
// The path to an arbitrary file in the user data dir that will be present
// only when a reset does not take place.
base::FilePath other_file_;
registry_util::RegistryOverrideManager registry_override_manager_;
};
// A gMock matcher that is satisfied when its argument is a command line
// containing a given switch.
MATCHER_P(HasSwitch, switch_name, "") {
return arg.HasSwitch(switch_name);
}
// A test fixture that triggers a downgrade, expects a relaunch, and verifies
// that User Data was moved and then subsequently deleted.
class UserDataDowngradeBrowserCopyAndCleanTest
: public UserDataDowngradeBrowserTestBase {
public:
UserDataDowngradeBrowserCopyAndCleanTest(
const UserDataDowngradeBrowserCopyAndCleanTest&) = delete;
UserDataDowngradeBrowserCopyAndCleanTest& operator=(
const UserDataDowngradeBrowserCopyAndCleanTest&) = delete;
protected:
using ParentClass = UserDataDowngradeBrowserTestBase;
UserDataDowngradeBrowserCopyAndCleanTest() = default;
// InProcessBrowserTest:
void SetUpInProcessBrowserTestFixture() override {
if (ParentClass::IsPreTest()) {
// Pretend that a downgrade was performed via the installer.
ASSERT_NO_FATAL_FAILURE(SetDowngradeVersion(GetNextChromeVersion()));
// Expect a browser relaunch late in browser shutdown.
mock_relaunch_callback_ = std::make_unique<::testing::StrictMock<
base::MockCallback<upgrade_util::RelaunchChromeBrowserCallback>>>();
EXPECT_CALL(*mock_relaunch_callback_,
Run(HasSwitch(switches::kUserDataMigrated)));
relaunch_chrome_override_ =
std::make_unique<upgrade_util::ScopedRelaunchChromeBrowserOverride>(
mock_relaunch_callback_->Get());
// Expect that browser startup short-circuits into a relaunch.
set_expected_exit_code(CHROME_RESULT_CODE_DOWNGRADE_AND_RELAUNCH);
// Prepare to check histograms during the restart.
histogram_tester_ = std::make_unique<base::HistogramTester>();
} else {
// Verify the contents of the renamed user data directory.
ASSERT_TRUE(base::DirectoryExists(moved_user_data_dir()));
EXPECT_TRUE(base::PathExists(
moved_user_data_dir().Append(other_file().BaseName())));
EXPECT_EQ(GetNextChromeVersion(),
GetLastVersion(moved_user_data_dir())->GetString());
}
}
void CreatedBrowserMainParts(content::BrowserMainParts* parts) override {
ParentClass::CreatedBrowserMainParts(parts);
if (!ParentClass::IsPreTest()) {
// Ensure that the after-startup task to delete User Data has a chance to
// run.
static_cast<ChromeBrowserMainParts*>(parts)->AddParts(
std::make_unique<
RunAllPendingTasksPostMainMessageLoopRunExtraParts>());
}
}
void SetUpOnMainThread() override {
// This is never reached in the pre test due to the relaunch.
ASSERT_FALSE(ParentClass::IsPreTest());
ParentClass::SetUpOnMainThread();
}
void TearDownInProcessBrowserTestFixture() override {
if (!ParentClass::IsPreTest()) {
// Verify the renamed user data directory has been deleted.
EXPECT_FALSE(base::DirectoryExists(moved_user_data_dir()));
}
}
private:
// DeleteMovedUserDataSoon() is called after the test is run, and will post a
// task to perform the actual deletion. Make sure this task gets a chance to
// run, so that the check in TearDownInProcessBrowserTestFixture() works.
class RunAllPendingTasksPostMainMessageLoopRunExtraParts
: public ChromeBrowserMainExtraParts {
public:
void PostMainMessageLoopRun() override { content::RunAllTasksUntilIdle(); }
};
// Writes |downgrade_version| into the DowngradeVersion value in ClientState
// so that the browser believes that a downgrade was driven by an
// administrator rather than an accident of fate.
void SetDowngradeVersion(std::string_view downgrade_version) {
ASSERT_EQ(base::win::RegKey(root_key(),
install_static::GetClientStateKeyPath().c_str(),
KEY_SET_VALUE | KEY_WOW64_32KEY)
.WriteValue(L"DowngradeVersion",
base::ASCIIToWide(downgrade_version).c_str()),
ERROR_SUCCESS);
}
std::unique_ptr<
base::MockCallback<upgrade_util::RelaunchChromeBrowserCallback>>
mock_relaunch_callback_;
std::unique_ptr<upgrade_util::ScopedRelaunchChromeBrowserOverride>
relaunch_chrome_override_;
std::unique_ptr<base::HistogramTester> histogram_tester_;
};
// Verify the user data directory has been renamed and created again after
// downgrade.
IN_PROC_BROWSER_TEST_F(UserDataDowngradeBrowserCopyAndCleanTest, PRE_Test) {}
IN_PROC_BROWSER_TEST_F(UserDataDowngradeBrowserCopyAndCleanTest, Test) {
base::ScopedAllowBlockingForTesting allow_blocking;
base::ThreadPoolInstance::Get()->FlushForTesting();
EXPECT_EQ(chrome::kChromeVersion,
GetLastVersion(user_data_dir())->GetString());
EXPECT_FALSE(base::PathExists(other_file()));
}
// A test fixture that launches the browser twice for a non-administrator
// driven downgrade and ensures that User Data is not moved aside and deleted.
class UserDataDowngradeBrowserNoResetTest
: public UserDataDowngradeBrowserTestBase {
public:
UserDataDowngradeBrowserNoResetTest(
const UserDataDowngradeBrowserNoResetTest&) = delete;
UserDataDowngradeBrowserNoResetTest& operator=(
const UserDataDowngradeBrowserNoResetTest&) = delete;
protected:
UserDataDowngradeBrowserNoResetTest() = default;
};
// Verify the user data directory will not be reset without downgrade.
IN_PROC_BROWSER_TEST_F(UserDataDowngradeBrowserNoResetTest, PRE_Test) {}
// TODO(crbug.com/40925550): Re-enable this test
IN_PROC_BROWSER_TEST_F(UserDataDowngradeBrowserNoResetTest, DISABLED_Test) {
base::ScopedAllowBlockingForTesting allow_blocking;
EXPECT_EQ(chrome::kChromeVersion,
GetLastVersion(user_data_dir())->GetString());
EXPECT_TRUE(base::PathExists(other_file()));
}
} // namespace downgrade
|