summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorfdoray <fdoray@chromium.org>2016-03-02 11:35:52 -0800
committerCommit bot <commit-bot@chromium.org>2016-03-02 19:36:49 +0000
commit1b50c70773051b24925b46bab8b006e606633c5c (patch)
treea057ebb46fabb4568096e26032ce8a4d54cf4978
parent456b8978ac83e8b0360f76cf98801cdf5172025c (diff)
downloadchromium_src-1b50c70773051b24925b46bab8b006e606633c5c.zip
chromium_src-1b50c70773051b24925b46bab8b006e606633c5c.tar.gz
chromium_src-1b50c70773051b24925b46bab8b006e606633c5c.tar.bz2
Delete old files after an update.
This CL introduces a WorkItem which deletes old executables and version directories from the Chrome installation directory. For now, the WorkItem is used at the end of an update and at the end of a new_chrome.exe->chrome.exe rename. Eventually, it will also be invoked repetitively from a cleanup process launched when the WorkItem fails to delete everything the first time. BUG=451546 Review URL: https://codereview.chromium.org/1666363002 Cr-Commit-Position: refs/heads/master@{#378802}
-rw-r--r--chrome/chrome_installer.gypi1
-rw-r--r--chrome/chrome_installer_util.gypi2
-rw-r--r--chrome/installer/setup/install.cc7
-rw-r--r--chrome/installer/setup/install_worker.cc7
-rw-r--r--chrome/installer/setup/setup_main.cc13
-rw-r--r--chrome/installer/util/BUILD.gn3
-rw-r--r--chrome/installer/util/delete_old_versions.cc277
-rw-r--r--chrome/installer/util/delete_old_versions.h24
-rw-r--r--chrome/installer/util/delete_old_versions_unittest.cc286
-rw-r--r--chrome/installer/util/installer_state.cc65
-rw-r--r--chrome/installer/util/installer_state.h13
-rw-r--r--chrome/installer/util/installer_state_unittest.cc353
12 files changed, 609 insertions, 442 deletions
diff --git a/chrome/chrome_installer.gypi b/chrome/chrome_installer.gypi
index dc4cb53..a90d710 100644
--- a/chrome/chrome_installer.gypi
+++ b/chrome/chrome_installer.gypi
@@ -111,6 +111,7 @@
'installer/util/create_dir_work_item_unittest.cc',
'installer/util/create_reg_key_work_item_unittest.cc',
'installer/util/delete_after_reboot_helper_unittest.cc',
+ 'installer/util/delete_old_versions_unittest.cc',
'installer/util/delete_reg_key_work_item_unittest.cc',
'installer/util/delete_reg_value_work_item_unittest.cc',
'installer/util/delete_tree_work_item_unittest.cc',
diff --git a/chrome/chrome_installer_util.gypi b/chrome/chrome_installer_util.gypi
index e21ccbd..facb617 100644
--- a/chrome/chrome_installer_util.gypi
+++ b/chrome/chrome_installer_util.gypi
@@ -40,6 +40,8 @@
'installer/util/create_dir_work_item.h',
'installer/util/create_reg_key_work_item.cc',
'installer/util/create_reg_key_work_item.h',
+ 'installer/util/delete_old_versions.cc',
+ 'installer/util/delete_old_versions.h',
'installer/util/delete_reg_key_work_item.cc',
'installer/util/delete_reg_key_work_item.h',
'installer/util/delete_reg_value_work_item.cc',
diff --git a/chrome/installer/setup/install.cc b/chrome/installer/setup/install.cc
index a8660d3..9285629 100644
--- a/chrome/installer/setup/install.cc
+++ b/chrome/installer/setup/install.cc
@@ -553,13 +553,6 @@ InstallStatus InstallOrUpdateProduct(
installer_state.target_path().Append(installer::kChromeExe));
}
}
-
- installer_state.UpdateStage(installer::REMOVING_OLD_VERSIONS);
-
- installer_state.RemoveOldVersionDirectories(
- new_version,
- existing_version.get(),
- install_temp_path);
}
return result;
diff --git a/chrome/installer/setup/install_worker.cc b/chrome/installer/setup/install_worker.cc
index 5878c00..3825511 100644
--- a/chrome/installer/setup/install_worker.cc
+++ b/chrome/installer/setup/install_worker.cc
@@ -40,6 +40,7 @@
#include "chrome/installer/util/callback_work_item.h"
#include "chrome/installer/util/conditional_work_item_list.h"
#include "chrome/installer/util/create_reg_key_work_item.h"
+#include "chrome/installer/util/delete_old_versions.h"
#include "chrome/installer/util/firewall_manager_win.h"
#include "chrome/installer/util/google_update_constants.h"
#include "chrome/installer/util/helper.h"
@@ -1008,6 +1009,12 @@ bool AppendPostInstallTasks(const InstallerState& installer_state,
post_install_task_list->AddWorkItem(regular_update_work_items.release());
}
+ // If this is a regular update (chrome.exe not in use), this step will try to
+ // delete files from all versions except the one that was just installed.
+ // Otherwise, it will try to delete files from all versions except the one
+ // that is in use and the one that was just installed.
+ AddDeleteOldVersionsWorkItem(installer_state, post_install_task_list);
+
AddRegisterComDllWorkItemsForPackage(installer_state, current_version,
new_version, post_install_task_list);
diff --git a/chrome/installer/setup/setup_main.cc b/chrome/installer/setup/setup_main.cc
index bab5bd5..1de8649 100644
--- a/chrome/installer/setup/setup_main.cc
+++ b/chrome/installer/setup/setup_main.cc
@@ -53,6 +53,7 @@
#include "chrome/installer/setup/uninstall.h"
#include "chrome/installer/util/browser_distribution.h"
#include "chrome/installer/util/delete_after_reboot_helper.h"
+#include "chrome/installer/util/delete_old_versions.h"
#include "chrome/installer/util/delete_tree_work_item.h"
#include "chrome/installer/util/google_update_constants.h"
#include "chrome/installer/util/google_update_settings.h"
@@ -340,11 +341,11 @@ void AddExistingMultiInstalls(const InstallationState& original_state,
// This function is called when --rename-chrome-exe option is specified on
// setup.exe command line. This function assumes an in-use update has happened
-// for Chrome so there should be a file called new_chrome.exe on the file
-// system and a key called 'opv' in the registry. This function will move
+// for Chrome so there should be a file called new_chrome.exe on the file system
+// and a key called 'opv' in the registry. This function will move
// new_chrome.exe to chrome.exe and delete 'opv' key in one atomic operation.
-// This function also deletes elevation policies associated with the old version
-// if they exist.
+// This function also takes care of deleting files and elevation policies that
+// belong to old versions of Chrome.
installer::InstallStatus RenameChromeExecutables(
const InstallationState& original_state,
InstallerState* installer_state) {
@@ -404,6 +405,10 @@ installer::InstallStatus RenameChromeExecutables(
KEY_WOW64_32KEY,
google_update::kRegRenameCmdField);
}
+
+ // Delete old versions.
+ AddDeleteOldVersionsWorkItem(*installer_state, install_list.get());
+
installer::InstallStatus ret = installer::RENAME_SUCCESSFUL;
if (!install_list->Do()) {
LOG(ERROR) << "Renaming of executables failed. Rolling back any changes.";
diff --git a/chrome/installer/util/BUILD.gn b/chrome/installer/util/BUILD.gn
index 13ca030..1581959 100644
--- a/chrome/installer/util/BUILD.gn
+++ b/chrome/installer/util/BUILD.gn
@@ -159,6 +159,8 @@ static_library("with_no_strings") {
"create_dir_work_item.h",
"create_reg_key_work_item.cc",
"create_reg_key_work_item.h",
+ "delete_old_versions.cc",
+ "delete_old_versions.h",
"delete_reg_key_work_item.cc",
"delete_reg_key_work_item.h",
"delete_reg_value_work_item.cc",
@@ -291,6 +293,7 @@ if (is_win) {
"create_dir_work_item_unittest.cc",
"create_reg_key_work_item_unittest.cc",
"delete_after_reboot_helper_unittest.cc",
+ "delete_old_versions_unittest.cc",
"delete_reg_key_work_item_unittest.cc",
"delete_reg_value_work_item_unittest.cc",
"delete_tree_work_item_unittest.cc",
diff --git a/chrome/installer/util/delete_old_versions.cc b/chrome/installer/util/delete_old_versions.cc
new file mode 100644
index 0000000..222f510
--- /dev/null
+++ b/chrome/installer/util/delete_old_versions.cc
@@ -0,0 +1,277 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/installer/util/delete_old_versions.h"
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/file_version_info.h"
+#include "base/files/file.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/stl_util.h"
+#include "base/strings/string16.h"
+#include "base/version.h"
+#include "chrome/installer/util/callback_work_item.h"
+#include "chrome/installer/util/installer_state.h"
+#include "chrome/installer/util/util_constants.h"
+#include "chrome/installer/util/work_item_list.h"
+
+namespace installer {
+
+namespace {
+
+using PathVector = std::vector<base::FilePath>;
+using DirectorySet = std::set<base::FilePath>;
+using ExecutableMap = std::map<base::FilePath, PathVector>;
+
+// Returns the name of the version directory for executable |exe_path|.
+base::FilePath GetExecutableVersionDirName(const base::FilePath& exe_path) {
+ scoped_ptr<FileVersionInfo> file_version_info(
+ FileVersionInfo::CreateFileVersionInfo(exe_path));
+ if (!file_version_info.get())
+ return base::FilePath();
+ return base::FilePath(file_version_info->file_version());
+}
+
+// Returns the names of the old version directories found in |install_dir|. The
+// directories named after the version of chrome.exe or new_chrome.exe are
+// excluded.
+DirectorySet GetOldVersionDirectories(const base::FilePath& install_dir) {
+ const base::FilePath new_chrome_exe_version_dir_name =
+ GetExecutableVersionDirName(install_dir.Append(kChromeNewExe));
+ const base::FilePath chrome_exe_version_dir_name =
+ GetExecutableVersionDirName(install_dir.Append(kChromeExe));
+
+ DirectorySet directories;
+ base::FileEnumerator enum_directories(install_dir, false,
+ base::FileEnumerator::DIRECTORIES);
+ for (base::FilePath directory_path = enum_directories.Next();
+ !directory_path.empty(); directory_path = enum_directories.Next()) {
+ const base::FilePath directory_name = directory_path.BaseName();
+ const base::Version version(directory_name.AsUTF8Unsafe());
+ const size_t kNumChromeVersionComponents = 4;
+ if (version.IsValid() &&
+ version.components().size() == kNumChromeVersionComponents &&
+ directory_name != new_chrome_exe_version_dir_name &&
+ directory_name != chrome_exe_version_dir_name) {
+ directories.insert(directory_name);
+ }
+ }
+ return directories;
+}
+
+// Returns a map where the keys are version directory names and values are paths
+// of old_chrome*.exe executables found in |install_dir|.
+ExecutableMap GetOldExecutables(const base::FilePath& install_dir) {
+ ExecutableMap executables;
+ base::FileEnumerator enum_executables(install_dir, false,
+ base::FileEnumerator::FILES,
+ FILE_PATH_LITERAL("old_chrome*.exe"));
+ for (base::FilePath exe_path = enum_executables.Next(); !exe_path.empty();
+ exe_path = enum_executables.Next()) {
+ executables[GetExecutableVersionDirName(exe_path)].push_back(exe_path);
+ }
+ return executables;
+}
+
+// Deletes directories that are in |directories| and don't have a matching
+// executable in |executables|. Returns false if any such directories could not
+// be deleted.
+bool DeleteDirectoriesWithoutMatchingExecutable(
+ const DirectorySet& directories,
+ const ExecutableMap& executables,
+ const base::FilePath& install_dir) {
+ bool success = true;
+ for (const base::FilePath& directory_name : directories) {
+ // Delete the directory if it doesn't have a matching executable.
+ if (!ContainsKey(executables, directory_name)) {
+ const base::FilePath directory_path = install_dir.Append(directory_name);
+ LOG(WARNING) << "Attempting to delete stray directory "
+ << directory_path.value();
+ if (!base::DeleteFile(directory_path, true)) {
+ PLOG(ERROR) << "Failed to delete stray directory "
+ << directory_path.value();
+ success = false;
+ }
+ }
+ }
+ return success;
+}
+
+// Deletes executables that are in |executables| and don't have a matching
+// directory in |directories|. Returns false if any such files could not be
+// deleted.
+bool DeleteExecutablesWithoutMatchingDirectory(
+ const DirectorySet& directories,
+ const ExecutableMap& executables) {
+ bool success = true;
+ for (const auto& version_and_executables : executables) {
+ const auto& version_dir_name = version_and_executables.first;
+ const auto& executables_for_version = version_and_executables.second;
+
+ // Don't delete the executables if they have a matching directory.
+ if (ContainsValue(directories, version_dir_name))
+ continue;
+
+ // Delete executables for version |version_dir_name|.
+ for (const auto& executable_path : executables_for_version) {
+ const base::FilePath executable_name = executable_path.BaseName();
+ LOG(WARNING) << "Attempting to delete stray executable "
+ << executable_path.value();
+ if (!base::DeleteFile(executable_path, false)) {
+ PLOG(ERROR) << "Failed to delete stray executable "
+ << executable_path.value();
+ success = false;
+ }
+ }
+ }
+ return success;
+}
+
+// Opens |path| with options that prevent the file from being read or written
+// via another handle. As long as the returned object is alive, it is guaranteed
+// that |path| isn't in use. It can however be deleted.
+base::File GetFileLock(const base::FilePath& path) {
+ return base::File(path, base::File::FLAG_OPEN | base::File::FLAG_READ |
+ base::File::FLAG_EXCLUSIVE_READ |
+ base::File::FLAG_EXCLUSIVE_WRITE |
+ base::File::FLAG_SHARE_DELETE);
+}
+
+// Deletes |version_directory| and all executables in |version_executables| if
+// no .exe or .dll file for the version is in use. Returns false if any file
+// or directory for the version could not be deleted.
+bool DeleteVersion(const base::FilePath& version_directory,
+ const PathVector& version_executables) {
+ std::vector<base::File> locks;
+ PathVector locked_file_paths;
+
+ // Lock .exe/.dll files in |version_directory|.
+ base::FileEnumerator enum_version_directory(version_directory, true,
+ base::FileEnumerator::FILES);
+ for (base::FilePath path = enum_version_directory.Next(); !path.empty();
+ path = enum_version_directory.Next()) {
+ if (!path.MatchesExtension(FILE_PATH_LITERAL(".exe")) &&
+ !path.MatchesExtension(FILE_PATH_LITERAL(".dll"))) {
+ continue;
+ }
+ locks.push_back(GetFileLock(path));
+ if (!locks.back().IsValid()) {
+ LOG(WARNING) << "Failed to delete old version "
+ << version_directory.value() << " because " << path.value()
+ << " is in use.";
+ return false;
+ }
+ locked_file_paths.push_back(path);
+ }
+
+ // Lock executables in |version_executables|.
+ for (const base::FilePath& executable_path : version_executables) {
+ locks.push_back(GetFileLock(executable_path));
+ if (!locks.back().IsValid()) {
+ LOG(WARNING) << "Failed to delete old version "
+ << version_directory.value() << " because "
+ << executable_path.value() << " is in use.";
+ return false;
+ }
+ locked_file_paths.push_back(executable_path);
+ }
+
+ bool success = true;
+
+ // Delete locked files. The files won't actually be deleted until the locks
+ // are released.
+ for (const base::FilePath& locked_file_path : locked_file_paths) {
+ if (!base::DeleteFile(locked_file_path, false)) {
+ PLOG(ERROR) << "Failed to delete locked file "
+ << locked_file_path.value();
+ success = false;
+ }
+ }
+
+ // Release the locks, causing the locked files to actually be deleted. The
+ // version directory can't be deleted before this is done.
+ locks.clear();
+
+ // Delete the version directory.
+ if (!base::DeleteFile(version_directory, true)) {
+ PLOG(ERROR) << "Failed to delete version directory "
+ << version_directory.value();
+ success = false;
+ }
+
+ return success;
+}
+
+// For each executable in |executables| that has a matching directory in
+// |directories|, tries to delete the executable and the matching directory. No
+// deletion occurs for a given version if a .exe or .dll file for that version
+// is in use. Returns false if any directory/executables pair could not be
+// deleted.
+bool DeleteMatchingExecutablesAndDirectories(
+ const DirectorySet& directories,
+ const ExecutableMap& executables,
+ const base::FilePath& install_dir) {
+ bool success = true;
+ for (const auto& directory_name : directories) {
+ // Don't delete the version unless the directory has at least one matching
+ // executable.
+ auto version_executables_it = executables.find(directory_name);
+ if (version_executables_it == executables.end())
+ continue;
+
+ // Try to delete all files for the version.
+ success &= DeleteVersion(install_dir.Append(directory_name),
+ version_executables_it->second);
+ }
+ return success;
+}
+
+bool DeleteOldVersions(const base::FilePath& install_dir) {
+ const DirectorySet old_directories = GetOldVersionDirectories(install_dir);
+ const ExecutableMap old_executables = GetOldExecutables(install_dir);
+
+ bool success = true;
+ success &= DeleteDirectoriesWithoutMatchingExecutable(
+ old_directories, old_executables, install_dir);
+ success &= DeleteExecutablesWithoutMatchingDirectory(old_directories,
+ old_executables);
+ success &= DeleteMatchingExecutablesAndDirectories(
+ old_directories, old_executables, install_dir);
+
+ return success;
+}
+
+bool DeleteOldVersionsCallback(const InstallerState& installer_state,
+ const CallbackWorkItem& work_item) {
+ // No rollback is possible for this action.
+ if (work_item.IsRollback())
+ return true;
+
+ installer_state.UpdateStage(REMOVING_OLD_VERSIONS);
+ return DeleteOldVersions(installer_state.target_path());
+}
+
+} // namespace
+
+void AddDeleteOldVersionsWorkItem(const InstallerState& installer_state,
+ WorkItemList* work_item_list) {
+ DCHECK(work_item_list);
+
+ // TODO(fdoray): Launch a cleanup process when this fails during a regular
+ // update or after a rename. crbug.com/451546
+ work_item_list
+ ->AddCallbackWorkItem(base::Bind(&DeleteOldVersionsCallback,
+ base::ConstRef(installer_state)))
+ ->set_ignore_failure(true);
+}
+
+} // namespace installer
diff --git a/chrome/installer/util/delete_old_versions.h b/chrome/installer/util/delete_old_versions.h
new file mode 100644
index 0000000..098119b
--- /dev/null
+++ b/chrome/installer/util/delete_old_versions.h
@@ -0,0 +1,24 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_INSTALLER_UTIL_DELETE_OLD_VERSIONS_H_
+#define CHROME_INSTALLER_UTIL_DELETE_OLD_VERSIONS_H_
+
+class WorkItemList;
+
+namespace installer {
+
+class InstallerState;
+
+// Adds to |work_item_list| a WorkItem which deletes files that belong to old
+// versions of Chrome. chrome.exe, new_chrome.exe and their associated version
+// directories are never deleted. Also, no file is deleted for a given version
+// if a .exe or .dll file for that version is in use. |installer_state| must be
+// alive when |work_item_list| is executed.
+void AddDeleteOldVersionsWorkItem(const InstallerState& installer_state,
+ WorkItemList* work_item_list);
+
+} // namespace installer
+
+#endif // CHROME_INSTALLER_UTIL_DELETE_OLD_VERSIONS_H_
diff --git a/chrome/installer/util/delete_old_versions_unittest.cc b/chrome/installer/util/delete_old_versions_unittest.cc
new file mode 100644
index 0000000..7a9a58e
--- /dev/null
+++ b/chrome/installer/util/delete_old_versions_unittest.cc
@@ -0,0 +1,286 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/installer/util/delete_old_versions.h"
+
+#include <set>
+
+#include "base/files/file.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/path_service.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/version.h"
+#include "chrome/installer/test/alternate_version_generator.h"
+#include "chrome/installer/util/installer_state.h"
+#include "chrome/installer/util/util_constants.h"
+#include "chrome/installer/util/work_item.h"
+#include "chrome/installer/util/work_item_list.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace installer {
+
+namespace {
+
+class MockInstallerState : public InstallerState {
+ public:
+ explicit MockInstallerState(const base::FilePath& target_path)
+ : InstallerState(InstallerState::USER_LEVEL) {
+ target_path_ = target_path;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockInstallerState);
+};
+
+const base::char16 kVersionA[] = L"47.0.0.0";
+const base::char16 kVersionB[] = L"48.0.0.0";
+
+class DeleteOldVersionsTest : public testing::Test {
+ protected:
+ DeleteOldVersionsTest() = default;
+
+ void DeleteOldVersions() {
+ MockInstallerState installer_state(install_dir());
+ scoped_ptr<WorkItemList> work_item_list(WorkItem::CreateWorkItemList());
+ AddDeleteOldVersionsWorkItem(installer_state, work_item_list.get());
+ work_item_list->Do();
+ }
+
+ bool CreateInstallDir() { return install_dir_.CreateUniqueTempDir(); }
+
+ // Creates an executable with |name| and |version| in |install_dir_|.
+ bool CreateExecutable(const base::string16& name,
+ const base::string16& version) {
+ base::FilePath current_exe_path;
+ return base::PathService::Get(base::FILE_EXE, &current_exe_path) &&
+ upgrade_test::GenerateSpecificPEFileVersion(
+ current_exe_path, install_dir().Append(name),
+ base::Version(base::UTF16ToUTF8(version)));
+ }
+
+ // Creates a version directory named |name| in |install_dir_|.
+ bool CreateVersionDirectory(const base::string16& name) {
+ static const char kDummyContent[] = "dummy";
+ const base::FilePath version_dir_path(install_dir().Append(name));
+
+ return base::CreateDirectory(install_dir().Append(name)) &&
+ base::CreateDirectory(version_dir_path.Append(L"Installer")) &&
+ base::WriteFile(version_dir_path.Append(L"chrome.dll"),
+ kDummyContent, sizeof(kDummyContent)) &&
+ base::WriteFile(version_dir_path.Append(L"nacl64.exe"),
+ kDummyContent, sizeof(kDummyContent)) &&
+ base::WriteFile(version_dir_path.Append(L"icudtl.dat"),
+ kDummyContent, sizeof(kDummyContent)) &&
+ base::WriteFile(version_dir_path.Append(L"Installer\\setup.exe"),
+ kDummyContent, sizeof(kDummyContent));
+ }
+
+ // Returns the relative paths of all files and directories in |install_dir_|.
+ using FilePathSet = std::set<base::FilePath>;
+ FilePathSet GetInstallDirContent() const {
+ std::set<base::FilePath> content;
+ base::FileEnumerator file_enumerator(
+ install_dir(), true,
+ base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
+ for (base::FilePath path = file_enumerator.Next(); !path.empty();
+ path = file_enumerator.Next()) {
+ DCHECK(base::StartsWith(path.value(), install_dir().value(),
+ base::CompareCase::SENSITIVE));
+ content.insert(base::FilePath(
+ path.value().substr(install_dir().value().size() + 1)));
+ }
+ return content;
+ }
+
+ // Adds to |file_path_set| all files and directories that are expected to be
+ // found in the version directory |version| before any attempt to delete it.
+ void AddVersionFiles(const base::string16& version,
+ FilePathSet* file_path_set) {
+ file_path_set->insert(base::FilePath(version));
+ file_path_set->insert(base::FilePath(version).Append(L"chrome.dll"));
+ file_path_set->insert(base::FilePath(version).Append(L"nacl64.exe"));
+ file_path_set->insert(base::FilePath(version).Append(L"icudtl.dat"));
+ file_path_set->insert(base::FilePath(version).Append(L"Installer"));
+ file_path_set->insert(
+ base::FilePath(version).Append(L"Installer\\setup.exe"));
+ }
+
+ base::FilePath install_dir() const { return install_dir_.path(); }
+
+ private:
+ base::ScopedTempDir install_dir_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeleteOldVersionsTest);
+};
+
+} // namespace
+
+// An old executable without a matching directory should be deleted.
+TEST_F(DeleteOldVersionsTest, DeleteOldExecutableWithoutMatchingDirectory) {
+ ASSERT_TRUE(CreateInstallDir());
+ ASSERT_TRUE(CreateExecutable(installer::kChromeOldExe, kVersionA));
+
+ DeleteOldVersions();
+ EXPECT_TRUE(GetInstallDirContent().empty());
+}
+
+// chrome.exe and new_chrome.exe should never be deleted.
+TEST_F(DeleteOldVersionsTest, DeleteNewExecutablesWithoutMatchingDirectory) {
+ ASSERT_TRUE(CreateInstallDir());
+ ASSERT_TRUE(CreateExecutable(installer::kChromeExe, kVersionA));
+ ASSERT_TRUE(CreateExecutable(installer::kChromeNewExe, kVersionB));
+
+ DeleteOldVersions();
+ FilePathSet expected_install_dir_content;
+ expected_install_dir_content.insert(base::FilePath(installer::kChromeExe));
+ expected_install_dir_content.insert(base::FilePath(installer::kChromeNewExe));
+ EXPECT_EQ(expected_install_dir_content, GetInstallDirContent());
+}
+
+// A directory without a matching executable should be deleted.
+TEST_F(DeleteOldVersionsTest, DeleteDirectoryWithoutMatchingExecutable) {
+ ASSERT_TRUE(CreateInstallDir());
+ ASSERT_TRUE(CreateVersionDirectory(kVersionA));
+
+ DeleteOldVersions();
+ EXPECT_TRUE(GetInstallDirContent().empty());
+}
+
+// A pair of matching old executable/version directory that is not in use should
+// be deleted.
+TEST_F(DeleteOldVersionsTest, DeleteOldExecutableWithMatchingDirectory) {
+ ASSERT_TRUE(CreateInstallDir());
+ ASSERT_TRUE(CreateExecutable(installer::kChromeOldExe, kVersionA));
+ ASSERT_TRUE(CreateVersionDirectory(kVersionA));
+
+ DeleteOldVersions();
+ EXPECT_TRUE(GetInstallDirContent().empty());
+}
+
+// chrome.exe, new_chrome.exe and their matching version directories should
+// never be deleted.
+TEST_F(DeleteOldVersionsTest, DeleteNewExecutablesWithMatchingDirectory) {
+ ASSERT_TRUE(CreateInstallDir());
+ ASSERT_TRUE(CreateExecutable(installer::kChromeExe, kVersionA));
+ ASSERT_TRUE(CreateVersionDirectory(kVersionA));
+ ASSERT_TRUE(CreateExecutable(installer::kChromeNewExe, kVersionB));
+ ASSERT_TRUE(CreateVersionDirectory(kVersionB));
+
+ DeleteOldVersions();
+
+ FilePathSet expected_install_dir_content;
+ expected_install_dir_content.insert(base::FilePath(installer::kChromeExe));
+ AddVersionFiles(kVersionA, &expected_install_dir_content);
+ expected_install_dir_content.insert(base::FilePath(installer::kChromeNewExe));
+ AddVersionFiles(kVersionB, &expected_install_dir_content);
+ EXPECT_EQ(expected_install_dir_content, GetInstallDirContent());
+}
+
+// chrome.exe, new_chrome.exe and their matching version directories should
+// never be deleted, even when files named old_chrome*.exe have the same
+// versions as chrome.exe/new_chrome.exe. The old_chrome*.exe files, however,
+// should be deleted.
+TEST_F(DeleteOldVersionsTest,
+ DeleteNewExecutablesWithMatchingDirectoryAndOldExecutables) {
+ ASSERT_TRUE(CreateInstallDir());
+ ASSERT_TRUE(CreateExecutable(installer::kChromeExe, kVersionA));
+ ASSERT_TRUE(CreateVersionDirectory(kVersionA));
+ ASSERT_TRUE(CreateExecutable(installer::kChromeNewExe, kVersionB));
+ ASSERT_TRUE(CreateVersionDirectory(kVersionB));
+ ASSERT_TRUE(CreateExecutable(L"old_chrome.exe", kVersionA));
+ ASSERT_TRUE(CreateExecutable(L"old_chrome2.exe", kVersionB));
+
+ DeleteOldVersions();
+
+ FilePathSet expected_install_dir_content;
+ expected_install_dir_content.insert(base::FilePath(installer::kChromeExe));
+ AddVersionFiles(kVersionA, &expected_install_dir_content);
+ expected_install_dir_content.insert(base::FilePath(installer::kChromeNewExe));
+ AddVersionFiles(kVersionB, &expected_install_dir_content);
+ EXPECT_EQ(expected_install_dir_content, GetInstallDirContent());
+}
+
+// No file should be deleted for a given version if the executable is in use.
+TEST_F(DeleteOldVersionsTest, DeleteVersionWithExecutableInUse) {
+ ASSERT_TRUE(CreateInstallDir());
+ ASSERT_TRUE(CreateExecutable(installer::kChromeOldExe, kVersionA));
+ ASSERT_TRUE(CreateVersionDirectory(kVersionA));
+
+ base::File file_in_use(install_dir().Append(installer::kChromeOldExe),
+ base::File::FLAG_OPEN | base::File::FLAG_READ);
+ ASSERT_TRUE(file_in_use.IsValid());
+
+ DeleteOldVersions();
+
+ FilePathSet expected_install_dir_content;
+ expected_install_dir_content.insert(base::FilePath(installer::kChromeOldExe));
+ AddVersionFiles(kVersionA, &expected_install_dir_content);
+ EXPECT_EQ(expected_install_dir_content, GetInstallDirContent());
+}
+
+// No file should be deleted for a given version if a .dll file in the version
+// directory is in use.
+TEST_F(DeleteOldVersionsTest, DeleteVersionWithVersionDirectoryDllInUse) {
+ ASSERT_TRUE(CreateInstallDir());
+ ASSERT_TRUE(CreateExecutable(installer::kChromeOldExe, kVersionA));
+ ASSERT_TRUE(CreateVersionDirectory(kVersionA));
+
+ base::File file_in_use(install_dir().Append(kVersionA).Append(L"chrome.dll"),
+ base::File::FLAG_OPEN | base::File::FLAG_READ);
+ ASSERT_TRUE(file_in_use.IsValid());
+
+ DeleteOldVersions();
+
+ FilePathSet expected_install_dir_content;
+ expected_install_dir_content.insert(base::FilePath(installer::kChromeOldExe));
+ AddVersionFiles(kVersionA, &expected_install_dir_content);
+ EXPECT_EQ(expected_install_dir_content, GetInstallDirContent());
+}
+
+// No file should be deleted for a given version if a .exe file in the version
+// directory is in use.
+TEST_F(DeleteOldVersionsTest, DeleteVersionWithVersionDirectoryExeInUse) {
+ ASSERT_TRUE(CreateInstallDir());
+ ASSERT_TRUE(CreateExecutable(installer::kChromeOldExe, kVersionA));
+ ASSERT_TRUE(CreateVersionDirectory(kVersionA));
+
+ base::File file_in_use(
+ install_dir().Append(kVersionA).Append(L"Installer\\setup.exe"),
+ base::File::FLAG_OPEN | base::File::FLAG_READ);
+ ASSERT_TRUE(file_in_use.IsValid());
+
+ DeleteOldVersions();
+
+ FilePathSet expected_install_dir_content;
+ expected_install_dir_content.insert(base::FilePath(installer::kChromeOldExe));
+ AddVersionFiles(kVersionA, &expected_install_dir_content);
+ EXPECT_EQ(expected_install_dir_content, GetInstallDirContent());
+}
+
+// If an installation directory contains a file named chrome.exe with a matching
+// directory v1 and a file name old_chrome.exe with a matching directory v2,
+// old_chrome.exe and v2 should be deleted but chrome.exe and v1 shouldn't.
+TEST_F(DeleteOldVersionsTest, TypicalAfterRenameState) {
+ ASSERT_TRUE(CreateInstallDir());
+ ASSERT_TRUE(CreateExecutable(installer::kChromeOldExe, kVersionA));
+ ASSERT_TRUE(CreateVersionDirectory(kVersionA));
+ ASSERT_TRUE(CreateExecutable(installer::kChromeExe, kVersionB));
+ ASSERT_TRUE(CreateVersionDirectory(kVersionB));
+
+ DeleteOldVersions();
+
+ FilePathSet expected_install_dir_content;
+ expected_install_dir_content.insert(base::FilePath(installer::kChromeExe));
+ AddVersionFiles(kVersionB, &expected_install_dir_content);
+ EXPECT_EQ(expected_install_dir_content, GetInstallDirContent());
+}
+
+} // namespace installer
diff --git a/chrome/installer/util/installer_state.cc b/chrome/installer/util/installer_state.cc
index 3da5a99..f2d4eb2 100644
--- a/chrome/installer/util/installer_state.cc
+++ b/chrome/installer/util/installer_state.cc
@@ -11,8 +11,6 @@
#include <utility>
#include "base/command_line.h"
-#include "base/file_version_info.h"
-#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/macros.h"
@@ -514,69 +512,6 @@ bool InstallerState::AnyExistsAndIsInUse(const InstallationState& machine_state,
return false;
}
-void InstallerState::GetExistingExeVersions(
- std::set<std::string>* existing_versions) const {
-
- static const wchar_t* const kChromeFilenames[] = {
- installer::kChromeExe,
- installer::kChromeNewExe,
- installer::kChromeOldExe,
- };
-
- for (size_t i = 0; i < arraysize(kChromeFilenames); ++i) {
- base::FilePath chrome_exe(target_path().Append(kChromeFilenames[i]));
- scoped_ptr<FileVersionInfo> file_version_info(
- FileVersionInfo::CreateFileVersionInfo(chrome_exe));
- if (file_version_info) {
- base::string16 version_string = file_version_info->file_version();
- if (!version_string.empty() && base::IsStringASCII(version_string))
- existing_versions->insert(base::UTF16ToASCII(version_string));
- }
- }
-}
-
-void InstallerState::RemoveOldVersionDirectories(
- const Version& new_version,
- Version* existing_version,
- const base::FilePath& temp_path) const {
- Version version;
- scoped_ptr<WorkItem> item;
-
- std::set<std::string> existing_version_strings;
- existing_version_strings.insert(new_version.GetString());
- if (existing_version)
- existing_version_strings.insert(existing_version->GetString());
-
- // Make sure not to delete any version dir that is "referenced" by an existing
- // Chrome executable.
- GetExistingExeVersions(&existing_version_strings);
-
- // Try to delete all directories that are not in the set we care to keep.
- base::FileEnumerator version_enum(target_path(), false,
- base::FileEnumerator::DIRECTORIES);
- for (base::FilePath next_version = version_enum.Next(); !next_version.empty();
- next_version = version_enum.Next()) {
- base::FilePath dir_name(next_version.BaseName());
- version = Version(base::UTF16ToASCII(dir_name.value()));
- // Delete the version folder if it is less than the new version and not
- // equal to the old version (if we have an old version).
- if (version.IsValid() &&
- existing_version_strings.count(version.GetString()) == 0) {
- // Note: temporarily log old version deletion at ERROR level to make it
- // more likely we see this in the installer log.
- LOG(ERROR) << "Deleting old version directory: " << next_version.value();
-
- // Attempt to recursively delete the old version dir.
- bool delete_succeeded = base::DeleteFile(next_version, true);
-
- // Note: temporarily log old version deletion at ERROR level to make it
- // more likely we see this in the installer log.
- LOG_IF(ERROR, !delete_succeeded)
- << "Failed to delete old version directory: " << next_version.value();
- }
- }
-}
-
void InstallerState::AddComDllList(
std::vector<base::FilePath>* com_dll_list) const {
for (auto* product : products_)
diff --git a/chrome/installer/util/installer_state.h b/chrome/installer/util/installer_state.h
index 2b62465..1de3000 100644
--- a/chrome/installer/util/installer_state.h
+++ b/chrome/installer/util/installer_state.h
@@ -7,7 +7,6 @@
#include <stdint.h>
-#include <set>
#include <string>
#include <vector>
@@ -178,13 +177,6 @@ class InstallerState {
// (for example <target_path>\Google\Chrome\Application\<Version>\Installer)
base::FilePath GetInstallerDirectory(const base::Version& version) const;
- // Try to delete all directories under |temp_path| whose versions are less
- // than |new_version| and not equal to |existing_version|. |existing_version|
- // may be NULL.
- void RemoveOldVersionDirectories(const base::Version& new_version,
- base::Version* existing_version,
- const base::FilePath& temp_path) const;
-
// Adds to |com_dll_list| the list of COM DLLs that are to be registered
// and/or unregistered. The list may be empty.
void AddComDllList(std::vector<base::FilePath>* com_dll_list) const;
@@ -243,11 +235,6 @@ class InstallerState {
bool IsMultiInstallUpdate(const MasterPreferences& prefs,
const InstallationState& machine_state);
- // Enumerates all files named one of
- // [chrome.exe, old_chrome.exe, new_chrome.exe] in target_path_ and
- // returns their version numbers in a set.
- void GetExistingExeVersions(std::set<std::string>* existing_versions) const;
-
// Sets this object's level and updates the root_key_ accordingly.
void set_level(Level level);
diff --git a/chrome/installer/util/installer_state_unittest.cc b/chrome/installer/util/installer_state_unittest.cc
index 1ced49e..91d8ee3 100644
--- a/chrome/installer/util/installer_state_unittest.cc
+++ b/chrome/installer/util/installer_state_unittest.cc
@@ -11,7 +11,6 @@
#include "base/base_paths.h"
#include "base/command_line.h"
-#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
@@ -70,9 +69,6 @@ class MockInstallerState : public InstallerState {
const Version& critical_update_version() const {
return critical_update_version_;
}
- void GetExistingExeVersions(std::set<std::string>* existing_version_strings) {
- return InstallerState::GetExistingExeVersions(existing_version_strings);
- }
};
// Simple function to dump some text into a new file.
@@ -97,249 +93,6 @@ void BuildSingleChromeState(const base::FilePath& target_dir,
!= NULL);
}
-wchar_t text_content_1[] = L"delete me";
-wchar_t text_content_2[] = L"delete me as well";
-
-// Delete version directories. Everything lower than the given version
-// should be deleted.
-TEST_F(InstallerStateTest, Delete) {
- // TODO(grt): move common stuff into the test fixture.
- // Create a Chrome dir
- base::FilePath chrome_dir(test_dir_.path());
- chrome_dir = chrome_dir.AppendASCII("chrome");
- base::CreateDirectory(chrome_dir);
- ASSERT_TRUE(base::PathExists(chrome_dir));
-
- base::FilePath chrome_dir_1(chrome_dir);
- chrome_dir_1 = chrome_dir_1.AppendASCII("1.0.1.0");
- base::CreateDirectory(chrome_dir_1);
- ASSERT_TRUE(base::PathExists(chrome_dir_1));
-
- base::FilePath chrome_dir_2(chrome_dir);
- chrome_dir_2 = chrome_dir_2.AppendASCII("1.0.2.0");
- base::CreateDirectory(chrome_dir_2);
- ASSERT_TRUE(base::PathExists(chrome_dir_2));
-
- base::FilePath chrome_dir_3(chrome_dir);
- chrome_dir_3 = chrome_dir_3.AppendASCII("1.0.3.0");
- base::CreateDirectory(chrome_dir_3);
- ASSERT_TRUE(base::PathExists(chrome_dir_3));
-
- base::FilePath chrome_dir_4(chrome_dir);
- chrome_dir_4 = chrome_dir_4.AppendASCII("1.0.4.0");
- base::CreateDirectory(chrome_dir_4);
- ASSERT_TRUE(base::PathExists(chrome_dir_4));
-
- base::FilePath chrome_dll_1(chrome_dir_1);
- chrome_dll_1 = chrome_dll_1.AppendASCII("chrome.dll");
- CreateTextFile(chrome_dll_1.value(), text_content_1);
- ASSERT_TRUE(base::PathExists(chrome_dll_1));
-
- base::FilePath chrome_dll_2(chrome_dir_2);
- chrome_dll_2 = chrome_dll_2.AppendASCII("chrome.dll");
- CreateTextFile(chrome_dll_2.value(), text_content_1);
- ASSERT_TRUE(base::PathExists(chrome_dll_2));
-
- base::FilePath chrome_dll_3(chrome_dir_3);
- chrome_dll_3 = chrome_dll_3.AppendASCII("chrome.dll");
- CreateTextFile(chrome_dll_3.value(), text_content_1);
- ASSERT_TRUE(base::PathExists(chrome_dll_3));
-
- base::FilePath chrome_dll_4(chrome_dir_4);
- chrome_dll_4 = chrome_dll_4.AppendASCII("chrome.dll");
- CreateTextFile(chrome_dll_4.value(), text_content_1);
- ASSERT_TRUE(base::PathExists(chrome_dll_4));
-
- MockInstallerState installer_state;
- BuildSingleChromeState(chrome_dir, &installer_state);
- Version latest_version("1.0.4.0");
- {
- base::ScopedTempDir temp_dir;
- ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- installer_state.RemoveOldVersionDirectories(latest_version, NULL,
- temp_dir.path());
- }
-
- // old versions should be gone
- EXPECT_FALSE(base::PathExists(chrome_dir_1));
- EXPECT_FALSE(base::PathExists(chrome_dir_2));
- EXPECT_FALSE(base::PathExists(chrome_dir_3));
- // the latest version should stay
- EXPECT_TRUE(base::PathExists(chrome_dll_4));
-}
-
-// Delete older version directories, keeping the one in used intact.
-TEST_F(InstallerStateTest, DeleteInUsed) {
- // Create a Chrome dir
- base::FilePath chrome_dir(test_dir_.path());
- chrome_dir = chrome_dir.AppendASCII("chrome");
- base::CreateDirectory(chrome_dir);
- ASSERT_TRUE(base::PathExists(chrome_dir));
-
- base::FilePath chrome_dir_1(chrome_dir);
- chrome_dir_1 = chrome_dir_1.AppendASCII("1.0.1.0");
- base::CreateDirectory(chrome_dir_1);
- ASSERT_TRUE(base::PathExists(chrome_dir_1));
-
- base::FilePath chrome_dir_2(chrome_dir);
- chrome_dir_2 = chrome_dir_2.AppendASCII("1.0.2.0");
- base::CreateDirectory(chrome_dir_2);
- ASSERT_TRUE(base::PathExists(chrome_dir_2));
-
- base::FilePath chrome_dir_3(chrome_dir);
- chrome_dir_3 = chrome_dir_3.AppendASCII("1.0.3.0");
- base::CreateDirectory(chrome_dir_3);
- ASSERT_TRUE(base::PathExists(chrome_dir_3));
-
- base::FilePath chrome_dir_4(chrome_dir);
- chrome_dir_4 = chrome_dir_4.AppendASCII("1.0.4.0");
- base::CreateDirectory(chrome_dir_4);
- ASSERT_TRUE(base::PathExists(chrome_dir_4));
-
- base::FilePath chrome_dll_1(chrome_dir_1);
- chrome_dll_1 = chrome_dll_1.AppendASCII("chrome.dll");
- CreateTextFile(chrome_dll_1.value(), text_content_1);
- ASSERT_TRUE(base::PathExists(chrome_dll_1));
-
- base::FilePath chrome_dll_2(chrome_dir_2);
- chrome_dll_2 = chrome_dll_2.AppendASCII("chrome.dll");
- CreateTextFile(chrome_dll_2.value(), text_content_1);
- ASSERT_TRUE(base::PathExists(chrome_dll_2));
-
- // Open the file to make it in use.
- std::ofstream file;
- file.open(chrome_dll_2.value().c_str());
-
- base::FilePath chrome_othera_2(chrome_dir_2);
- chrome_othera_2 = chrome_othera_2.AppendASCII("othera.dll");
- CreateTextFile(chrome_othera_2.value(), text_content_2);
- ASSERT_TRUE(base::PathExists(chrome_othera_2));
-
- base::FilePath chrome_otherb_2(chrome_dir_2);
- chrome_otherb_2 = chrome_otherb_2.AppendASCII("otherb.dll");
- CreateTextFile(chrome_otherb_2.value(), text_content_2);
- ASSERT_TRUE(base::PathExists(chrome_otherb_2));
-
- base::FilePath chrome_dll_3(chrome_dir_3);
- chrome_dll_3 = chrome_dll_3.AppendASCII("chrome.dll");
- CreateTextFile(chrome_dll_3.value(), text_content_1);
- ASSERT_TRUE(base::PathExists(chrome_dll_3));
-
- base::FilePath chrome_dll_4(chrome_dir_4);
- chrome_dll_4 = chrome_dll_4.AppendASCII("chrome.dll");
- CreateTextFile(chrome_dll_4.value(), text_content_1);
- ASSERT_TRUE(base::PathExists(chrome_dll_4));
-
- MockInstallerState installer_state;
- BuildSingleChromeState(chrome_dir, &installer_state);
- Version latest_version("1.0.4.0");
- Version existing_version("1.0.1.0");
- {
- base::ScopedTempDir temp_dir;
- ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- installer_state.RemoveOldVersionDirectories(latest_version,
- &existing_version,
- temp_dir.path());
- }
-
- // the version defined as the existing version should stay
- EXPECT_TRUE(base::PathExists(chrome_dir_1));
- // old versions not in used should be gone
- EXPECT_FALSE(base::PathExists(chrome_dir_3));
- // every thing under in used version should stay
- EXPECT_TRUE(base::PathExists(chrome_dir_2));
- EXPECT_TRUE(base::PathExists(chrome_dll_2));
- EXPECT_TRUE(base::PathExists(chrome_othera_2));
- EXPECT_TRUE(base::PathExists(chrome_otherb_2));
- // the latest version should stay
- EXPECT_TRUE(base::PathExists(chrome_dll_4));
-}
-
-// Tests a few basic things of the Package class. Makes sure that the path
-// operations are correct
-TEST_F(InstallerStateTest, Basic) {
- const bool multi_install = false;
- const bool system_level = true;
- base::CommandLine cmd_line = base::CommandLine::FromString(
- std::wstring(L"setup.exe") +
- (multi_install ? L" --multi-install --chrome" : L"") +
- (system_level ? L" --system-level" : L""));
- MasterPreferences prefs(cmd_line);
- InstallationState machine_state;
- machine_state.Initialize();
- MockInstallerState installer_state;
- installer_state.Initialize(cmd_line, prefs, machine_state);
- installer_state.set_target_path(test_dir_.path());
- EXPECT_EQ(test_dir_.path().value(), installer_state.target_path().value());
- EXPECT_EQ(1U, installer_state.products().size());
-
- const char kOldVersion[] = "1.2.3.4";
- const char kNewVersion[] = "2.3.4.5";
-
- Version new_version(kNewVersion);
- Version old_version(kOldVersion);
- ASSERT_TRUE(new_version.IsValid());
- ASSERT_TRUE(old_version.IsValid());
-
- base::FilePath installer_dir(
- installer_state.GetInstallerDirectory(new_version));
- EXPECT_FALSE(installer_dir.empty());
-
- base::FilePath new_version_dir(installer_state.target_path().Append(
- base::UTF8ToWide(new_version.GetString())));
- base::FilePath old_version_dir(installer_state.target_path().Append(
- base::UTF8ToWide(old_version.GetString())));
-
- EXPECT_FALSE(base::PathExists(new_version_dir));
- EXPECT_FALSE(base::PathExists(old_version_dir));
-
- EXPECT_FALSE(base::PathExists(installer_dir));
- base::CreateDirectory(installer_dir);
- EXPECT_TRUE(base::PathExists(new_version_dir));
-
- base::CreateDirectory(old_version_dir);
- EXPECT_TRUE(base::PathExists(old_version_dir));
-
- // Create a fake chrome.dll key file in the old version directory. This
- // should prevent the old version directory from getting deleted.
- base::FilePath old_chrome_dll(old_version_dir.Append(installer::kChromeDll));
- EXPECT_FALSE(base::PathExists(old_chrome_dll));
-
- // Hold on to the file exclusively to prevent the directory from
- // being deleted.
- base::win::ScopedHandle file(
- ::CreateFile(old_chrome_dll.value().c_str(), GENERIC_READ,
- 0, NULL, OPEN_ALWAYS, 0, NULL));
- EXPECT_TRUE(file.IsValid());
- EXPECT_TRUE(base::PathExists(old_chrome_dll));
-
- base::ScopedTempDir temp_dir;
- ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
-
- // Don't explicitly tell the directory cleanup logic not to delete the
- // old version, rely on the key files to keep it around.
- installer_state.RemoveOldVersionDirectories(new_version,
- NULL,
- temp_dir.path());
-
- // The old directory should still exist.
- EXPECT_TRUE(base::PathExists(old_version_dir));
- EXPECT_TRUE(base::PathExists(new_version_dir));
-
- // Now close the file handle to make it possible to delete our key file.
- file.Close();
-
- installer_state.RemoveOldVersionDirectories(new_version,
- NULL,
- temp_dir.path());
- // The new directory should still exist.
- EXPECT_TRUE(base::PathExists(new_version_dir));
-
- // Now, the old directory and key file should be gone.
- EXPECT_FALSE(base::PathExists(old_chrome_dll));
- EXPECT_FALSE(base::PathExists(old_version_dir));
-}
-
TEST_F(InstallerStateTest, WithProduct) {
const bool multi_install = false;
const bool system_level = true;
@@ -507,112 +260,6 @@ TEST_F(InstallerStateTest, IsFileInUse) {
EXPECT_FALSE(MockInstallerState::IsFileInUse(temp_file));
}
-TEST_F(InstallerStateTest, RemoveOldVersionDirs) {
- MockInstallerState installer_state;
- installer_state.set_target_path(test_dir_.path());
- EXPECT_EQ(test_dir_.path().value(), installer_state.target_path().value());
-
- const char kOldVersion[] = "2.0.0.0";
- const char kNewVersion[] = "3.0.0.0";
- const char kOldChromeExeVersion[] = "2.1.0.0";
- const char kChromeExeVersion[] = "2.1.1.1";
- const char kNewChromeExeVersion[] = "3.0.0.0";
-
- Version new_version(kNewVersion);
- Version old_version(kOldVersion);
- Version old_chrome_exe_version(kOldChromeExeVersion);
- Version chrome_exe_version(kChromeExeVersion);
- Version new_chrome_exe_version(kNewChromeExeVersion);
-
- ASSERT_TRUE(new_version.IsValid());
- ASSERT_TRUE(old_version.IsValid());
- ASSERT_TRUE(old_chrome_exe_version.IsValid());
- ASSERT_TRUE(chrome_exe_version.IsValid());
- ASSERT_TRUE(new_chrome_exe_version.IsValid());
-
- // Set up a bunch of version dir paths.
- base::FilePath version_dirs[] = {
- installer_state.target_path().Append(L"1.2.3.4"),
- installer_state.target_path().Append(L"1.2.3.5"),
- installer_state.target_path().Append(L"1.2.3.6"),
- installer_state.target_path().AppendASCII(kOldVersion),
- installer_state.target_path().AppendASCII(kOldChromeExeVersion),
- installer_state.target_path().Append(L"2.1.1.0"),
- installer_state.target_path().AppendASCII(kChromeExeVersion),
- installer_state.target_path().AppendASCII(kNewVersion),
- installer_state.target_path().Append(L"3.9.1.1"),
- };
-
- // Create the version directories.
- for (size_t i = 0; i < arraysize(version_dirs); i++) {
- base::CreateDirectory(version_dirs[i]);
- EXPECT_TRUE(base::PathExists(version_dirs[i]));
- }
-
- // Create exes with the appropriate version resource.
- // Use the current test exe as a baseline.
- base::FilePath exe_path;
- ASSERT_TRUE(PathService::Get(base::FILE_EXE, &exe_path));
-
- struct target_info {
- base::FilePath target_file;
- const Version& target_version;
- } targets[] = {
- { installer_state.target_path().Append(installer::kChromeOldExe),
- old_chrome_exe_version },
- { installer_state.target_path().Append(installer::kChromeExe),
- chrome_exe_version },
- { installer_state.target_path().Append(installer::kChromeNewExe),
- new_chrome_exe_version },
- };
- for (size_t i = 0; i < arraysize(targets); ++i) {
- ASSERT_TRUE(upgrade_test::GenerateSpecificPEFileVersion(
- exe_path, targets[i].target_file, targets[i].target_version));
- }
-
- // Call GetExistingExeVersions, validate that picks up the
- // exe resources.
- std::set<std::string> expected_exe_versions;
- expected_exe_versions.insert(kOldChromeExeVersion);
- expected_exe_versions.insert(kChromeExeVersion);
- expected_exe_versions.insert(kNewChromeExeVersion);
-
- std::set<std::string> actual_exe_versions;
- installer_state.GetExistingExeVersions(&actual_exe_versions);
- EXPECT_EQ(expected_exe_versions, actual_exe_versions);
-
- // Call RemoveOldVersionDirectories
- installer_state.RemoveOldVersionDirectories(new_version,
- &old_version,
- installer_state.target_path());
-
- // What we expect to have left.
- std::set<std::string> expected_remaining_dirs;
- expected_remaining_dirs.insert(kOldVersion);
- expected_remaining_dirs.insert(kNewVersion);
- expected_remaining_dirs.insert(kOldChromeExeVersion);
- expected_remaining_dirs.insert(kChromeExeVersion);
- expected_remaining_dirs.insert(kNewChromeExeVersion);
-
- // Enumerate dirs in target_path(), ensure only desired remain.
- base::FileEnumerator version_enum(installer_state.target_path(), false,
- base::FileEnumerator::DIRECTORIES);
- for (base::FilePath next_version = version_enum.Next(); !next_version.empty();
- next_version = version_enum.Next()) {
- base::FilePath dir_name(next_version.BaseName());
- Version version(base::UTF16ToASCII(dir_name.value()));
- if (version.IsValid()) {
- EXPECT_TRUE(expected_remaining_dirs.erase(version.GetString()))
- << "Unexpected version dir found: " << version.GetString();
- }
- }
-
- std::set<std::string>::const_iterator iter(
- expected_remaining_dirs.begin());
- for (; iter != expected_remaining_dirs.end(); ++iter)
- ADD_FAILURE() << "Expected to find version dir for " << *iter;
-}
-
TEST_F(InstallerStateTest, InitializeTwice) {
// Override these paths so that they can be found after the registry override
// manager is in place.