installer_state_unittest.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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  EXPECT_TRUE(installer_state->FindProduct(BrowserDistribution::CHROME_FRAME)
87      == NULL);
88}
89
90wchar_t text_content_1[] = L"delete me";
91wchar_t text_content_2[] = L"delete me as well";
92
93// Delete version directories. Everything lower than the given version
94// should be deleted.
95TEST_F(InstallerStateTest, Delete) {
96  // TODO(grt): move common stuff into the test fixture.
97  // Create a Chrome dir
98  base::FilePath chrome_dir(test_dir_.path());
99  chrome_dir = chrome_dir.AppendASCII("chrome");
100  base::CreateDirectory(chrome_dir);
101  ASSERT_TRUE(base::PathExists(chrome_dir));
102
103  base::FilePath chrome_dir_1(chrome_dir);
104  chrome_dir_1 = chrome_dir_1.AppendASCII("1.0.1.0");
105  base::CreateDirectory(chrome_dir_1);
106  ASSERT_TRUE(base::PathExists(chrome_dir_1));
107
108  base::FilePath chrome_dir_2(chrome_dir);
109  chrome_dir_2 = chrome_dir_2.AppendASCII("1.0.2.0");
110  base::CreateDirectory(chrome_dir_2);
111  ASSERT_TRUE(base::PathExists(chrome_dir_2));
112
113  base::FilePath chrome_dir_3(chrome_dir);
114  chrome_dir_3 = chrome_dir_3.AppendASCII("1.0.3.0");
115  base::CreateDirectory(chrome_dir_3);
116  ASSERT_TRUE(base::PathExists(chrome_dir_3));
117
118  base::FilePath chrome_dir_4(chrome_dir);
119  chrome_dir_4 = chrome_dir_4.AppendASCII("1.0.4.0");
120  base::CreateDirectory(chrome_dir_4);
121  ASSERT_TRUE(base::PathExists(chrome_dir_4));
122
123  base::FilePath chrome_dll_1(chrome_dir_1);
124  chrome_dll_1 = chrome_dll_1.AppendASCII("chrome.dll");
125  CreateTextFile(chrome_dll_1.value(), text_content_1);
126  ASSERT_TRUE(base::PathExists(chrome_dll_1));
127
128  base::FilePath chrome_dll_2(chrome_dir_2);
129  chrome_dll_2 = chrome_dll_2.AppendASCII("chrome.dll");
130  CreateTextFile(chrome_dll_2.value(), text_content_1);
131  ASSERT_TRUE(base::PathExists(chrome_dll_2));
132
133  base::FilePath chrome_dll_3(chrome_dir_3);
134  chrome_dll_3 = chrome_dll_3.AppendASCII("chrome.dll");
135  CreateTextFile(chrome_dll_3.value(), text_content_1);
136  ASSERT_TRUE(base::PathExists(chrome_dll_3));
137
138  base::FilePath chrome_dll_4(chrome_dir_4);
139  chrome_dll_4 = chrome_dll_4.AppendASCII("chrome.dll");
140  CreateTextFile(chrome_dll_4.value(), text_content_1);
141  ASSERT_TRUE(base::PathExists(chrome_dll_4));
142
143  MockInstallerState installer_state;
144  BuildSingleChromeState(chrome_dir, &installer_state);
145  Version latest_version("1.0.4.0");
146  {
147    base::ScopedTempDir temp_dir;
148    ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
149    installer_state.RemoveOldVersionDirectories(latest_version, NULL,
150                                                temp_dir.path());
151  }
152
153  // old versions should be gone
154  EXPECT_FALSE(base::PathExists(chrome_dir_1));
155  EXPECT_FALSE(base::PathExists(chrome_dir_2));
156  EXPECT_FALSE(base::PathExists(chrome_dir_3));
157  // the latest version should stay
158  EXPECT_TRUE(base::PathExists(chrome_dll_4));
159}
160
161// Delete older version directories, keeping the one in used intact.
162TEST_F(InstallerStateTest, DeleteInUsed) {
163  // Create a Chrome dir
164  base::FilePath chrome_dir(test_dir_.path());
165  chrome_dir = chrome_dir.AppendASCII("chrome");
166  base::CreateDirectory(chrome_dir);
167  ASSERT_TRUE(base::PathExists(chrome_dir));
168
169  base::FilePath chrome_dir_1(chrome_dir);
170  chrome_dir_1 = chrome_dir_1.AppendASCII("1.0.1.0");
171  base::CreateDirectory(chrome_dir_1);
172  ASSERT_TRUE(base::PathExists(chrome_dir_1));
173
174  base::FilePath chrome_dir_2(chrome_dir);
175  chrome_dir_2 = chrome_dir_2.AppendASCII("1.0.2.0");
176  base::CreateDirectory(chrome_dir_2);
177  ASSERT_TRUE(base::PathExists(chrome_dir_2));
178
179  base::FilePath chrome_dir_3(chrome_dir);
180  chrome_dir_3 = chrome_dir_3.AppendASCII("1.0.3.0");
181  base::CreateDirectory(chrome_dir_3);
182  ASSERT_TRUE(base::PathExists(chrome_dir_3));
183
184  base::FilePath chrome_dir_4(chrome_dir);
185  chrome_dir_4 = chrome_dir_4.AppendASCII("1.0.4.0");
186  base::CreateDirectory(chrome_dir_4);
187  ASSERT_TRUE(base::PathExists(chrome_dir_4));
188
189  base::FilePath chrome_dll_1(chrome_dir_1);
190  chrome_dll_1 = chrome_dll_1.AppendASCII("chrome.dll");
191  CreateTextFile(chrome_dll_1.value(), text_content_1);
192  ASSERT_TRUE(base::PathExists(chrome_dll_1));
193
194  base::FilePath chrome_dll_2(chrome_dir_2);
195  chrome_dll_2 = chrome_dll_2.AppendASCII("chrome.dll");
196  CreateTextFile(chrome_dll_2.value(), text_content_1);
197  ASSERT_TRUE(base::PathExists(chrome_dll_2));
198
199  // Open the file to make it in use.
200  std::ofstream file;
201  file.open(chrome_dll_2.value().c_str());
202
203  base::FilePath chrome_othera_2(chrome_dir_2);
204  chrome_othera_2 = chrome_othera_2.AppendASCII("othera.dll");
205  CreateTextFile(chrome_othera_2.value(), text_content_2);
206  ASSERT_TRUE(base::PathExists(chrome_othera_2));
207
208  base::FilePath chrome_otherb_2(chrome_dir_2);
209  chrome_otherb_2 = chrome_otherb_2.AppendASCII("otherb.dll");
210  CreateTextFile(chrome_otherb_2.value(), text_content_2);
211  ASSERT_TRUE(base::PathExists(chrome_otherb_2));
212
213  base::FilePath chrome_dll_3(chrome_dir_3);
214  chrome_dll_3 = chrome_dll_3.AppendASCII("chrome.dll");
215  CreateTextFile(chrome_dll_3.value(), text_content_1);
216  ASSERT_TRUE(base::PathExists(chrome_dll_3));
217
218  base::FilePath chrome_dll_4(chrome_dir_4);
219  chrome_dll_4 = chrome_dll_4.AppendASCII("chrome.dll");
220  CreateTextFile(chrome_dll_4.value(), text_content_1);
221  ASSERT_TRUE(base::PathExists(chrome_dll_4));
222
223  MockInstallerState installer_state;
224  BuildSingleChromeState(chrome_dir, &installer_state);
225  Version latest_version("1.0.4.0");
226  Version existing_version("1.0.1.0");
227  {
228    base::ScopedTempDir temp_dir;
229    ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
230    installer_state.RemoveOldVersionDirectories(latest_version,
231                                                &existing_version,
232                                                temp_dir.path());
233  }
234
235  // the version defined as the existing version should stay
236  EXPECT_TRUE(base::PathExists(chrome_dir_1));
237  // old versions not in used should be gone
238  EXPECT_FALSE(base::PathExists(chrome_dir_3));
239  // every thing under in used version should stay
240  EXPECT_TRUE(base::PathExists(chrome_dir_2));
241  EXPECT_TRUE(base::PathExists(chrome_dll_2));
242  EXPECT_TRUE(base::PathExists(chrome_othera_2));
243  EXPECT_TRUE(base::PathExists(chrome_otherb_2));
244  // the latest version should stay
245  EXPECT_TRUE(base::PathExists(chrome_dll_4));
246}
247
248// Tests a few basic things of the Package class.  Makes sure that the path
249// operations are correct
250TEST_F(InstallerStateTest, Basic) {
251  const bool multi_install = false;
252  const bool system_level = true;
253  CommandLine cmd_line = CommandLine::FromString(
254      std::wstring(L"setup.exe") +
255      (multi_install ? L" --multi-install --chrome" : L"") +
256      (system_level ? L" --system-level" : L""));
257  MasterPreferences prefs(cmd_line);
258  InstallationState machine_state;
259  machine_state.Initialize();
260  MockInstallerState installer_state;
261  installer_state.Initialize(cmd_line, prefs, machine_state);
262  installer_state.set_target_path(test_dir_.path());
263  EXPECT_EQ(test_dir_.path().value(), installer_state.target_path().value());
264  EXPECT_EQ(1U, installer_state.products().size());
265
266  const char kOldVersion[] = "1.2.3.4";
267  const char kNewVersion[] = "2.3.4.5";
268
269  Version new_version(kNewVersion);
270  Version old_version(kOldVersion);
271  ASSERT_TRUE(new_version.IsValid());
272  ASSERT_TRUE(old_version.IsValid());
273
274  base::FilePath installer_dir(
275      installer_state.GetInstallerDirectory(new_version));
276  EXPECT_FALSE(installer_dir.empty());
277
278  base::FilePath new_version_dir(installer_state.target_path().Append(
279      UTF8ToWide(new_version.GetString())));
280  base::FilePath old_version_dir(installer_state.target_path().Append(
281      UTF8ToWide(old_version.GetString())));
282
283  EXPECT_FALSE(base::PathExists(new_version_dir));
284  EXPECT_FALSE(base::PathExists(old_version_dir));
285
286  EXPECT_FALSE(base::PathExists(installer_dir));
287  base::CreateDirectory(installer_dir);
288  EXPECT_TRUE(base::PathExists(new_version_dir));
289
290  base::CreateDirectory(old_version_dir);
291  EXPECT_TRUE(base::PathExists(old_version_dir));
292
293  // Create a fake chrome.dll key file in the old version directory.  This
294  // should prevent the old version directory from getting deleted.
295  base::FilePath old_chrome_dll(old_version_dir.Append(installer::kChromeDll));
296  EXPECT_FALSE(base::PathExists(old_chrome_dll));
297
298  // Hold on to the file exclusively to prevent the directory from
299  // being deleted.
300  base::win::ScopedHandle file(
301    ::CreateFile(old_chrome_dll.value().c_str(), GENERIC_READ,
302                 0, NULL, OPEN_ALWAYS, 0, NULL));
303  EXPECT_TRUE(file.IsValid());
304  EXPECT_TRUE(base::PathExists(old_chrome_dll));
305
306  base::ScopedTempDir temp_dir;
307  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
308
309  // Don't explicitly tell the directory cleanup logic not to delete the
310  // old version, rely on the key files to keep it around.
311  installer_state.RemoveOldVersionDirectories(new_version,
312                                              NULL,
313                                              temp_dir.path());
314
315  // The old directory should still exist.
316  EXPECT_TRUE(base::PathExists(old_version_dir));
317  EXPECT_TRUE(base::PathExists(new_version_dir));
318
319  // Now close the file handle to make it possible to delete our key file.
320  file.Close();
321
322  installer_state.RemoveOldVersionDirectories(new_version,
323                                              NULL,
324                                              temp_dir.path());
325  // The new directory should still exist.
326  EXPECT_TRUE(base::PathExists(new_version_dir));
327
328  // Now, the old directory and key file should be gone.
329  EXPECT_FALSE(base::PathExists(old_chrome_dll));
330  EXPECT_FALSE(base::PathExists(old_version_dir));
331}
332
333TEST_F(InstallerStateTest, WithProduct) {
334  const bool multi_install = false;
335  const bool system_level = true;
336  CommandLine cmd_line = CommandLine::FromString(
337      std::wstring(L"setup.exe") +
338      (multi_install ? L" --multi-install --chrome" : L"") +
339      (system_level ? L" --system-level" : L""));
340  MasterPreferences prefs(cmd_line);
341  InstallationState machine_state;
342  machine_state.Initialize();
343  MockInstallerState installer_state;
344  installer_state.Initialize(cmd_line, prefs, machine_state);
345  installer_state.set_target_path(test_dir_.path());
346  EXPECT_EQ(1U, installer_state.products().size());
347  EXPECT_EQ(system_level, installer_state.system_install());
348
349  const char kCurrentVersion[] = "1.2.3.4";
350  Version current_version(kCurrentVersion);
351
352  HKEY root = system_level ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
353  EXPECT_EQ(root, installer_state.root_key());
354
355  {
356    RegistryOverrideManager override_manager;
357    override_manager.OverrideRegistry(root, L"root_pit");
358    BrowserDistribution* dist = BrowserDistribution::GetSpecificDistribution(
359        BrowserDistribution::CHROME_BROWSER);
360    RegKey chrome_key(root, dist->GetVersionKey().c_str(), KEY_ALL_ACCESS);
361    EXPECT_TRUE(chrome_key.Valid());
362    if (chrome_key.Valid()) {
363      chrome_key.WriteValue(google_update::kRegVersionField,
364                            UTF8ToWide(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, L"root_inst_res");
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, L"root_inst_res");
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 != NULL);
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(ASCIIToWide(kOldVersion)),
528    installer_state.target_path().Append(ASCIIToWide(kOldChromeExeVersion)),
529    installer_state.target_path().Append(L"2.1.1.0"),
530    installer_state.target_path().Append(ASCIIToWide(kChromeExeVersion)),
531    installer_state.target_path().Append(ASCIIToWide(kNewVersion)),
532    installer_state.target_path().Append(L"3.9.1.1"),
533  };
534
535  // Create the version directories.
536  for (int i = 0; i < arraysize(version_dirs); i++) {
537    base::CreateDirectory(version_dirs[i]);
538    EXPECT_TRUE(base::PathExists(version_dirs[i]));
539  }
540
541  // Create exes with the appropriate version resource.
542  // Use the current test exe as a baseline.
543  base::FilePath exe_path;
544  ASSERT_TRUE(PathService::Get(base::FILE_EXE, &exe_path));
545
546  struct target_info {
547    base::FilePath target_file;
548    const Version& target_version;
549  } targets[] = {
550    { installer_state.target_path().Append(installer::kChromeOldExe),
551      old_chrome_exe_version },
552    { installer_state.target_path().Append(installer::kChromeExe),
553      chrome_exe_version },
554    { installer_state.target_path().Append(installer::kChromeNewExe),
555      new_chrome_exe_version },
556  };
557  for (int i = 0; i < arraysize(targets); ++i) {
558    ASSERT_TRUE(upgrade_test::GenerateSpecificPEFileVersion(
559        exe_path, targets[i].target_file, targets[i].target_version));
560  }
561
562  // Call GetExistingExeVersions, validate that picks up the
563  // exe resources.
564  std::set<std::string> expected_exe_versions;
565  expected_exe_versions.insert(kOldChromeExeVersion);
566  expected_exe_versions.insert(kChromeExeVersion);
567  expected_exe_versions.insert(kNewChromeExeVersion);
568
569  std::set<std::string> actual_exe_versions;
570  installer_state.GetExistingExeVersions(&actual_exe_versions);
571  EXPECT_EQ(expected_exe_versions, actual_exe_versions);
572
573  // Call RemoveOldVersionDirectories
574  installer_state.RemoveOldVersionDirectories(new_version,
575                                              &old_version,
576                                              installer_state.target_path());
577
578  // What we expect to have left.
579  std::set<std::string> expected_remaining_dirs;
580  expected_remaining_dirs.insert(kOldVersion);
581  expected_remaining_dirs.insert(kNewVersion);
582  expected_remaining_dirs.insert(kOldChromeExeVersion);
583  expected_remaining_dirs.insert(kChromeExeVersion);
584  expected_remaining_dirs.insert(kNewChromeExeVersion);
585
586  // Enumerate dirs in target_path(), ensure only desired remain.
587  base::FileEnumerator version_enum(installer_state.target_path(), false,
588                                    base::FileEnumerator::DIRECTORIES);
589  for (base::FilePath next_version = version_enum.Next(); !next_version.empty();
590       next_version = version_enum.Next()) {
591    base::FilePath dir_name(next_version.BaseName());
592    Version version(WideToASCII(dir_name.value()));
593    if (version.IsValid()) {
594      EXPECT_TRUE(expected_remaining_dirs.erase(version.GetString()))
595          << "Unexpected version dir found: " << version.GetString();
596    }
597  }
598
599  std::set<std::string>::const_iterator iter(
600      expected_remaining_dirs.begin());
601  for (; iter != expected_remaining_dirs.end(); ++iter)
602    ADD_FAILURE() << "Expected to find version dir for " << *iter;
603}
604
605TEST_F(InstallerStateTest, InitializeTwice) {
606  InstallationState machine_state;
607  machine_state.Initialize();
608
609  InstallerState installer_state;
610
611  // Initialize the instance to install multi Chrome.
612  {
613    CommandLine cmd_line(
614        CommandLine::FromString(L"setup.exe --multi-install --chrome"));
615    MasterPreferences prefs(cmd_line);
616    installer_state.Initialize(cmd_line, prefs, machine_state);
617  }
618  // Confirm the expected state.
619  EXPECT_EQ(InstallerState::USER_LEVEL, installer_state.level());
620  EXPECT_EQ(InstallerState::MULTI_PACKAGE, installer_state.package_type());
621  EXPECT_EQ(InstallerState::MULTI_INSTALL, installer_state.operation());
622  EXPECT_TRUE(wcsstr(installer_state.target_path().value().c_str(),
623                     BrowserDistribution::GetSpecificDistribution(
624                         BrowserDistribution::CHROME_BINARIES)->
625                         GetInstallSubDir().c_str()));
626  EXPECT_FALSE(installer_state.verbose_logging());
627  EXPECT_EQ(installer_state.state_key(),
628            BrowserDistribution::GetSpecificDistribution(
629                BrowserDistribution::CHROME_BROWSER)->GetStateKey());
630  EXPECT_EQ(installer_state.state_type(), BrowserDistribution::CHROME_BROWSER);
631  EXPECT_TRUE(installer_state.multi_package_binaries_distribution());
632  EXPECT_TRUE(installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER));
633  EXPECT_FALSE(installer_state.FindProduct(BrowserDistribution::CHROME_FRAME));
634
635  // Now initialize it to install system-level single Chrome Frame.
636  {
637    CommandLine cmd_line(
638        CommandLine::FromString(L"setup.exe --system-level --chrome-frame "
639                                L"--verbose-logging"));
640    MasterPreferences prefs(cmd_line);
641    installer_state.Initialize(cmd_line, prefs, machine_state);
642  }
643
644  // Confirm that the old state is gone.
645  EXPECT_EQ(InstallerState::SYSTEM_LEVEL, installer_state.level());
646  EXPECT_EQ(InstallerState::SINGLE_PACKAGE, installer_state.package_type());
647  EXPECT_EQ(InstallerState::SINGLE_INSTALL_OR_UPDATE,
648            installer_state.operation());
649  EXPECT_TRUE(wcsstr(installer_state.target_path().value().c_str(),
650                     BrowserDistribution::GetSpecificDistribution(
651                         BrowserDistribution::CHROME_FRAME)->
652                         GetInstallSubDir().c_str()));
653  EXPECT_TRUE(installer_state.verbose_logging());
654  // state_key and type are wrong in unittests since it is set based on the
655  // current process's BrowserDistribution.
656  // EXPECT_EQ(installer_state.state_key(),
657  //           BrowserDistribution::GetSpecificDistribution(
658  //               BrowserDistribution::CHROME_FRAME)->GetStateKey());
659  // EXPECT_EQ(installer_state.state_type(), BrowserDistribution::CHROME_FRAME);
660  EXPECT_FALSE(
661      installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER));
662  EXPECT_TRUE(installer_state.FindProduct(BrowserDistribution::CHROME_FRAME));
663}
664
665// A fixture for testing InstallerState::DetermineCriticalVersion.  Individual
666// tests must invoke Initialize() with a critical version.
667class InstallerStateCriticalVersionTest : public ::testing::Test {
668 protected:
669  InstallerStateCriticalVersionTest() : cmd_line_(CommandLine::NO_PROGRAM) {}
670
671  // Creates a set of versions for use by all test runs.
672  static void SetUpTestCase() {
673    low_version_    = new Version("15.0.874.106");
674    opv_version_    = new Version("15.0.874.255");
675    middle_version_ = new Version("16.0.912.32");
676    pv_version_     = new Version("16.0.912.255");
677    high_version_   = new Version("17.0.932.0");
678  }
679
680  // Cleans up versions used by all test runs.
681  static void TearDownTestCase() {
682    delete low_version_;
683    delete opv_version_;
684    delete middle_version_;
685    delete pv_version_;
686    delete high_version_;
687  }
688
689  // Initializes the InstallerState to use for a test run.  The returned
690  // instance's critical update version is set to |version|.  |version| may be
691  // NULL, in which case the critical update version is unset.
692  MockInstallerState& Initialize(const Version* version) {
693    cmd_line_ = version == NULL ?
694        CommandLine::FromString(L"setup.exe") :
695        CommandLine::FromString(
696            L"setup.exe --critical-update-version=" +
697            ASCIIToWide(version->GetString()));
698    prefs_.reset(new MasterPreferences(cmd_line_));
699    machine_state_.Initialize();
700    installer_state_.Initialize(cmd_line_, *prefs_, machine_state_);
701    return installer_state_;
702  }
703
704  static Version* low_version_;
705  static Version* opv_version_;
706  static Version* middle_version_;
707  static Version* pv_version_;
708  static Version* high_version_;
709
710  CommandLine cmd_line_;
711  scoped_ptr<MasterPreferences> prefs_;
712  InstallationState machine_state_;
713  MockInstallerState installer_state_;
714};
715
716Version* InstallerStateCriticalVersionTest::low_version_ = NULL;
717Version* InstallerStateCriticalVersionTest::opv_version_ = NULL;
718Version* InstallerStateCriticalVersionTest::middle_version_ = NULL;
719Version* InstallerStateCriticalVersionTest::pv_version_ = NULL;
720Version* InstallerStateCriticalVersionTest::high_version_ = NULL;
721
722// Test the case where the critical version is less than the currently-running
723// Chrome.  The critical version is ignored since it doesn't apply.
724TEST_F(InstallerStateCriticalVersionTest, CriticalBeforeOpv) {
725  MockInstallerState& installer_state(Initialize(low_version_));
726
727  EXPECT_TRUE(installer_state.critical_update_version().Equals(*low_version_));
728  // Unable to determine the installed version, so assume critical update.
729  EXPECT_TRUE(
730      installer_state.DetermineCriticalVersion(NULL, *pv_version_).IsValid());
731  // Installed version is past the critical update.
732  EXPECT_FALSE(
733      installer_state.DetermineCriticalVersion(opv_version_, *pv_version_)
734          .IsValid());
735  // Installed version is past the critical update.
736  EXPECT_FALSE(
737      installer_state.DetermineCriticalVersion(pv_version_, *pv_version_)
738          .IsValid());
739}
740
741// Test the case where the critical version is equal to the currently-running
742// Chrome.  The critical version is ignored since it doesn't apply.
743TEST_F(InstallerStateCriticalVersionTest, CriticalEqualsOpv) {
744  MockInstallerState& installer_state(Initialize(opv_version_));
745
746  EXPECT_TRUE(installer_state.critical_update_version().Equals(*opv_version_));
747  // Unable to determine the installed version, so assume critical update.
748  EXPECT_TRUE(
749      installer_state.DetermineCriticalVersion(NULL, *pv_version_).IsValid());
750  // Installed version equals the critical update.
751  EXPECT_FALSE(
752      installer_state.DetermineCriticalVersion(opv_version_, *pv_version_)
753          .IsValid());
754  // Installed version equals the critical update.
755  EXPECT_FALSE(
756      installer_state.DetermineCriticalVersion(pv_version_, *pv_version_)
757          .IsValid());
758}
759
760// Test the case where the critical version is between the currently-running
761// Chrome and the to-be-installed Chrome.
762TEST_F(InstallerStateCriticalVersionTest, CriticalBetweenOpvAndPv) {
763  MockInstallerState& installer_state(Initialize(middle_version_));
764
765  EXPECT_TRUE(installer_state.critical_update_version().Equals(
766      *middle_version_));
767  // Unable to determine the installed version, so assume critical update.
768  EXPECT_TRUE(
769      installer_state.DetermineCriticalVersion(NULL, *pv_version_).IsValid());
770  // Installed version before the critical update.
771  EXPECT_TRUE(
772      installer_state.DetermineCriticalVersion(opv_version_, *pv_version_)
773          .IsValid());
774  // Installed version is past the critical update.
775  EXPECT_FALSE(
776      installer_state.DetermineCriticalVersion(pv_version_, *pv_version_)
777          .IsValid());
778}
779
780// Test the case where the critical version is the same as the to-be-installed
781// Chrome.
782TEST_F(InstallerStateCriticalVersionTest, CriticalEqualsPv) {
783  MockInstallerState& installer_state(Initialize(pv_version_));
784
785  EXPECT_TRUE(installer_state.critical_update_version().Equals(
786      *pv_version_));
787  // Unable to determine the installed version, so assume critical update.
788  EXPECT_TRUE(
789      installer_state.DetermineCriticalVersion(NULL, *pv_version_).IsValid());
790  // Installed version before the critical update.
791  EXPECT_TRUE(
792      installer_state.DetermineCriticalVersion(opv_version_, *pv_version_)
793          .IsValid());
794  // Installed version equals the critical update.
795  EXPECT_FALSE(
796      installer_state.DetermineCriticalVersion(pv_version_, *pv_version_)
797          .IsValid());
798}
799
800// Test the case where the critical version is greater than the to-be-installed
801// Chrome.
802TEST_F(InstallerStateCriticalVersionTest, CriticalAfterPv) {
803  MockInstallerState& installer_state(Initialize(high_version_));
804
805  EXPECT_TRUE(installer_state.critical_update_version().Equals(
806      *high_version_));
807  // Critical update newer than the new version.
808  EXPECT_FALSE(
809      installer_state.DetermineCriticalVersion(NULL, *pv_version_).IsValid());
810  EXPECT_FALSE(
811      installer_state.DetermineCriticalVersion(opv_version_, *pv_version_)
812          .IsValid());
813  EXPECT_FALSE(
814      installer_state.DetermineCriticalVersion(pv_version_, *pv_version_)
815          .IsValid());
816}
817