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