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 <windows.h>
6
7#include <fstream>
8
9#include "base/base_paths.h"
10#include "base/command_line.h"
11#include "base/files/file_enumerator.h"
12#include "base/files/file_path.h"
13#include "base/files/file_util.h"
14#include "base/files/scoped_temp_dir.h"
15#include "base/path_service.h"
16#include "base/strings/string_util.h"
17#include "base/strings/utf_string_conversions.h"
18#include "base/test/scoped_path_override.h"
19#include "base/test/test_reg_util_win.h"
20#include "base/version.h"
21#include "base/win/registry.h"
22#include "base/win/scoped_handle.h"
23#include "chrome/common/chrome_constants.h"
24#include "chrome/installer/test/alternate_version_generator.h"
25#include "chrome/installer/util/fake_installation_state.h"
26#include "chrome/installer/util/fake_product_state.h"
27#include "chrome/installer/util/google_update_constants.h"
28#include "chrome/installer/util/helper.h"
29#include "chrome/installer/util/installation_state.h"
30#include "chrome/installer/util/installer_state.h"
31#include "chrome/installer/util/master_preferences.h"
32#include "chrome/installer/util/product_unittest.h"
33#include "chrome/installer/util/util_constants.h"
34#include "chrome/installer/util/work_item.h"
35#include "testing/gtest/include/gtest/gtest.h"
36
37#include "installer_util_strings.h"  // NOLINT
38
39using base::win::RegKey;
40using installer::InstallationState;
41using installer::InstallerState;
42using installer::MasterPreferences;
43using registry_util::RegistryOverrideManager;
44
45class InstallerStateTest : public TestWithTempDirAndDeleteTempOverrideKeys {
46 protected:
47};
48
49// An installer state on which we can access otherwise protected members.
50class MockInstallerState : public InstallerState {
51 public:
52  MockInstallerState() : InstallerState() { }
53  void set_target_path(const base::FilePath& target_path) {
54    target_path_ = target_path;
55  }
56  static bool IsFileInUse(const base::FilePath& file) {
57    return InstallerState::IsFileInUse(file);
58  }
59  const Version& critical_update_version() const {
60    return critical_update_version_;
61  }
62  void GetExistingExeVersions(std::set<std::string>* existing_version_strings) {
63    return InstallerState::GetExistingExeVersions(existing_version_strings);
64  }
65};
66
67// Simple function to dump some text into a new file.
68void CreateTextFile(const std::wstring& filename,
69                    const std::wstring& contents) {
70  std::ofstream file;
71  file.open(filename.c_str());
72  ASSERT_TRUE(file.is_open());
73  file << contents;
74  file.close();
75}
76
77void BuildSingleChromeState(const base::FilePath& target_dir,
78                            MockInstallerState* installer_state) {
79  CommandLine cmd_line = CommandLine::FromString(L"setup.exe");
80  MasterPreferences prefs(cmd_line);
81  InstallationState machine_state;
82  machine_state.Initialize();
83  installer_state->Initialize(cmd_line, prefs, machine_state);
84  installer_state->set_target_path(target_dir);
85  EXPECT_TRUE(installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER)
86      != NULL);
87}
88
89wchar_t text_content_1[] = L"delete me";
90wchar_t text_content_2[] = L"delete me as well";
91
92// Delete version directories. Everything lower than the given version
93// should be deleted.
94TEST_F(InstallerStateTest, Delete) {
95  // TODO(grt): move common stuff into the test fixture.
96  // Create a Chrome dir
97  base::FilePath chrome_dir(test_dir_.path());
98  chrome_dir = chrome_dir.AppendASCII("chrome");
99  base::CreateDirectory(chrome_dir);
100  ASSERT_TRUE(base::PathExists(chrome_dir));
101
102  base::FilePath chrome_dir_1(chrome_dir);
103  chrome_dir_1 = chrome_dir_1.AppendASCII("1.0.1.0");
104  base::CreateDirectory(chrome_dir_1);
105  ASSERT_TRUE(base::PathExists(chrome_dir_1));
106
107  base::FilePath chrome_dir_2(chrome_dir);
108  chrome_dir_2 = chrome_dir_2.AppendASCII("1.0.2.0");
109  base::CreateDirectory(chrome_dir_2);
110  ASSERT_TRUE(base::PathExists(chrome_dir_2));
111
112  base::FilePath chrome_dir_3(chrome_dir);
113  chrome_dir_3 = chrome_dir_3.AppendASCII("1.0.3.0");
114  base::CreateDirectory(chrome_dir_3);
115  ASSERT_TRUE(base::PathExists(chrome_dir_3));
116
117  base::FilePath chrome_dir_4(chrome_dir);
118  chrome_dir_4 = chrome_dir_4.AppendASCII("1.0.4.0");
119  base::CreateDirectory(chrome_dir_4);
120  ASSERT_TRUE(base::PathExists(chrome_dir_4));
121
122  base::FilePath chrome_dll_1(chrome_dir_1);
123  chrome_dll_1 = chrome_dll_1.AppendASCII("chrome.dll");
124  CreateTextFile(chrome_dll_1.value(), text_content_1);
125  ASSERT_TRUE(base::PathExists(chrome_dll_1));
126
127  base::FilePath chrome_dll_2(chrome_dir_2);
128  chrome_dll_2 = chrome_dll_2.AppendASCII("chrome.dll");
129  CreateTextFile(chrome_dll_2.value(), text_content_1);
130  ASSERT_TRUE(base::PathExists(chrome_dll_2));
131
132  base::FilePath chrome_dll_3(chrome_dir_3);
133  chrome_dll_3 = chrome_dll_3.AppendASCII("chrome.dll");
134  CreateTextFile(chrome_dll_3.value(), text_content_1);
135  ASSERT_TRUE(base::PathExists(chrome_dll_3));
136
137  base::FilePath chrome_dll_4(chrome_dir_4);
138  chrome_dll_4 = chrome_dll_4.AppendASCII("chrome.dll");
139  CreateTextFile(chrome_dll_4.value(), text_content_1);
140  ASSERT_TRUE(base::PathExists(chrome_dll_4));
141
142  MockInstallerState installer_state;
143  BuildSingleChromeState(chrome_dir, &installer_state);
144  Version latest_version("1.0.4.0");
145  {
146    base::ScopedTempDir temp_dir;
147    ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
148    installer_state.RemoveOldVersionDirectories(latest_version, NULL,
149                                                temp_dir.path());
150  }
151
152  // old versions should be gone
153  EXPECT_FALSE(base::PathExists(chrome_dir_1));
154  EXPECT_FALSE(base::PathExists(chrome_dir_2));
155  EXPECT_FALSE(base::PathExists(chrome_dir_3));
156  // the latest version should stay
157  EXPECT_TRUE(base::PathExists(chrome_dll_4));
158}
159
160// Delete older version directories, keeping the one in used intact.
161TEST_F(InstallerStateTest, DeleteInUsed) {
162  // Create a Chrome dir
163  base::FilePath chrome_dir(test_dir_.path());
164  chrome_dir = chrome_dir.AppendASCII("chrome");
165  base::CreateDirectory(chrome_dir);
166  ASSERT_TRUE(base::PathExists(chrome_dir));
167
168  base::FilePath chrome_dir_1(chrome_dir);
169  chrome_dir_1 = chrome_dir_1.AppendASCII("1.0.1.0");
170  base::CreateDirectory(chrome_dir_1);
171  ASSERT_TRUE(base::PathExists(chrome_dir_1));
172
173  base::FilePath chrome_dir_2(chrome_dir);
174  chrome_dir_2 = chrome_dir_2.AppendASCII("1.0.2.0");
175  base::CreateDirectory(chrome_dir_2);
176  ASSERT_TRUE(base::PathExists(chrome_dir_2));
177
178  base::FilePath chrome_dir_3(chrome_dir);
179  chrome_dir_3 = chrome_dir_3.AppendASCII("1.0.3.0");
180  base::CreateDirectory(chrome_dir_3);
181  ASSERT_TRUE(base::PathExists(chrome_dir_3));
182
183  base::FilePath chrome_dir_4(chrome_dir);
184  chrome_dir_4 = chrome_dir_4.AppendASCII("1.0.4.0");
185  base::CreateDirectory(chrome_dir_4);
186  ASSERT_TRUE(base::PathExists(chrome_dir_4));
187
188  base::FilePath chrome_dll_1(chrome_dir_1);
189  chrome_dll_1 = chrome_dll_1.AppendASCII("chrome.dll");
190  CreateTextFile(chrome_dll_1.value(), text_content_1);
191  ASSERT_TRUE(base::PathExists(chrome_dll_1));
192
193  base::FilePath chrome_dll_2(chrome_dir_2);
194  chrome_dll_2 = chrome_dll_2.AppendASCII("chrome.dll");
195  CreateTextFile(chrome_dll_2.value(), text_content_1);
196  ASSERT_TRUE(base::PathExists(chrome_dll_2));
197
198  // Open the file to make it in use.
199  std::ofstream file;
200  file.open(chrome_dll_2.value().c_str());
201
202  base::FilePath chrome_othera_2(chrome_dir_2);
203  chrome_othera_2 = chrome_othera_2.AppendASCII("othera.dll");
204  CreateTextFile(chrome_othera_2.value(), text_content_2);
205  ASSERT_TRUE(base::PathExists(chrome_othera_2));
206
207  base::FilePath chrome_otherb_2(chrome_dir_2);
208  chrome_otherb_2 = chrome_otherb_2.AppendASCII("otherb.dll");
209  CreateTextFile(chrome_otherb_2.value(), text_content_2);
210  ASSERT_TRUE(base::PathExists(chrome_otherb_2));
211
212  base::FilePath chrome_dll_3(chrome_dir_3);
213  chrome_dll_3 = chrome_dll_3.AppendASCII("chrome.dll");
214  CreateTextFile(chrome_dll_3.value(), text_content_1);
215  ASSERT_TRUE(base::PathExists(chrome_dll_3));
216
217  base::FilePath chrome_dll_4(chrome_dir_4);
218  chrome_dll_4 = chrome_dll_4.AppendASCII("chrome.dll");
219  CreateTextFile(chrome_dll_4.value(), text_content_1);
220  ASSERT_TRUE(base::PathExists(chrome_dll_4));
221
222  MockInstallerState installer_state;
223  BuildSingleChromeState(chrome_dir, &installer_state);
224  Version latest_version("1.0.4.0");
225  Version existing_version("1.0.1.0");
226  {
227    base::ScopedTempDir temp_dir;
228    ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
229    installer_state.RemoveOldVersionDirectories(latest_version,
230                                                &existing_version,
231                                                temp_dir.path());
232  }
233
234  // the version defined as the existing version should stay
235  EXPECT_TRUE(base::PathExists(chrome_dir_1));
236  // old versions not in used should be gone
237  EXPECT_FALSE(base::PathExists(chrome_dir_3));
238  // every thing under in used version should stay
239  EXPECT_TRUE(base::PathExists(chrome_dir_2));
240  EXPECT_TRUE(base::PathExists(chrome_dll_2));
241  EXPECT_TRUE(base::PathExists(chrome_othera_2));
242  EXPECT_TRUE(base::PathExists(chrome_otherb_2));
243  // the latest version should stay
244  EXPECT_TRUE(base::PathExists(chrome_dll_4));
245}
246
247// Tests a few basic things of the Package class.  Makes sure that the path
248// operations are correct
249TEST_F(InstallerStateTest, Basic) {
250  const bool multi_install = false;
251  const bool system_level = true;
252  CommandLine cmd_line = CommandLine::FromString(
253      std::wstring(L"setup.exe") +
254      (multi_install ? L" --multi-install --chrome" : L"") +
255      (system_level ? L" --system-level" : L""));
256  MasterPreferences prefs(cmd_line);
257  InstallationState machine_state;
258  machine_state.Initialize();
259  MockInstallerState installer_state;
260  installer_state.Initialize(cmd_line, prefs, machine_state);
261  installer_state.set_target_path(test_dir_.path());
262  EXPECT_EQ(test_dir_.path().value(), installer_state.target_path().value());
263  EXPECT_EQ(1U, installer_state.products().size());
264
265  const char kOldVersion[] = "1.2.3.4";
266  const char kNewVersion[] = "2.3.4.5";
267
268  Version new_version(kNewVersion);
269  Version old_version(kOldVersion);
270  ASSERT_TRUE(new_version.IsValid());
271  ASSERT_TRUE(old_version.IsValid());
272
273  base::FilePath installer_dir(
274      installer_state.GetInstallerDirectory(new_version));
275  EXPECT_FALSE(installer_dir.empty());
276
277  base::FilePath new_version_dir(installer_state.target_path().Append(
278      base::UTF8ToWide(new_version.GetString())));
279  base::FilePath old_version_dir(installer_state.target_path().Append(
280      base::UTF8ToWide(old_version.GetString())));
281
282  EXPECT_FALSE(base::PathExists(new_version_dir));
283  EXPECT_FALSE(base::PathExists(old_version_dir));
284
285  EXPECT_FALSE(base::PathExists(installer_dir));
286  base::CreateDirectory(installer_dir);
287  EXPECT_TRUE(base::PathExists(new_version_dir));
288
289  base::CreateDirectory(old_version_dir);
290  EXPECT_TRUE(base::PathExists(old_version_dir));
291
292  // Create a fake chrome.dll key file in the old version directory.  This
293  // should prevent the old version directory from getting deleted.
294  base::FilePath old_chrome_dll(old_version_dir.Append(installer::kChromeDll));
295  EXPECT_FALSE(base::PathExists(old_chrome_dll));
296
297  // Hold on to the file exclusively to prevent the directory from
298  // being deleted.
299  base::win::ScopedHandle file(
300    ::CreateFile(old_chrome_dll.value().c_str(), GENERIC_READ,
301                 0, NULL, OPEN_ALWAYS, 0, NULL));
302  EXPECT_TRUE(file.IsValid());
303  EXPECT_TRUE(base::PathExists(old_chrome_dll));
304
305  base::ScopedTempDir temp_dir;
306  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
307
308  // Don't explicitly tell the directory cleanup logic not to delete the
309  // old version, rely on the key files to keep it around.
310  installer_state.RemoveOldVersionDirectories(new_version,
311                                              NULL,
312                                              temp_dir.path());
313
314  // The old directory should still exist.
315  EXPECT_TRUE(base::PathExists(old_version_dir));
316  EXPECT_TRUE(base::PathExists(new_version_dir));
317
318  // Now close the file handle to make it possible to delete our key file.
319  file.Close();
320
321  installer_state.RemoveOldVersionDirectories(new_version,
322                                              NULL,
323                                              temp_dir.path());
324  // The new directory should still exist.
325  EXPECT_TRUE(base::PathExists(new_version_dir));
326
327  // Now, the old directory and key file should be gone.
328  EXPECT_FALSE(base::PathExists(old_chrome_dll));
329  EXPECT_FALSE(base::PathExists(old_version_dir));
330}
331
332TEST_F(InstallerStateTest, WithProduct) {
333  const bool multi_install = false;
334  const bool system_level = true;
335  CommandLine cmd_line = CommandLine::FromString(
336      std::wstring(L"setup.exe") +
337      (multi_install ? L" --multi-install --chrome" : L"") +
338      (system_level ? L" --system-level" : L""));
339  MasterPreferences prefs(cmd_line);
340  InstallationState machine_state;
341  machine_state.Initialize();
342  MockInstallerState installer_state;
343  installer_state.Initialize(cmd_line, prefs, machine_state);
344  installer_state.set_target_path(test_dir_.path());
345  EXPECT_EQ(1U, installer_state.products().size());
346  EXPECT_EQ(system_level, installer_state.system_install());
347
348  const char kCurrentVersion[] = "1.2.3.4";
349  Version current_version(kCurrentVersion);
350
351  HKEY root = system_level ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
352  EXPECT_EQ(root, installer_state.root_key());
353
354  {
355    RegistryOverrideManager override_manager;
356    override_manager.OverrideRegistry(root);
357    BrowserDistribution* dist = BrowserDistribution::GetSpecificDistribution(
358        BrowserDistribution::CHROME_BROWSER);
359    RegKey chrome_key(root, dist->GetVersionKey().c_str(), KEY_ALL_ACCESS);
360    EXPECT_TRUE(chrome_key.Valid());
361    if (chrome_key.Valid()) {
362      chrome_key.WriteValue(google_update::kRegVersionField,
363                            base::UTF8ToWide(
364                                current_version.GetString()).c_str());
365      machine_state.Initialize();
366      // TODO(tommi): Also test for when there exists a new_chrome.exe.
367      Version found_version(*installer_state.GetCurrentVersion(machine_state));
368      EXPECT_TRUE(found_version.IsValid());
369      if (found_version.IsValid())
370        EXPECT_TRUE(current_version.Equals(found_version));
371    }
372  }
373}
374
375TEST_F(InstallerStateTest, InstallerResult) {
376  const bool system_level = true;
377  bool multi_install = false;
378  HKEY root = system_level ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
379
380  RegKey key;
381  std::wstring launch_cmd = L"hey diddle diddle";
382  std::wstring value;
383  DWORD dw_value;
384
385  // check results for a fresh install of single Chrome
386  {
387    RegistryOverrideManager override_manager;
388    override_manager.OverrideRegistry(root);
389    CommandLine cmd_line = CommandLine::FromString(L"setup.exe --system-level");
390    const MasterPreferences prefs(cmd_line);
391    InstallationState machine_state;
392    machine_state.Initialize();
393    InstallerState state;
394    state.Initialize(cmd_line, prefs, machine_state);
395    state.WriteInstallerResult(installer::FIRST_INSTALL_SUCCESS,
396                               IDS_INSTALL_OS_ERROR_BASE, &launch_cmd);
397    BrowserDistribution* distribution =
398        BrowserDistribution::GetSpecificDistribution(
399            BrowserDistribution::CHROME_BROWSER);
400    EXPECT_EQ(ERROR_SUCCESS,
401        key.Open(root, distribution->GetStateKey().c_str(), KEY_READ));
402    EXPECT_EQ(ERROR_SUCCESS,
403        key.ReadValueDW(installer::kInstallerResult, &dw_value));
404    EXPECT_EQ(static_cast<DWORD>(0), dw_value);
405    EXPECT_EQ(ERROR_SUCCESS,
406        key.ReadValueDW(installer::kInstallerError, &dw_value));
407    EXPECT_EQ(static_cast<DWORD>(installer::FIRST_INSTALL_SUCCESS), dw_value);
408    EXPECT_EQ(ERROR_SUCCESS,
409        key.ReadValue(installer::kInstallerResultUIString, &value));
410    EXPECT_FALSE(value.empty());
411    EXPECT_EQ(ERROR_SUCCESS,
412        key.ReadValue(installer::kInstallerSuccessLaunchCmdLine, &value));
413    EXPECT_EQ(launch_cmd, value);
414  }
415
416  // check results for a fresh install of multi Chrome
417  {
418    RegistryOverrideManager override_manager;
419    override_manager.OverrideRegistry(root);
420    CommandLine cmd_line = CommandLine::FromString(
421        L"setup.exe --system-level --multi-install --chrome");
422    const MasterPreferences prefs(cmd_line);
423    InstallationState machine_state;
424    machine_state.Initialize();
425    InstallerState state;
426    state.Initialize(cmd_line, prefs, machine_state);
427    state.WriteInstallerResult(installer::FIRST_INSTALL_SUCCESS, 0,
428                               &launch_cmd);
429    BrowserDistribution* distribution =
430        BrowserDistribution::GetSpecificDistribution(
431            BrowserDistribution::CHROME_BROWSER);
432    BrowserDistribution* binaries =
433        BrowserDistribution::GetSpecificDistribution(
434            BrowserDistribution::CHROME_BINARIES);
435    EXPECT_EQ(ERROR_SUCCESS,
436        key.Open(root, distribution->GetStateKey().c_str(), KEY_READ));
437    EXPECT_EQ(ERROR_SUCCESS,
438        key.ReadValue(installer::kInstallerSuccessLaunchCmdLine, &value));
439    EXPECT_EQ(launch_cmd, value);
440    EXPECT_EQ(ERROR_SUCCESS,
441        key.Open(root, binaries->GetStateKey().c_str(), KEY_READ));
442    EXPECT_EQ(ERROR_SUCCESS,
443        key.ReadValue(installer::kInstallerSuccessLaunchCmdLine, &value));
444    EXPECT_EQ(launch_cmd, value);
445    key.Close();
446  }
447}
448
449// Test GetCurrentVersion when migrating single Chrome to multi
450TEST_F(InstallerStateTest, GetCurrentVersionMigrateChrome) {
451  using installer::FakeInstallationState;
452
453  const bool system_install = false;
454  FakeInstallationState machine_state;
455
456  // Pretend that this version of single-install Chrome is already installed.
457  machine_state.AddChrome(system_install, false,
458                          new Version(chrome::kChromeVersion));
459
460  // Now we're invoked to install multi Chrome.
461  CommandLine cmd_line(
462      CommandLine::FromString(L"setup.exe --multi-install --chrome"));
463  MasterPreferences prefs(cmd_line);
464  InstallerState installer_state;
465  installer_state.Initialize(cmd_line, prefs, machine_state);
466
467  // Is the Chrome version picked up?
468  scoped_ptr<Version> version(installer_state.GetCurrentVersion(machine_state));
469  EXPECT_TRUE(version.get() != NULL);
470}
471
472TEST_F(InstallerStateTest, IsFileInUse) {
473  base::ScopedTempDir temp_dir;
474  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
475
476  base::FilePath temp_file;
477  ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir.path(), &temp_file));
478
479  EXPECT_FALSE(MockInstallerState::IsFileInUse(temp_file));
480
481  {
482    // Open a handle to the file with the same access mode and sharing options
483    // as the loader.
484    base::win::ScopedHandle temp_handle(
485        CreateFile(temp_file.value().c_str(),
486                   SYNCHRONIZE | FILE_EXECUTE,
487                   FILE_SHARE_DELETE | FILE_SHARE_READ,
488                   NULL, OPEN_EXISTING, 0, 0));
489    ASSERT_TRUE(temp_handle.IsValid());
490
491    // The file should now be in use.
492    EXPECT_TRUE(MockInstallerState::IsFileInUse(temp_file));
493  }
494
495  // And once the handle is gone, it should no longer be in use.
496  EXPECT_FALSE(MockInstallerState::IsFileInUse(temp_file));
497}
498
499TEST_F(InstallerStateTest, RemoveOldVersionDirs) {
500  MockInstallerState installer_state;
501  installer_state.set_target_path(test_dir_.path());
502  EXPECT_EQ(test_dir_.path().value(), installer_state.target_path().value());
503
504  const char kOldVersion[] = "2.0.0.0";
505  const char kNewVersion[] = "3.0.0.0";
506  const char kOldChromeExeVersion[] = "2.1.0.0";
507  const char kChromeExeVersion[] = "2.1.1.1";
508  const char kNewChromeExeVersion[] = "3.0.0.0";
509
510  Version new_version(kNewVersion);
511  Version old_version(kOldVersion);
512  Version old_chrome_exe_version(kOldChromeExeVersion);
513  Version chrome_exe_version(kChromeExeVersion);
514  Version new_chrome_exe_version(kNewChromeExeVersion);
515
516  ASSERT_TRUE(new_version.IsValid());
517  ASSERT_TRUE(old_version.IsValid());
518  ASSERT_TRUE(old_chrome_exe_version.IsValid());
519  ASSERT_TRUE(chrome_exe_version.IsValid());
520  ASSERT_TRUE(new_chrome_exe_version.IsValid());
521
522  // Set up a bunch of version dir paths.
523  base::FilePath version_dirs[] = {
524    installer_state.target_path().Append(L"1.2.3.4"),
525    installer_state.target_path().Append(L"1.2.3.5"),
526    installer_state.target_path().Append(L"1.2.3.6"),
527    installer_state.target_path().Append(base::ASCIIToWide(kOldVersion)),
528    installer_state.target_path().Append(
529        base::ASCIIToWide(kOldChromeExeVersion)),
530    installer_state.target_path().Append(L"2.1.1.0"),
531    installer_state.target_path().Append(base::ASCIIToWide(kChromeExeVersion)),
532    installer_state.target_path().Append(base::ASCIIToWide(kNewVersion)),
533    installer_state.target_path().Append(L"3.9.1.1"),
534  };
535
536  // Create the version directories.
537  for (int i = 0; i < arraysize(version_dirs); i++) {
538    base::CreateDirectory(version_dirs[i]);
539    EXPECT_TRUE(base::PathExists(version_dirs[i]));
540  }
541
542  // Create exes with the appropriate version resource.
543  // Use the current test exe as a baseline.
544  base::FilePath exe_path;
545  ASSERT_TRUE(PathService::Get(base::FILE_EXE, &exe_path));
546
547  struct target_info {
548    base::FilePath target_file;
549    const Version& target_version;
550  } targets[] = {
551    { installer_state.target_path().Append(installer::kChromeOldExe),
552      old_chrome_exe_version },
553    { installer_state.target_path().Append(installer::kChromeExe),
554      chrome_exe_version },
555    { installer_state.target_path().Append(installer::kChromeNewExe),
556      new_chrome_exe_version },
557  };
558  for (int i = 0; i < arraysize(targets); ++i) {
559    ASSERT_TRUE(upgrade_test::GenerateSpecificPEFileVersion(
560        exe_path, targets[i].target_file, targets[i].target_version));
561  }
562
563  // Call GetExistingExeVersions, validate that picks up the
564  // exe resources.
565  std::set<std::string> expected_exe_versions;
566  expected_exe_versions.insert(kOldChromeExeVersion);
567  expected_exe_versions.insert(kChromeExeVersion);
568  expected_exe_versions.insert(kNewChromeExeVersion);
569
570  std::set<std::string> actual_exe_versions;
571  installer_state.GetExistingExeVersions(&actual_exe_versions);
572  EXPECT_EQ(expected_exe_versions, actual_exe_versions);
573
574  // Call RemoveOldVersionDirectories
575  installer_state.RemoveOldVersionDirectories(new_version,
576                                              &old_version,
577                                              installer_state.target_path());
578
579  // What we expect to have left.
580  std::set<std::string> expected_remaining_dirs;
581  expected_remaining_dirs.insert(kOldVersion);
582  expected_remaining_dirs.insert(kNewVersion);
583  expected_remaining_dirs.insert(kOldChromeExeVersion);
584  expected_remaining_dirs.insert(kChromeExeVersion);
585  expected_remaining_dirs.insert(kNewChromeExeVersion);
586
587  // Enumerate dirs in target_path(), ensure only desired remain.
588  base::FileEnumerator version_enum(installer_state.target_path(), false,
589                                    base::FileEnumerator::DIRECTORIES);
590  for (base::FilePath next_version = version_enum.Next(); !next_version.empty();
591       next_version = version_enum.Next()) {
592    base::FilePath dir_name(next_version.BaseName());
593    Version version(base::UTF16ToASCII(dir_name.value()));
594    if (version.IsValid()) {
595      EXPECT_TRUE(expected_remaining_dirs.erase(version.GetString()))
596          << "Unexpected version dir found: " << version.GetString();
597    }
598  }
599
600  std::set<std::string>::const_iterator iter(
601      expected_remaining_dirs.begin());
602  for (; iter != expected_remaining_dirs.end(); ++iter)
603    ADD_FAILURE() << "Expected to find version dir for " << *iter;
604}
605
606TEST_F(InstallerStateTest, InitializeTwice) {
607  // Override these paths so that they can be found after the registry override
608  // manager is in place.
609  base::FilePath temp;
610  PathService::Get(base::DIR_PROGRAM_FILES, &temp);
611  base::ScopedPathOverride program_files_override(base::DIR_PROGRAM_FILES,
612                                                  temp);
613  PathService::Get(base::DIR_PROGRAM_FILESX86, &temp);
614  base::ScopedPathOverride program_filesx86_override(base::DIR_PROGRAM_FILESX86,
615                                                     temp);
616  PathService::Get(base::DIR_LOCAL_APP_DATA, &temp);
617  base::ScopedPathOverride local_app_data_override(base::DIR_LOCAL_APP_DATA,
618                                                   temp);
619  registry_util::RegistryOverrideManager override_manager;
620  override_manager.OverrideRegistry(HKEY_CURRENT_USER);
621  override_manager.OverrideRegistry(HKEY_LOCAL_MACHINE);
622
623  InstallationState machine_state;
624  machine_state.Initialize();
625
626  InstallerState installer_state;
627
628  // Initialize the instance to install multi Chrome.
629  {
630    CommandLine cmd_line(
631        CommandLine::FromString(L"setup.exe --multi-install --chrome"));
632    MasterPreferences prefs(cmd_line);
633    installer_state.Initialize(cmd_line, prefs, machine_state);
634  }
635  // Confirm the expected state.
636  EXPECT_EQ(InstallerState::USER_LEVEL, installer_state.level());
637  EXPECT_EQ(InstallerState::MULTI_PACKAGE, installer_state.package_type());
638  EXPECT_EQ(InstallerState::MULTI_INSTALL, installer_state.operation());
639  EXPECT_TRUE(wcsstr(installer_state.target_path().value().c_str(),
640                     BrowserDistribution::GetSpecificDistribution(
641                         BrowserDistribution::CHROME_BINARIES)->
642                         GetInstallSubDir().c_str()));
643  EXPECT_FALSE(installer_state.verbose_logging());
644  EXPECT_EQ(installer_state.state_key(),
645            BrowserDistribution::GetSpecificDistribution(
646                BrowserDistribution::CHROME_BROWSER)->GetStateKey());
647  EXPECT_EQ(installer_state.state_type(), BrowserDistribution::CHROME_BROWSER);
648  EXPECT_TRUE(installer_state.multi_package_binaries_distribution());
649  EXPECT_TRUE(installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER));
650
651  // Now initialize it to install system-level single Chrome.
652  {
653    CommandLine cmd_line(
654        CommandLine::FromString(L"setup.exe --system-level --verbose-logging"));
655    MasterPreferences prefs(cmd_line);
656    installer_state.Initialize(cmd_line, prefs, machine_state);
657  }
658
659  // Confirm that the old state is gone.
660  EXPECT_EQ(InstallerState::SYSTEM_LEVEL, installer_state.level());
661  EXPECT_EQ(InstallerState::SINGLE_PACKAGE, installer_state.package_type());
662  EXPECT_EQ(InstallerState::SINGLE_INSTALL_OR_UPDATE,
663            installer_state.operation());
664  EXPECT_TRUE(wcsstr(installer_state.target_path().value().c_str(),
665                     BrowserDistribution::GetSpecificDistribution(
666                         BrowserDistribution::CHROME_BROWSER)->
667                         GetInstallSubDir().c_str()));
668  EXPECT_TRUE(installer_state.verbose_logging());
669  EXPECT_EQ(installer_state.state_key(),
670            BrowserDistribution::GetSpecificDistribution(
671                BrowserDistribution::CHROME_BROWSER)->GetStateKey());
672  EXPECT_EQ(installer_state.state_type(), BrowserDistribution::CHROME_BROWSER);
673  EXPECT_TRUE(installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER));
674}
675
676// A fixture for testing InstallerState::DetermineCriticalVersion.  Individual
677// tests must invoke Initialize() with a critical version.
678class InstallerStateCriticalVersionTest : public ::testing::Test {
679 protected:
680  InstallerStateCriticalVersionTest() : cmd_line_(CommandLine::NO_PROGRAM) {}
681
682  // Creates a set of versions for use by all test runs.
683  static void SetUpTestCase() {
684    low_version_    = new Version("15.0.874.106");
685    opv_version_    = new Version("15.0.874.255");
686    middle_version_ = new Version("16.0.912.32");
687    pv_version_     = new Version("16.0.912.255");
688    high_version_   = new Version("17.0.932.0");
689  }
690
691  // Cleans up versions used by all test runs.
692  static void TearDownTestCase() {
693    delete low_version_;
694    delete opv_version_;
695    delete middle_version_;
696    delete pv_version_;
697    delete high_version_;
698  }
699
700  // Initializes the InstallerState to use for a test run.  The returned
701  // instance's critical update version is set to |version|.  |version| may be
702  // NULL, in which case the critical update version is unset.
703  MockInstallerState& Initialize(const Version* version) {
704    cmd_line_ = version == NULL ?
705        CommandLine::FromString(L"setup.exe") :
706        CommandLine::FromString(
707            L"setup.exe --critical-update-version=" +
708            base::ASCIIToWide(version->GetString()));
709    prefs_.reset(new MasterPreferences(cmd_line_));
710    machine_state_.Initialize();
711    installer_state_.Initialize(cmd_line_, *prefs_, machine_state_);
712    return installer_state_;
713  }
714
715  static Version* low_version_;
716  static Version* opv_version_;
717  static Version* middle_version_;
718  static Version* pv_version_;
719  static Version* high_version_;
720
721  CommandLine cmd_line_;
722  scoped_ptr<MasterPreferences> prefs_;
723  InstallationState machine_state_;
724  MockInstallerState installer_state_;
725};
726
727Version* InstallerStateCriticalVersionTest::low_version_ = NULL;
728Version* InstallerStateCriticalVersionTest::opv_version_ = NULL;
729Version* InstallerStateCriticalVersionTest::middle_version_ = NULL;
730Version* InstallerStateCriticalVersionTest::pv_version_ = NULL;
731Version* InstallerStateCriticalVersionTest::high_version_ = NULL;
732
733// Test the case where the critical version is less than the currently-running
734// Chrome.  The critical version is ignored since it doesn't apply.
735TEST_F(InstallerStateCriticalVersionTest, CriticalBeforeOpv) {
736  MockInstallerState& installer_state(Initialize(low_version_));
737
738  EXPECT_TRUE(installer_state.critical_update_version().Equals(*low_version_));
739  // Unable to determine the installed version, so assume critical update.
740  EXPECT_TRUE(
741      installer_state.DetermineCriticalVersion(NULL, *pv_version_).IsValid());
742  // Installed version is past the critical update.
743  EXPECT_FALSE(
744      installer_state.DetermineCriticalVersion(opv_version_, *pv_version_)
745          .IsValid());
746  // Installed version is past the critical update.
747  EXPECT_FALSE(
748      installer_state.DetermineCriticalVersion(pv_version_, *pv_version_)
749          .IsValid());
750}
751
752// Test the case where the critical version is equal to the currently-running
753// Chrome.  The critical version is ignored since it doesn't apply.
754TEST_F(InstallerStateCriticalVersionTest, CriticalEqualsOpv) {
755  MockInstallerState& installer_state(Initialize(opv_version_));
756
757  EXPECT_TRUE(installer_state.critical_update_version().Equals(*opv_version_));
758  // Unable to determine the installed version, so assume critical update.
759  EXPECT_TRUE(
760      installer_state.DetermineCriticalVersion(NULL, *pv_version_).IsValid());
761  // Installed version equals the critical update.
762  EXPECT_FALSE(
763      installer_state.DetermineCriticalVersion(opv_version_, *pv_version_)
764          .IsValid());
765  // Installed version equals the critical update.
766  EXPECT_FALSE(
767      installer_state.DetermineCriticalVersion(pv_version_, *pv_version_)
768          .IsValid());
769}
770
771// Test the case where the critical version is between the currently-running
772// Chrome and the to-be-installed Chrome.
773TEST_F(InstallerStateCriticalVersionTest, CriticalBetweenOpvAndPv) {
774  MockInstallerState& installer_state(Initialize(middle_version_));
775
776  EXPECT_TRUE(installer_state.critical_update_version().Equals(
777      *middle_version_));
778  // Unable to determine the installed version, so assume critical update.
779  EXPECT_TRUE(
780      installer_state.DetermineCriticalVersion(NULL, *pv_version_).IsValid());
781  // Installed version before the critical update.
782  EXPECT_TRUE(
783      installer_state.DetermineCriticalVersion(opv_version_, *pv_version_)
784          .IsValid());
785  // Installed version is past the critical update.
786  EXPECT_FALSE(
787      installer_state.DetermineCriticalVersion(pv_version_, *pv_version_)
788          .IsValid());
789}
790
791// Test the case where the critical version is the same as the to-be-installed
792// Chrome.
793TEST_F(InstallerStateCriticalVersionTest, CriticalEqualsPv) {
794  MockInstallerState& installer_state(Initialize(pv_version_));
795
796  EXPECT_TRUE(installer_state.critical_update_version().Equals(
797      *pv_version_));
798  // Unable to determine the installed version, so assume critical update.
799  EXPECT_TRUE(
800      installer_state.DetermineCriticalVersion(NULL, *pv_version_).IsValid());
801  // Installed version before the critical update.
802  EXPECT_TRUE(
803      installer_state.DetermineCriticalVersion(opv_version_, *pv_version_)
804          .IsValid());
805  // Installed version equals the critical update.
806  EXPECT_FALSE(
807      installer_state.DetermineCriticalVersion(pv_version_, *pv_version_)
808          .IsValid());
809}
810
811// Test the case where the critical version is greater than the to-be-installed
812// Chrome.
813TEST_F(InstallerStateCriticalVersionTest, CriticalAfterPv) {
814  MockInstallerState& installer_state(Initialize(high_version_));
815
816  EXPECT_TRUE(installer_state.critical_update_version().Equals(
817      *high_version_));
818  // Critical update newer than the new version.
819  EXPECT_FALSE(
820      installer_state.DetermineCriticalVersion(NULL, *pv_version_).IsValid());
821  EXPECT_FALSE(
822      installer_state.DetermineCriticalVersion(opv_version_, *pv_version_)
823          .IsValid());
824  EXPECT_FALSE(
825      installer_state.DetermineCriticalVersion(pv_version_, *pv_version_)
826          .IsValid());
827}
828