Convert browser_tests to Swarming.
[chromium-blink-merge.git] / chrome / installer / setup / setup_util_unittest.cc
blob5174d1c6f6a2d62386203e55c9469671a3bf1ff9
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/installer/setup/setup_util_unittest.h"
7 #include <windows.h>
9 #include <string>
11 #include "base/command_line.h"
12 #include "base/files/file_util.h"
13 #include "base/files/scoped_temp_dir.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/process/kill.h"
16 #include "base/process/launch.h"
17 #include "base/process/process_handle.h"
18 #include "base/strings/string_util.h"
19 #include "base/test/test_reg_util_win.h"
20 #include "base/threading/platform_thread.h"
21 #include "base/time/time.h"
22 #include "base/version.h"
23 #include "base/win/scoped_handle.h"
24 #include "base/win/windows_version.h"
25 #include "chrome/installer/setup/setup_constants.h"
26 #include "chrome/installer/setup/setup_util.h"
27 #include "chrome/installer/util/google_update_constants.h"
28 #include "chrome/installer/util/installation_state.h"
29 #include "chrome/installer/util/installer_state.h"
30 #include "chrome/installer/util/updating_app_registration_data.h"
31 #include "chrome/installer/util/util_constants.h"
32 #include "testing/gtest/include/gtest/gtest.h"
34 namespace {
36 class SetupUtilTestWithDir : public testing::Test {
37 protected:
38 void SetUp() override {
39 // Create a temp directory for testing.
40 ASSERT_TRUE(test_dir_.CreateUniqueTempDir());
43 void TearDown() override {
44 // Clean up test directory manually so we can fail if it leaks.
45 ASSERT_TRUE(test_dir_.Delete());
48 // The temporary directory used to contain the test operations.
49 base::ScopedTempDir test_dir_;
52 // The privilege tested in ScopeTokenPrivilege tests below.
53 // Use SE_RESTORE_NAME as it is one of the many privileges that is available,
54 // but not enabled by default on processes running at high integrity.
55 static const wchar_t kTestedPrivilege[] = SE_RESTORE_NAME;
57 // Returns true if the current process' token has privilege |privilege_name|
58 // enabled.
59 bool CurrentProcessHasPrivilege(const wchar_t* privilege_name) {
60 HANDLE temp_handle;
61 if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY,
62 &temp_handle)) {
63 ADD_FAILURE();
64 return false;
67 base::win::ScopedHandle token(temp_handle);
69 // First get the size of the buffer needed for |privileges| below.
70 DWORD size;
71 EXPECT_FALSE(::GetTokenInformation(token.Get(), TokenPrivileges, NULL, 0,
72 &size));
74 scoped_ptr<BYTE[]> privileges_bytes(new BYTE[size]);
75 TOKEN_PRIVILEGES* privileges =
76 reinterpret_cast<TOKEN_PRIVILEGES*>(privileges_bytes.get());
78 if (!::GetTokenInformation(token.Get(), TokenPrivileges, privileges, size,
79 &size)) {
80 ADD_FAILURE();
81 return false;
84 // There is no point getting a buffer to store more than |privilege_name|\0 as
85 // anything longer will obviously not be equal to |privilege_name|.
86 const DWORD desired_size = wcslen(privilege_name);
87 const DWORD buffer_size = desired_size + 1;
88 scoped_ptr<wchar_t[]> name_buffer(new wchar_t[buffer_size]);
89 for (int i = privileges->PrivilegeCount - 1; i >= 0 ; --i) {
90 LUID_AND_ATTRIBUTES& luid_and_att = privileges->Privileges[i];
91 DWORD size = buffer_size;
92 ::LookupPrivilegeName(NULL, &luid_and_att.Luid, name_buffer.get(), &size);
93 if (size == desired_size &&
94 wcscmp(name_buffer.get(), privilege_name) == 0) {
95 return luid_and_att.Attributes == SE_PRIVILEGE_ENABLED;
98 return false;
101 } // namespace
103 // Test that we are parsing Chrome version correctly.
104 TEST_F(SetupUtilTestWithDir, GetMaxVersionFromArchiveDirTest) {
105 // Create a version dir
106 base::FilePath chrome_dir = test_dir_.path().AppendASCII("1.0.0.0");
107 base::CreateDirectory(chrome_dir);
108 ASSERT_TRUE(base::PathExists(chrome_dir));
109 scoped_ptr<Version> version(
110 installer::GetMaxVersionFromArchiveDir(test_dir_.path()));
111 ASSERT_EQ(version->GetString(), "1.0.0.0");
113 base::DeleteFile(chrome_dir, true);
114 ASSERT_FALSE(base::PathExists(chrome_dir));
115 ASSERT_TRUE(installer::GetMaxVersionFromArchiveDir(test_dir_.path()) == NULL);
117 chrome_dir = test_dir_.path().AppendASCII("ABC");
118 base::CreateDirectory(chrome_dir);
119 ASSERT_TRUE(base::PathExists(chrome_dir));
120 ASSERT_TRUE(installer::GetMaxVersionFromArchiveDir(test_dir_.path()) == NULL);
122 chrome_dir = test_dir_.path().AppendASCII("2.3.4.5");
123 base::CreateDirectory(chrome_dir);
124 ASSERT_TRUE(base::PathExists(chrome_dir));
125 version.reset(installer::GetMaxVersionFromArchiveDir(test_dir_.path()));
126 ASSERT_EQ(version->GetString(), "2.3.4.5");
128 // Create multiple version dirs, ensure that we select the greatest.
129 chrome_dir = test_dir_.path().AppendASCII("9.9.9.9");
130 base::CreateDirectory(chrome_dir);
131 ASSERT_TRUE(base::PathExists(chrome_dir));
132 chrome_dir = test_dir_.path().AppendASCII("1.1.1.1");
133 base::CreateDirectory(chrome_dir);
134 ASSERT_TRUE(base::PathExists(chrome_dir));
136 version.reset(installer::GetMaxVersionFromArchiveDir(test_dir_.path()));
137 ASSERT_EQ(version->GetString(), "9.9.9.9");
140 TEST_F(SetupUtilTestWithDir, DeleteFileFromTempProcess) {
141 base::FilePath test_file;
142 base::CreateTemporaryFileInDir(test_dir_.path(), &test_file);
143 ASSERT_TRUE(base::PathExists(test_file));
144 base::WriteFile(test_file, "foo", 3);
145 EXPECT_TRUE(installer::DeleteFileFromTempProcess(test_file, 0));
146 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
147 EXPECT_FALSE(base::PathExists(test_file));
150 // Note: This test is only valid when run at high integrity (i.e. it will fail
151 // at medium integrity).
152 TEST(SetupUtilTest, ScopedTokenPrivilegeBasic) {
153 ASSERT_FALSE(CurrentProcessHasPrivilege(kTestedPrivilege));
156 installer::ScopedTokenPrivilege test_scoped_privilege(kTestedPrivilege);
157 ASSERT_TRUE(test_scoped_privilege.is_enabled());
158 ASSERT_TRUE(CurrentProcessHasPrivilege(kTestedPrivilege));
161 ASSERT_FALSE(CurrentProcessHasPrivilege(kTestedPrivilege));
164 // Note: This test is only valid when run at high integrity (i.e. it will fail
165 // at medium integrity).
166 TEST(SetupUtilTest, ScopedTokenPrivilegeAlreadyEnabled) {
167 ASSERT_FALSE(CurrentProcessHasPrivilege(kTestedPrivilege));
170 installer::ScopedTokenPrivilege test_scoped_privilege(kTestedPrivilege);
171 ASSERT_TRUE(test_scoped_privilege.is_enabled());
172 ASSERT_TRUE(CurrentProcessHasPrivilege(kTestedPrivilege));
174 installer::ScopedTokenPrivilege dup_scoped_privilege(kTestedPrivilege);
175 ASSERT_TRUE(dup_scoped_privilege.is_enabled());
176 ASSERT_TRUE(CurrentProcessHasPrivilege(kTestedPrivilege));
178 ASSERT_TRUE(CurrentProcessHasPrivilege(kTestedPrivilege));
181 ASSERT_FALSE(CurrentProcessHasPrivilege(kTestedPrivilege));
184 const char kAdjustProcessPriority[] = "adjust-process-priority";
186 PriorityClassChangeResult DoProcessPriorityAdjustment() {
187 return installer::AdjustProcessPriority() ? PCCR_CHANGED : PCCR_UNCHANGED;
190 namespace {
192 // A scoper that sets/resets the current process's priority class.
193 class ScopedPriorityClass {
194 public:
195 // Applies |priority_class|, returning an instance if a change was made.
196 // Otherwise, returns an empty scoped_ptr.
197 static scoped_ptr<ScopedPriorityClass> Create(DWORD priority_class);
198 ~ScopedPriorityClass();
200 private:
201 explicit ScopedPriorityClass(DWORD original_priority_class);
202 DWORD original_priority_class_;
203 DISALLOW_COPY_AND_ASSIGN(ScopedPriorityClass);
206 scoped_ptr<ScopedPriorityClass> ScopedPriorityClass::Create(
207 DWORD priority_class) {
208 HANDLE this_process = ::GetCurrentProcess();
209 DWORD original_priority_class = ::GetPriorityClass(this_process);
210 EXPECT_NE(0U, original_priority_class);
211 if (original_priority_class && original_priority_class != priority_class) {
212 BOOL result = ::SetPriorityClass(this_process, priority_class);
213 EXPECT_NE(FALSE, result);
214 if (result) {
215 return scoped_ptr<ScopedPriorityClass>(
216 new ScopedPriorityClass(original_priority_class));
219 return scoped_ptr<ScopedPriorityClass>();
222 ScopedPriorityClass::ScopedPriorityClass(DWORD original_priority_class)
223 : original_priority_class_(original_priority_class) {}
225 ScopedPriorityClass::~ScopedPriorityClass() {
226 BOOL result = ::SetPriorityClass(::GetCurrentProcess(),
227 original_priority_class_);
228 EXPECT_NE(FALSE, result);
231 PriorityClassChangeResult RelaunchAndDoProcessPriorityAdjustment() {
232 base::CommandLine cmd_line(*base::CommandLine::ForCurrentProcess());
233 cmd_line.AppendSwitch(kAdjustProcessPriority);
234 base::Process process = base::LaunchProcess(cmd_line, base::LaunchOptions());
235 int exit_code = 0;
236 if (!process.IsValid()) {
237 ADD_FAILURE() << " to launch subprocess.";
238 } else if (!process.WaitForExit(&exit_code)) {
239 ADD_FAILURE() << " to wait for subprocess to exit.";
240 } else {
241 return static_cast<PriorityClassChangeResult>(exit_code);
243 return PCCR_UNKNOWN;
246 } // namespace
248 // Launching a subprocess at normal priority class is a noop.
249 TEST(SetupUtilTest, AdjustFromNormalPriority) {
250 ASSERT_EQ(NORMAL_PRIORITY_CLASS, ::GetPriorityClass(::GetCurrentProcess()));
251 EXPECT_EQ(PCCR_UNCHANGED, RelaunchAndDoProcessPriorityAdjustment());
254 // Launching a subprocess below normal priority class drops it to bg mode for
255 // sufficiently recent operating systems.
256 TEST(SetupUtilTest, AdjustFromBelowNormalPriority) {
257 scoped_ptr<ScopedPriorityClass> below_normal =
258 ScopedPriorityClass::Create(BELOW_NORMAL_PRIORITY_CLASS);
259 ASSERT_TRUE(below_normal);
260 if (base::win::GetVersion() > base::win::VERSION_SERVER_2003)
261 EXPECT_EQ(PCCR_CHANGED, RelaunchAndDoProcessPriorityAdjustment());
262 else
263 EXPECT_EQ(PCCR_UNCHANGED, RelaunchAndDoProcessPriorityAdjustment());
266 namespace {
268 // A test fixture that configures an InstallationState and an InstallerState
269 // with a product being updated.
270 class FindArchiveToPatchTest : public SetupUtilTestWithDir {
271 protected:
272 class FakeInstallationState : public installer::InstallationState {
275 class FakeProductState : public installer::ProductState {
276 public:
277 static FakeProductState* FromProductState(const ProductState* product) {
278 return static_cast<FakeProductState*>(const_cast<ProductState*>(product));
281 void set_version(const Version& version) {
282 if (version.IsValid())
283 version_.reset(new Version(version));
284 else
285 version_.reset();
288 void set_uninstall_command(const base::CommandLine& uninstall_command) {
289 uninstall_command_ = uninstall_command;
293 void SetUp() override {
294 SetupUtilTestWithDir::SetUp();
295 product_version_ = Version("30.0.1559.0");
296 max_version_ = Version("47.0.1559.0");
298 // Install the product according to the version.
299 original_state_.reset(new FakeInstallationState());
300 InstallProduct();
302 // Prepare to update the product in the temp dir.
303 installer_state_.reset(new installer::InstallerState(
304 kSystemInstall_ ? installer::InstallerState::SYSTEM_LEVEL :
305 installer::InstallerState::USER_LEVEL));
306 installer_state_->AddProductFromState(
307 kProductType_,
308 *original_state_->GetProductState(kSystemInstall_, kProductType_));
310 // Create archives in the two version dirs.
311 ASSERT_TRUE(
312 base::CreateDirectory(GetProductVersionArchivePath().DirName()));
313 ASSERT_EQ(1, base::WriteFile(GetProductVersionArchivePath(), "a", 1));
314 ASSERT_TRUE(
315 base::CreateDirectory(GetMaxVersionArchivePath().DirName()));
316 ASSERT_EQ(1, base::WriteFile(GetMaxVersionArchivePath(), "b", 1));
319 void TearDown() override {
320 original_state_.reset();
321 SetupUtilTestWithDir::TearDown();
324 base::FilePath GetArchivePath(const Version& version) const {
325 return test_dir_.path()
326 .AppendASCII(version.GetString())
327 .Append(installer::kInstallerDir)
328 .Append(installer::kChromeArchive);
331 base::FilePath GetMaxVersionArchivePath() const {
332 return GetArchivePath(max_version_);
335 base::FilePath GetProductVersionArchivePath() const {
336 return GetArchivePath(product_version_);
339 void InstallProduct() {
340 FakeProductState* product = FakeProductState::FromProductState(
341 original_state_->GetNonVersionedProductState(kSystemInstall_,
342 kProductType_));
344 product->set_version(product_version_);
345 base::CommandLine uninstall_command(
346 test_dir_.path()
347 .AppendASCII(product_version_.GetString())
348 .Append(installer::kInstallerDir)
349 .Append(installer::kSetupExe));
350 uninstall_command.AppendSwitch(installer::switches::kUninstall);
351 product->set_uninstall_command(uninstall_command);
354 void UninstallProduct() {
355 FakeProductState::FromProductState(
356 original_state_->GetNonVersionedProductState(kSystemInstall_,
357 kProductType_))
358 ->set_version(Version());
361 static const bool kSystemInstall_;
362 static const BrowserDistribution::Type kProductType_;
363 Version product_version_;
364 Version max_version_;
365 scoped_ptr<FakeInstallationState> original_state_;
366 scoped_ptr<installer::InstallerState> installer_state_;
369 const bool FindArchiveToPatchTest::kSystemInstall_ = false;
370 const BrowserDistribution::Type FindArchiveToPatchTest::kProductType_ =
371 BrowserDistribution::CHROME_BROWSER;
373 } // namespace
375 // Test that the path to the advertised product version is found.
376 TEST_F(FindArchiveToPatchTest, ProductVersionFound) {
377 base::FilePath patch_source(installer::FindArchiveToPatch(
378 *original_state_, *installer_state_));
379 EXPECT_EQ(GetProductVersionArchivePath().value(), patch_source.value());
382 // Test that the path to the max version is found if the advertised version is
383 // missing.
384 TEST_F(FindArchiveToPatchTest, MaxVersionFound) {
385 // The patch file is absent.
386 ASSERT_TRUE(base::DeleteFile(GetProductVersionArchivePath(), false));
387 base::FilePath patch_source(installer::FindArchiveToPatch(
388 *original_state_, *installer_state_));
389 EXPECT_EQ(GetMaxVersionArchivePath().value(), patch_source.value());
391 // The product doesn't appear to be installed, so the max version is found.
392 UninstallProduct();
393 patch_source = installer::FindArchiveToPatch(
394 *original_state_, *installer_state_);
395 EXPECT_EQ(GetMaxVersionArchivePath().value(), patch_source.value());
398 // Test that an empty path is returned if no version is found.
399 TEST_F(FindArchiveToPatchTest, NoVersionFound) {
400 // The product doesn't appear to be installed and no archives are present.
401 UninstallProduct();
402 ASSERT_TRUE(base::DeleteFile(GetProductVersionArchivePath(), false));
403 ASSERT_TRUE(base::DeleteFile(GetMaxVersionArchivePath(), false));
405 base::FilePath patch_source(installer::FindArchiveToPatch(
406 *original_state_, *installer_state_));
407 EXPECT_EQ(base::FilePath::StringType(), patch_source.value());
410 namespace {
412 class MigrateMultiToSingleTest : public testing::Test {
413 protected:
414 void SetUp() override {
415 registry_override_manager_.OverrideRegistry(kRootKey);
418 static const bool kSystemLevel = false;
419 static const HKEY kRootKey;
420 static const wchar_t kVersionString[];
421 static const wchar_t kMultiChannel[];
422 registry_util::RegistryOverrideManager registry_override_manager_;
425 const bool MigrateMultiToSingleTest::kSystemLevel;
426 const HKEY MigrateMultiToSingleTest::kRootKey =
427 kSystemLevel ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
428 const wchar_t MigrateMultiToSingleTest::kVersionString[] = L"30.0.1574.0";
429 const wchar_t MigrateMultiToSingleTest::kMultiChannel[] =
430 L"2.0-dev-multi-chromeframe";
432 } // namespace
434 // Test migrating Chrome Frame from multi to single.
435 TEST_F(MigrateMultiToSingleTest, ChromeFrame) {
436 installer::ProductState chrome_frame;
437 installer::ProductState binaries;
438 DWORD usagestats = 0;
440 // Set up a config with dev-channel multi-install GCF.
441 base::win::RegKey key;
443 BrowserDistribution* dist = BrowserDistribution::GetSpecificDistribution(
444 BrowserDistribution::CHROME_BINARIES);
445 ASSERT_EQ(ERROR_SUCCESS,
446 base::win::RegKey(kRootKey, dist->GetVersionKey().c_str(),
447 KEY_SET_VALUE)
448 .WriteValue(google_update::kRegVersionField, kVersionString));
449 ASSERT_EQ(ERROR_SUCCESS,
450 base::win::RegKey(kRootKey, dist->GetStateKey().c_str(),
451 KEY_SET_VALUE)
452 .WriteValue(google_update::kRegApField, kMultiChannel));
453 ASSERT_EQ(ERROR_SUCCESS,
454 base::win::RegKey(kRootKey, dist->GetStateKey().c_str(),
455 KEY_SET_VALUE)
456 .WriteValue(google_update::kRegUsageStatsField, 1U));
458 dist = BrowserDistribution::GetSpecificDistribution(
459 BrowserDistribution::CHROME_FRAME);
460 ASSERT_EQ(ERROR_SUCCESS,
461 base::win::RegKey(kRootKey, dist->GetVersionKey().c_str(),
462 KEY_SET_VALUE)
463 .WriteValue(google_update::kRegVersionField, kVersionString));
464 ASSERT_EQ(ERROR_SUCCESS,
465 base::win::RegKey(kRootKey, dist->GetStateKey().c_str(),
466 KEY_SET_VALUE)
467 .WriteValue(google_update::kRegApField, kMultiChannel));
469 // Do the registry migration.
470 installer::InstallationState machine_state;
471 machine_state.Initialize();
473 installer::MigrateGoogleUpdateStateMultiToSingle(
474 kSystemLevel,
475 BrowserDistribution::CHROME_FRAME,
476 machine_state);
478 // Confirm that usagestats were copied to CF and that its channel was
479 // stripped.
480 ASSERT_TRUE(chrome_frame.Initialize(kSystemLevel,
481 BrowserDistribution::CHROME_FRAME));
482 EXPECT_TRUE(chrome_frame.GetUsageStats(&usagestats));
483 EXPECT_EQ(1U, usagestats);
484 EXPECT_EQ(L"2.0-dev", chrome_frame.channel().value());
486 // Confirm that the binaries' channel no longer contains GCF.
487 ASSERT_TRUE(binaries.Initialize(kSystemLevel,
488 BrowserDistribution::CHROME_BINARIES));
489 EXPECT_EQ(L"2.0-dev-multi", binaries.channel().value());
492 TEST(SetupUtilTest, ContainsUnsupportedSwitch) {
493 EXPECT_FALSE(installer::ContainsUnsupportedSwitch(
494 base::CommandLine::FromString(L"foo.exe")));
495 EXPECT_FALSE(installer::ContainsUnsupportedSwitch(
496 base::CommandLine::FromString(L"foo.exe --multi-install --chrome")));
497 EXPECT_TRUE(installer::ContainsUnsupportedSwitch(
498 base::CommandLine::FromString(L"foo.exe --chrome-frame")));
501 TEST(SetupUtilTest, GetRegistrationDataCommandKey) {
502 base::string16 app_guid = L"{AAAAAAAA-BBBB-1111-0123-456789ABCDEF}";
503 UpdatingAppRegistrationData reg_data(app_guid);
504 base::string16 key =
505 installer::GetRegistrationDataCommandKey(reg_data, L"test_name");
506 EXPECT_TRUE(base::EndsWith(key, app_guid + L"\\Commands\\test_name", true));