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