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