installer_state_unittest.cc revision 7dbb3d5cf0c15f500944d211057644d6a2f37371
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/process_util.h"
17#include "base/strings/string_util.h"
18#include "base/strings/utf_string_conversions.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  EXPECT_TRUE(installer_state->FindProduct(BrowserDistribution::CHROME_FRAME)
88      == NULL);
89}
90
91wchar_t text_content_1[] = L"delete me";
92wchar_t text_content_2[] = L"delete me as well";
93
94// Delete version directories. Everything lower than the given version
95// should be deleted.
96TEST_F(InstallerStateTest, Delete) {
97  // TODO(grt): move common stuff into the test fixture.
98  // Create a Chrome dir
99  base::FilePath chrome_dir(test_dir_.path());
100  chrome_dir = chrome_dir.AppendASCII("chrome");
101  file_util::CreateDirectory(chrome_dir);
102  ASSERT_TRUE(base::PathExists(chrome_dir));
103
104  base::FilePath chrome_dir_1(chrome_dir);
105  chrome_dir_1 = chrome_dir_1.AppendASCII("1.0.1.0");
106  file_util::CreateDirectory(chrome_dir_1);
107  ASSERT_TRUE(base::PathExists(chrome_dir_1));
108
109  base::FilePath chrome_dir_2(chrome_dir);
110  chrome_dir_2 = chrome_dir_2.AppendASCII("1.0.2.0");
111  file_util::CreateDirectory(chrome_dir_2);
112  ASSERT_TRUE(base::PathExists(chrome_dir_2));
113
114  base::FilePath chrome_dir_3(chrome_dir);
115  chrome_dir_3 = chrome_dir_3.AppendASCII("1.0.3.0");
116  file_util::CreateDirectory(chrome_dir_3);
117  ASSERT_TRUE(base::PathExists(chrome_dir_3));
118
119  base::FilePath chrome_dir_4(chrome_dir);
120  chrome_dir_4 = chrome_dir_4.AppendASCII("1.0.4.0");
121  file_util::CreateDirectory(chrome_dir_4);
122  ASSERT_TRUE(base::PathExists(chrome_dir_4));
123
124  base::FilePath chrome_dll_1(chrome_dir_1);
125  chrome_dll_1 = chrome_dll_1.AppendASCII("chrome.dll");
126  CreateTextFile(chrome_dll_1.value(), text_content_1);
127  ASSERT_TRUE(base::PathExists(chrome_dll_1));
128
129  base::FilePath chrome_dll_2(chrome_dir_2);
130  chrome_dll_2 = chrome_dll_2.AppendASCII("chrome.dll");
131  CreateTextFile(chrome_dll_2.value(), text_content_1);
132  ASSERT_TRUE(base::PathExists(chrome_dll_2));
133
134  base::FilePath chrome_dll_3(chrome_dir_3);
135  chrome_dll_3 = chrome_dll_3.AppendASCII("chrome.dll");
136  CreateTextFile(chrome_dll_3.value(), text_content_1);
137  ASSERT_TRUE(base::PathExists(chrome_dll_3));
138
139  base::FilePath chrome_dll_4(chrome_dir_4);
140  chrome_dll_4 = chrome_dll_4.AppendASCII("chrome.dll");
141  CreateTextFile(chrome_dll_4.value(), text_content_1);
142  ASSERT_TRUE(base::PathExists(chrome_dll_4));
143
144  MockInstallerState installer_state;
145  BuildSingleChromeState(chrome_dir, &installer_state);
146  Version latest_version("1.0.4.0");
147  {
148    base::ScopedTempDir temp_dir;
149    ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
150    installer_state.RemoveOldVersionDirectories(latest_version, NULL,
151                                                temp_dir.path());
152  }
153
154  // old versions should be gone
155  EXPECT_FALSE(base::PathExists(chrome_dir_1));
156  EXPECT_FALSE(base::PathExists(chrome_dir_2));
157  EXPECT_FALSE(base::PathExists(chrome_dir_3));
158  // the latest version should stay
159  EXPECT_TRUE(base::PathExists(chrome_dll_4));
160}
161
162// Delete older version directories, keeping the one in used intact.
163TEST_F(InstallerStateTest, DeleteInUsed) {
164  // Create a Chrome dir
165  base::FilePath chrome_dir(test_dir_.path());
166  chrome_dir = chrome_dir.AppendASCII("chrome");
167  file_util::CreateDirectory(chrome_dir);
168  ASSERT_TRUE(base::PathExists(chrome_dir));
169
170  base::FilePath chrome_dir_1(chrome_dir);
171  chrome_dir_1 = chrome_dir_1.AppendASCII("1.0.1.0");
172  file_util::CreateDirectory(chrome_dir_1);
173  ASSERT_TRUE(base::PathExists(chrome_dir_1));
174
175  base::FilePath chrome_dir_2(chrome_dir);
176  chrome_dir_2 = chrome_dir_2.AppendASCII("1.0.2.0");
177  file_util::CreateDirectory(chrome_dir_2);
178  ASSERT_TRUE(base::PathExists(chrome_dir_2));
179
180  base::FilePath chrome_dir_3(chrome_dir);
181  chrome_dir_3 = chrome_dir_3.AppendASCII("1.0.3.0");
182  file_util::CreateDirectory(chrome_dir_3);
183  ASSERT_TRUE(base::PathExists(chrome_dir_3));
184
185  base::FilePath chrome_dir_4(chrome_dir);
186  chrome_dir_4 = chrome_dir_4.AppendASCII("1.0.4.0");
187  file_util::CreateDirectory(chrome_dir_4);
188  ASSERT_TRUE(base::PathExists(chrome_dir_4));
189
190  base::FilePath chrome_dll_1(chrome_dir_1);
191  chrome_dll_1 = chrome_dll_1.AppendASCII("chrome.dll");
192  CreateTextFile(chrome_dll_1.value(), text_content_1);
193  ASSERT_TRUE(base::PathExists(chrome_dll_1));
194
195  base::FilePath chrome_dll_2(chrome_dir_2);
196  chrome_dll_2 = chrome_dll_2.AppendASCII("chrome.dll");
197  CreateTextFile(chrome_dll_2.value(), text_content_1);
198  ASSERT_TRUE(base::PathExists(chrome_dll_2));
199
200  // Open the file to make it in use.
201  std::ofstream file;
202  file.open(chrome_dll_2.value().c_str());
203
204  base::FilePath chrome_othera_2(chrome_dir_2);
205  chrome_othera_2 = chrome_othera_2.AppendASCII("othera.dll");
206  CreateTextFile(chrome_othera_2.value(), text_content_2);
207  ASSERT_TRUE(base::PathExists(chrome_othera_2));
208
209  base::FilePath chrome_otherb_2(chrome_dir_2);
210  chrome_otherb_2 = chrome_otherb_2.AppendASCII("otherb.dll");
211  CreateTextFile(chrome_otherb_2.value(), text_content_2);
212  ASSERT_TRUE(base::PathExists(chrome_otherb_2));
213
214  base::FilePath chrome_dll_3(chrome_dir_3);
215  chrome_dll_3 = chrome_dll_3.AppendASCII("chrome.dll");
216  CreateTextFile(chrome_dll_3.value(), text_content_1);
217  ASSERT_TRUE(base::PathExists(chrome_dll_3));
218
219  base::FilePath chrome_dll_4(chrome_dir_4);
220  chrome_dll_4 = chrome_dll_4.AppendASCII("chrome.dll");
221  CreateTextFile(chrome_dll_4.value(), text_content_1);
222  ASSERT_TRUE(base::PathExists(chrome_dll_4));
223
224  MockInstallerState installer_state;
225  BuildSingleChromeState(chrome_dir, &installer_state);
226  Version latest_version("1.0.4.0");
227  Version existing_version("1.0.1.0");
228  {
229    base::ScopedTempDir temp_dir;
230    ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
231    installer_state.RemoveOldVersionDirectories(latest_version,
232                                                &existing_version,
233                                                temp_dir.path());
234  }
235
236  // the version defined as the existing version should stay
237  EXPECT_TRUE(base::PathExists(chrome_dir_1));
238  // old versions not in used should be gone
239  EXPECT_FALSE(base::PathExists(chrome_dir_3));
240  // every thing under in used version should stay
241  EXPECT_TRUE(base::PathExists(chrome_dir_2));
242  EXPECT_TRUE(base::PathExists(chrome_dll_2));
243  EXPECT_TRUE(base::PathExists(chrome_othera_2));
244  EXPECT_TRUE(base::PathExists(chrome_otherb_2));
245  // the latest version should stay
246  EXPECT_TRUE(base::PathExists(chrome_dll_4));
247}
248
249// Tests a few basic things of the Package class.  Makes sure that the path
250// operations are correct
251TEST_F(InstallerStateTest, Basic) {
252  const bool multi_install = false;
253  const bool system_level = true;
254  CommandLine cmd_line = CommandLine::FromString(
255      std::wstring(L"setup.exe") +
256      (multi_install ? L" --multi-install --chrome" : L"") +
257      (system_level ? L" --system-level" : L""));
258  MasterPreferences prefs(cmd_line);
259  InstallationState machine_state;
260  machine_state.Initialize();
261  MockInstallerState installer_state;
262  installer_state.Initialize(cmd_line, prefs, machine_state);
263  installer_state.set_target_path(test_dir_.path());
264  EXPECT_EQ(test_dir_.path().value(), installer_state.target_path().value());
265  EXPECT_EQ(1U, installer_state.products().size());
266
267  const char kOldVersion[] = "1.2.3.4";
268  const char kNewVersion[] = "2.3.4.5";
269
270  Version new_version(kNewVersion);
271  Version old_version(kOldVersion);
272  ASSERT_TRUE(new_version.IsValid());
273  ASSERT_TRUE(old_version.IsValid());
274
275  base::FilePath installer_dir(
276      installer_state.GetInstallerDirectory(new_version));
277  EXPECT_FALSE(installer_dir.empty());
278
279  base::FilePath new_version_dir(installer_state.target_path().Append(
280      UTF8ToWide(new_version.GetString())));
281  base::FilePath old_version_dir(installer_state.target_path().Append(
282      UTF8ToWide(old_version.GetString())));
283
284  EXPECT_FALSE(base::PathExists(new_version_dir));
285  EXPECT_FALSE(base::PathExists(old_version_dir));
286
287  EXPECT_FALSE(base::PathExists(installer_dir));
288  file_util::CreateDirectory(installer_dir);
289  EXPECT_TRUE(base::PathExists(new_version_dir));
290
291  file_util::CreateDirectory(old_version_dir);
292  EXPECT_TRUE(base::PathExists(old_version_dir));
293
294  // Create a fake chrome.dll key file in the old version directory.  This
295  // should prevent the old version directory from getting deleted.
296  base::FilePath old_chrome_dll(old_version_dir.Append(installer::kChromeDll));
297  EXPECT_FALSE(base::PathExists(old_chrome_dll));
298
299  // Hold on to the file exclusively to prevent the directory from
300  // being deleted.
301  base::win::ScopedHandle file(
302    ::CreateFile(old_chrome_dll.value().c_str(), GENERIC_READ,
303                 0, NULL, OPEN_ALWAYS, 0, NULL));
304  EXPECT_TRUE(file.IsValid());
305  EXPECT_TRUE(base::PathExists(old_chrome_dll));
306
307  base::ScopedTempDir temp_dir;
308  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
309
310  // Don't explicitly tell the directory cleanup logic not to delete the
311  // old version, rely on the key files to keep it around.
312  installer_state.RemoveOldVersionDirectories(new_version,
313                                              NULL,
314                                              temp_dir.path());
315
316  // The old directory should still exist.
317  EXPECT_TRUE(base::PathExists(old_version_dir));
318  EXPECT_TRUE(base::PathExists(new_version_dir));
319
320  // Now close the file handle to make it possible to delete our key file.
321  file.Close();
322
323  installer_state.RemoveOldVersionDirectories(new_version,
324                                              NULL,
325                                              temp_dir.path());
326  // The new directory should still exist.
327  EXPECT_TRUE(base::PathExists(new_version_dir));
328
329  // Now, the old directory and key file should be gone.
330  EXPECT_FALSE(base::PathExists(old_chrome_dll));
331  EXPECT_FALSE(base::PathExists(old_version_dir));
332}
333
334TEST_F(InstallerStateTest, WithProduct) {
335  const bool multi_install = false;
336  const bool system_level = true;
337  CommandLine cmd_line = CommandLine::FromString(
338      std::wstring(L"setup.exe") +
339      (multi_install ? L" --multi-install --chrome" : L"") +
340      (system_level ? L" --system-level" : L""));
341  MasterPreferences prefs(cmd_line);
342  InstallationState machine_state;
343  machine_state.Initialize();
344  MockInstallerState installer_state;
345  installer_state.Initialize(cmd_line, prefs, machine_state);
346  installer_state.set_target_path(test_dir_.path());
347  EXPECT_EQ(1U, installer_state.products().size());
348  EXPECT_EQ(system_level, installer_state.system_install());
349
350  const char kCurrentVersion[] = "1.2.3.4";
351  Version current_version(kCurrentVersion);
352
353  HKEY root = system_level ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
354  EXPECT_EQ(root, installer_state.root_key());
355
356  {
357    RegistryOverrideManager override_manager;
358    override_manager.OverrideRegistry(root, L"root_pit");
359    BrowserDistribution* dist = BrowserDistribution::GetSpecificDistribution(
360        BrowserDistribution::CHROME_BROWSER);
361    RegKey chrome_key(root, dist->GetVersionKey().c_str(), KEY_ALL_ACCESS);
362    EXPECT_TRUE(chrome_key.Valid());
363    if (chrome_key.Valid()) {
364      chrome_key.WriteValue(google_update::kRegVersionField,
365                            UTF8ToWide(current_version.GetString()).c_str());
366      machine_state.Initialize();
367      // TODO(tommi): Also test for when there exists a new_chrome.exe.
368      Version found_version(*installer_state.GetCurrentVersion(machine_state));
369      EXPECT_TRUE(found_version.IsValid());
370      if (found_version.IsValid())
371        EXPECT_TRUE(current_version.Equals(found_version));
372    }
373  }
374}
375
376TEST_F(InstallerStateTest, InstallerResult) {
377  const bool system_level = true;
378  bool multi_install = false;
379  HKEY root = system_level ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
380
381  RegKey key;
382  std::wstring launch_cmd = L"hey diddle diddle";
383  std::wstring value;
384  DWORD dw_value;
385
386  // check results for a fresh install of single Chrome
387  {
388    RegistryOverrideManager override_manager;
389    override_manager.OverrideRegistry(root, L"root_inst_res");
390    CommandLine cmd_line = CommandLine::FromString(L"setup.exe --system-level");
391    const MasterPreferences prefs(cmd_line);
392    InstallationState machine_state;
393    machine_state.Initialize();
394    InstallerState state;
395    state.Initialize(cmd_line, prefs, machine_state);
396    state.WriteInstallerResult(installer::FIRST_INSTALL_SUCCESS,
397                               IDS_INSTALL_OS_ERROR_BASE, &launch_cmd);
398    BrowserDistribution* distribution =
399        BrowserDistribution::GetSpecificDistribution(
400            BrowserDistribution::CHROME_BROWSER);
401    EXPECT_EQ(ERROR_SUCCESS,
402        key.Open(root, distribution->GetStateKey().c_str(), KEY_READ));
403    EXPECT_EQ(ERROR_SUCCESS,
404        key.ReadValueDW(installer::kInstallerResult, &dw_value));
405    EXPECT_EQ(static_cast<DWORD>(0), dw_value);
406    EXPECT_EQ(ERROR_SUCCESS,
407        key.ReadValueDW(installer::kInstallerError, &dw_value));
408    EXPECT_EQ(static_cast<DWORD>(installer::FIRST_INSTALL_SUCCESS), dw_value);
409    EXPECT_EQ(ERROR_SUCCESS,
410        key.ReadValue(installer::kInstallerResultUIString, &value));
411    EXPECT_FALSE(value.empty());
412    EXPECT_EQ(ERROR_SUCCESS,
413        key.ReadValue(installer::kInstallerSuccessLaunchCmdLine, &value));
414    EXPECT_EQ(launch_cmd, value);
415  }
416
417  // check results for a fresh install of multi Chrome
418  {
419    RegistryOverrideManager override_manager;
420    override_manager.OverrideRegistry(root, L"root_inst_res");
421    CommandLine cmd_line = CommandLine::FromString(
422        L"setup.exe --system-level --multi-install --chrome");
423    const MasterPreferences prefs(cmd_line);
424    InstallationState machine_state;
425    machine_state.Initialize();
426    InstallerState state;
427    state.Initialize(cmd_line, prefs, machine_state);
428    state.WriteInstallerResult(installer::FIRST_INSTALL_SUCCESS, 0,
429                               &launch_cmd);
430    BrowserDistribution* distribution =
431        BrowserDistribution::GetSpecificDistribution(
432            BrowserDistribution::CHROME_BROWSER);
433    BrowserDistribution* binaries =
434        BrowserDistribution::GetSpecificDistribution(
435            BrowserDistribution::CHROME_BINARIES);
436    EXPECT_EQ(ERROR_SUCCESS,
437        key.Open(root, distribution->GetStateKey().c_str(), KEY_READ));
438    EXPECT_EQ(ERROR_SUCCESS,
439        key.ReadValue(installer::kInstallerSuccessLaunchCmdLine, &value));
440    EXPECT_EQ(launch_cmd, value);
441    EXPECT_EQ(ERROR_SUCCESS,
442        key.Open(root, binaries->GetStateKey().c_str(), KEY_READ));
443    EXPECT_EQ(ERROR_SUCCESS,
444        key.ReadValue(installer::kInstallerSuccessLaunchCmdLine, &value));
445    EXPECT_EQ(launch_cmd, value);
446    key.Close();
447  }
448}
449
450// Test GetCurrentVersion when migrating single Chrome to multi
451TEST_F(InstallerStateTest, GetCurrentVersionMigrateChrome) {
452  using installer::FakeInstallationState;
453
454  const bool system_install = false;
455  FakeInstallationState machine_state;
456
457  // Pretend that this version of single-install Chrome is already installed.
458  machine_state.AddChrome(system_install, false,
459                          new Version(chrome::kChromeVersion));
460
461  // Now we're invoked to install multi Chrome.
462  CommandLine cmd_line(
463      CommandLine::FromString(L"setup.exe --multi-install --chrome"));
464  MasterPreferences prefs(cmd_line);
465  InstallerState installer_state;
466  installer_state.Initialize(cmd_line, prefs, machine_state);
467
468  // Is the Chrome version picked up?
469  scoped_ptr<Version> version(installer_state.GetCurrentVersion(machine_state));
470  EXPECT_TRUE(version.get() != NULL);
471}
472
473TEST_F(InstallerStateTest, IsFileInUse) {
474  base::ScopedTempDir temp_dir;
475  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
476
477  base::FilePath temp_file;
478  ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir.path(), &temp_file));
479
480  EXPECT_FALSE(MockInstallerState::IsFileInUse(temp_file));
481
482  {
483    // Open a handle to the file with the same access mode and sharing options
484    // as the loader.
485    base::win::ScopedHandle temp_handle(
486        CreateFile(temp_file.value().c_str(),
487                   SYNCHRONIZE | FILE_EXECUTE,
488                   FILE_SHARE_DELETE | FILE_SHARE_READ,
489                   NULL, OPEN_EXISTING, 0, 0));
490    ASSERT_TRUE(temp_handle != NULL);
491
492    // The file should now be in use.
493    EXPECT_TRUE(MockInstallerState::IsFileInUse(temp_file));
494  }
495
496  // And once the handle is gone, it should no longer be in use.
497  EXPECT_FALSE(MockInstallerState::IsFileInUse(temp_file));
498}
499
500TEST_F(InstallerStateTest, RemoveOldVersionDirs) {
501  MockInstallerState installer_state;
502  installer_state.set_target_path(test_dir_.path());
503  EXPECT_EQ(test_dir_.path().value(), installer_state.target_path().value());
504
505  const char kOldVersion[] = "2.0.0.0";
506  const char kNewVersion[] = "3.0.0.0";
507  const char kOldChromeExeVersion[] = "2.1.0.0";
508  const char kChromeExeVersion[] = "2.1.1.1";
509  const char kNewChromeExeVersion[] = "3.0.0.0";
510
511  Version new_version(kNewVersion);
512  Version old_version(kOldVersion);
513  Version old_chrome_exe_version(kOldChromeExeVersion);
514  Version chrome_exe_version(kChromeExeVersion);
515  Version new_chrome_exe_version(kNewChromeExeVersion);
516
517  ASSERT_TRUE(new_version.IsValid());
518  ASSERT_TRUE(old_version.IsValid());
519  ASSERT_TRUE(old_chrome_exe_version.IsValid());
520  ASSERT_TRUE(chrome_exe_version.IsValid());
521  ASSERT_TRUE(new_chrome_exe_version.IsValid());
522
523  // Set up a bunch of version dir paths.
524  base::FilePath version_dirs[] = {
525    installer_state.target_path().Append(L"1.2.3.4"),
526    installer_state.target_path().Append(L"1.2.3.5"),
527    installer_state.target_path().Append(L"1.2.3.6"),
528    installer_state.target_path().Append(ASCIIToWide(kOldVersion)),
529    installer_state.target_path().Append(ASCIIToWide(kOldChromeExeVersion)),
530    installer_state.target_path().Append(L"2.1.1.0"),
531    installer_state.target_path().Append(ASCIIToWide(kChromeExeVersion)),
532    installer_state.target_path().Append(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    file_util::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(WideToASCII(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
606
607// A fixture for testing InstallerState::DetermineCriticalVersion.  Individual
608// tests must invoke Initialize() with a critical version.
609class InstallerStateCriticalVersionTest : public ::testing::Test {
610 protected:
611  InstallerStateCriticalVersionTest() : cmd_line_(CommandLine::NO_PROGRAM) {}
612
613  // Creates a set of versions for use by all test runs.
614  static void SetUpTestCase() {
615    low_version_    = new Version("15.0.874.106");
616    opv_version_    = new Version("15.0.874.255");
617    middle_version_ = new Version("16.0.912.32");
618    pv_version_     = new Version("16.0.912.255");
619    high_version_   = new Version("17.0.932.0");
620  }
621
622  // Cleans up versions used by all test runs.
623  static void TearDownTestCase() {
624    delete low_version_;
625    delete opv_version_;
626    delete middle_version_;
627    delete pv_version_;
628    delete high_version_;
629  }
630
631  // Initializes the InstallerState to use for a test run.  The returned
632  // instance's critical update version is set to |version|.  |version| may be
633  // NULL, in which case the critical update version is unset.
634  MockInstallerState& Initialize(const Version* version) {
635    cmd_line_ = version == NULL ?
636        CommandLine::FromString(L"setup.exe") :
637        CommandLine::FromString(
638            L"setup.exe --critical-update-version=" +
639            ASCIIToWide(version->GetString()));
640    prefs_.reset(new MasterPreferences(cmd_line_));
641    machine_state_.Initialize();
642    installer_state_.Initialize(cmd_line_, *prefs_, machine_state_);
643    return installer_state_;
644  }
645
646  static Version* low_version_;
647  static Version* opv_version_;
648  static Version* middle_version_;
649  static Version* pv_version_;
650  static Version* high_version_;
651
652  CommandLine cmd_line_;
653  scoped_ptr<MasterPreferences> prefs_;
654  InstallationState machine_state_;
655  MockInstallerState installer_state_;
656};
657
658Version* InstallerStateCriticalVersionTest::low_version_ = NULL;
659Version* InstallerStateCriticalVersionTest::opv_version_ = NULL;
660Version* InstallerStateCriticalVersionTest::middle_version_ = NULL;
661Version* InstallerStateCriticalVersionTest::pv_version_ = NULL;
662Version* InstallerStateCriticalVersionTest::high_version_ = NULL;
663
664// Test the case where the critical version is less than the currently-running
665// Chrome.  The critical version is ignored since it doesn't apply.
666TEST_F(InstallerStateCriticalVersionTest, CriticalBeforeOpv) {
667  MockInstallerState& installer_state(Initialize(low_version_));
668
669  EXPECT_TRUE(installer_state.critical_update_version().Equals(*low_version_));
670  // Unable to determine the installed version, so assume critical update.
671  EXPECT_TRUE(
672      installer_state.DetermineCriticalVersion(NULL, *pv_version_).IsValid());
673  // Installed version is past the critical update.
674  EXPECT_FALSE(
675      installer_state.DetermineCriticalVersion(opv_version_, *pv_version_)
676          .IsValid());
677  // Installed version is past the critical update.
678  EXPECT_FALSE(
679      installer_state.DetermineCriticalVersion(pv_version_, *pv_version_)
680          .IsValid());
681}
682
683// Test the case where the critical version is equal to the currently-running
684// Chrome.  The critical version is ignored since it doesn't apply.
685TEST_F(InstallerStateCriticalVersionTest, CriticalEqualsOpv) {
686  MockInstallerState& installer_state(Initialize(opv_version_));
687
688  EXPECT_TRUE(installer_state.critical_update_version().Equals(*opv_version_));
689  // Unable to determine the installed version, so assume critical update.
690  EXPECT_TRUE(
691      installer_state.DetermineCriticalVersion(NULL, *pv_version_).IsValid());
692  // Installed version equals the critical update.
693  EXPECT_FALSE(
694      installer_state.DetermineCriticalVersion(opv_version_, *pv_version_)
695          .IsValid());
696  // Installed version equals the critical update.
697  EXPECT_FALSE(
698      installer_state.DetermineCriticalVersion(pv_version_, *pv_version_)
699          .IsValid());
700}
701
702// Test the case where the critical version is between the currently-running
703// Chrome and the to-be-installed Chrome.
704TEST_F(InstallerStateCriticalVersionTest, CriticalBetweenOpvAndPv) {
705  MockInstallerState& installer_state(Initialize(middle_version_));
706
707  EXPECT_TRUE(installer_state.critical_update_version().Equals(
708      *middle_version_));
709  // Unable to determine the installed version, so assume critical update.
710  EXPECT_TRUE(
711      installer_state.DetermineCriticalVersion(NULL, *pv_version_).IsValid());
712  // Installed version before the critical update.
713  EXPECT_TRUE(
714      installer_state.DetermineCriticalVersion(opv_version_, *pv_version_)
715          .IsValid());
716  // Installed version is past the critical update.
717  EXPECT_FALSE(
718      installer_state.DetermineCriticalVersion(pv_version_, *pv_version_)
719          .IsValid());
720}
721
722// Test the case where the critical version is the same as the to-be-installed
723// Chrome.
724TEST_F(InstallerStateCriticalVersionTest, CriticalEqualsPv) {
725  MockInstallerState& installer_state(Initialize(pv_version_));
726
727  EXPECT_TRUE(installer_state.critical_update_version().Equals(
728      *pv_version_));
729  // Unable to determine the installed version, so assume critical update.
730  EXPECT_TRUE(
731      installer_state.DetermineCriticalVersion(NULL, *pv_version_).IsValid());
732  // Installed version before the critical update.
733  EXPECT_TRUE(
734      installer_state.DetermineCriticalVersion(opv_version_, *pv_version_)
735          .IsValid());
736  // Installed version equals the critical update.
737  EXPECT_FALSE(
738      installer_state.DetermineCriticalVersion(pv_version_, *pv_version_)
739          .IsValid());
740}
741
742// Test the case where the critical version is greater than the to-be-installed
743// Chrome.
744TEST_F(InstallerStateCriticalVersionTest, CriticalAfterPv) {
745  MockInstallerState& installer_state(Initialize(high_version_));
746
747  EXPECT_TRUE(installer_state.critical_update_version().Equals(
748      *high_version_));
749  // Critical update newer than the new version.
750  EXPECT_FALSE(
751      installer_state.DetermineCriticalVersion(NULL, *pv_version_).IsValid());
752  EXPECT_FALSE(
753      installer_state.DetermineCriticalVersion(opv_version_, *pv_version_)
754          .IsValid());
755  EXPECT_FALSE(
756      installer_state.DetermineCriticalVersion(pv_version_, *pv_version_)
757          .IsValid());
758}
759