Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 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 "chrome/installer/util/delete_old_versions.h" | |
| 6 | |
| 7 #include <set> | |
| 8 | |
| 9 #include "base/files/file.h" | |
| 10 #include "base/files/file_enumerator.h" | |
| 11 #include "base/files/file_util.h" | |
| 12 #include "base/files/scoped_temp_dir.h" | |
| 13 #include "base/logging.h" | |
| 14 #include "base/macros.h" | |
| 15 #include "base/memory/scoped_ptr.h" | |
| 16 #include "base/path_service.h" | |
| 17 #include "base/strings/string16.h" | |
| 18 #include "base/strings/string_util.h" | |
| 19 #include "base/strings/utf_string_conversions.h" | |
| 20 #include "base/version.h" | |
| 21 #include "chrome/installer/test/alternate_version_generator.h" | |
| 22 #include "chrome/installer/util/installer_state.h" | |
| 23 #include "chrome/installer/util/util_constants.h" | |
| 24 #include "chrome/installer/util/work_item.h" | |
| 25 #include "chrome/installer/util/work_item_list.h" | |
| 26 #include "testing/gtest/include/gtest/gtest.h" | |
| 27 | |
| 28 namespace installer { | |
| 29 namespace { | |
|
grt (UTC plus 2)
2016/02/25 19:25:40
nit: blank line between these
fdoray
2016/02/26 17:00:05
Done.
| |
| 30 | |
| 31 class MockInstallerState : public InstallerState { | |
| 32 public: | |
| 33 explicit MockInstallerState(const base::FilePath& target_path) | |
| 34 : InstallerState(InstallerState::USER_LEVEL) { | |
| 35 target_path_ = target_path; | |
| 36 } | |
| 37 | |
| 38 private: | |
| 39 DISALLOW_COPY_AND_ASSIGN(MockInstallerState); | |
| 40 }; | |
| 41 | |
| 42 const base::char16 kVersionA[] = L"47.0.0.0"; | |
| 43 const base::char16 kVersionB[] = L"48.0.0.0"; | |
| 44 | |
| 45 class DeleteOldVersionsTest : public testing::Test { | |
| 46 protected: | |
| 47 DeleteOldVersionsTest() = default; | |
| 48 | |
| 49 bool DeleteOldVersions() { | |
| 50 MockInstallerState installer_state(install_dir()); | |
| 51 scoped_ptr<WorkItemList> work_item_list(WorkItem::CreateWorkItemList()); | |
| 52 AddDeleteOldVersionsWorkItem(installer_state, work_item_list.get()); | |
| 53 return work_item_list->Do(); | |
| 54 } | |
| 55 | |
| 56 bool CreateInstallDir() { return install_dir_.CreateUniqueTempDir(); } | |
| 57 | |
| 58 // Creates an executable with |name| and |version| in |install_dir_|. | |
| 59 bool CreateExecutable(const base::string16& name, | |
| 60 const base::string16& version) { | |
| 61 base::FilePath current_exe_path; | |
| 62 return base::PathService::Get(base::FILE_EXE, ¤t_exe_path) && | |
| 63 upgrade_test::GenerateSpecificPEFileVersion( | |
| 64 current_exe_path, install_dir().Append(name), | |
| 65 base::Version(base::UTF16ToUTF8(version))); | |
| 66 } | |
| 67 | |
| 68 // Creates a version directory named |name| in |install_dir_|. | |
| 69 bool CreateVersionDirectory(const base::string16& name) { | |
| 70 static const char kDummyContent[] = "dummy"; | |
| 71 const base::FilePath version_dir_path(install_dir().Append(name)); | |
| 72 | |
| 73 return base::CreateDirectory(install_dir().Append(name)) && | |
| 74 base::CreateDirectory(version_dir_path.Append(L"Installer")) && | |
| 75 base::WriteFile(version_dir_path.Append(L"chrome.dll"), | |
| 76 kDummyContent, sizeof(kDummyContent)) && | |
| 77 base::WriteFile(version_dir_path.Append(L"nacl64.exe"), | |
| 78 kDummyContent, sizeof(kDummyContent)) && | |
| 79 base::WriteFile(version_dir_path.Append(L"icudtl.dat"), | |
| 80 kDummyContent, sizeof(kDummyContent)) && | |
| 81 base::WriteFile(version_dir_path.Append(L"Installer\\setup.exe"), | |
| 82 kDummyContent, sizeof(kDummyContent)); | |
| 83 } | |
| 84 | |
| 85 // Returns the relative paths of all files and directories in |install_dir_|. | |
| 86 using FilePathSet = std::set<base::FilePath>; | |
| 87 FilePathSet GetInstallDirContent() const { | |
| 88 std::set<base::FilePath> content; | |
| 89 base::FileEnumerator file_enumerator( | |
| 90 install_dir(), true, | |
| 91 base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES); | |
| 92 for (base::FilePath path = file_enumerator.Next(); !path.empty(); | |
| 93 path = file_enumerator.Next()) { | |
| 94 DCHECK(base::StartsWith(path.value(), install_dir().value(), | |
| 95 base::CompareCase::SENSITIVE)); | |
| 96 content.insert(base::FilePath( | |
| 97 path.value().substr(install_dir().value().size() + 1))); | |
| 98 } | |
| 99 return content; | |
| 100 } | |
| 101 | |
| 102 // Adds to |file_path_set| all files and directories that are expected to be | |
| 103 // found in the version directory |version| before any attempt to delete it. | |
| 104 void AddVersionFiles(const base::string16& version, | |
| 105 FilePathSet* file_path_set) { | |
| 106 file_path_set->insert(base::FilePath(version)); | |
| 107 file_path_set->insert(base::FilePath(version).Append(L"chrome.dll")); | |
| 108 file_path_set->insert(base::FilePath(version).Append(L"nacl64.exe")); | |
| 109 file_path_set->insert(base::FilePath(version).Append(L"icudtl.dat")); | |
| 110 file_path_set->insert(base::FilePath(version).Append(L"Installer")); | |
| 111 file_path_set->insert( | |
| 112 base::FilePath(version).Append(L"Installer\\setup.exe")); | |
| 113 } | |
| 114 | |
| 115 base::FilePath install_dir() const { return install_dir_.path(); } | |
| 116 | |
| 117 private: | |
| 118 base::ScopedTempDir install_dir_; | |
| 119 | |
| 120 DISALLOW_COPY_AND_ASSIGN(DeleteOldVersionsTest); | |
| 121 }; | |
| 122 | |
| 123 } // namespace | |
| 124 | |
| 125 // DeleteOldVersions() should return true when there is nothing to delete. | |
| 126 TEST_F(DeleteOldVersionsTest, DeleteEmptyInstallDir) { | |
| 127 ASSERT_TRUE(CreateInstallDir()); | |
| 128 EXPECT_TRUE(DeleteOldVersions()); | |
| 129 } | |
| 130 | |
| 131 // An old executable without a matching directory should be deleted. | |
| 132 TEST_F(DeleteOldVersionsTest, DeleteOldExecutableWithoutMatchingDirectory) { | |
| 133 ASSERT_TRUE(CreateInstallDir()); | |
| 134 ASSERT_TRUE(CreateExecutable(installer::kChromeOldExe, kVersionA)); | |
| 135 | |
| 136 EXPECT_TRUE(DeleteOldVersions()); | |
| 137 EXPECT_TRUE(GetInstallDirContent().empty()); | |
| 138 } | |
| 139 | |
| 140 // DeleteOldVersions() should return false when it fails to delete an old | |
| 141 // executable that is in use. | |
| 142 TEST_F(DeleteOldVersionsTest, | |
| 143 DeleteInUseOldExecutableWithoutMatchingDirectory) { | |
| 144 ASSERT_TRUE(CreateInstallDir()); | |
| 145 ASSERT_TRUE(CreateExecutable(installer::kChromeOldExe, kVersionA)); | |
| 146 | |
| 147 base::File file_in_use(install_dir().Append(installer::kChromeOldExe), | |
| 148 base::File::FLAG_OPEN | base::File::FLAG_READ); | |
| 149 ASSERT_TRUE(file_in_use.IsValid()); | |
| 150 | |
| 151 EXPECT_FALSE(DeleteOldVersions()); | |
| 152 } | |
| 153 | |
| 154 // chrome.exe and new_chrome.exe should never be deleted. | |
| 155 TEST_F(DeleteOldVersionsTest, DeleteNewExecutablesWithoutMatchingDirectory) { | |
| 156 ASSERT_TRUE(CreateInstallDir()); | |
| 157 ASSERT_TRUE(CreateExecutable(installer::kChromeExe, kVersionA)); | |
| 158 ASSERT_TRUE(CreateExecutable(installer::kChromeNewExe, kVersionB)); | |
| 159 | |
| 160 EXPECT_TRUE(DeleteOldVersions()); | |
| 161 FilePathSet expected_install_dir_content; | |
| 162 expected_install_dir_content.insert(base::FilePath(installer::kChromeExe)); | |
| 163 expected_install_dir_content.insert(base::FilePath(installer::kChromeNewExe)); | |
| 164 EXPECT_EQ(expected_install_dir_content, GetInstallDirContent()); | |
| 165 } | |
| 166 | |
| 167 // A directory without a matching executable should be deleted. | |
| 168 TEST_F(DeleteOldVersionsTest, DeleteDirectoryWithoutMatchingExecutable) { | |
| 169 ASSERT_TRUE(CreateInstallDir()); | |
| 170 ASSERT_TRUE(CreateVersionDirectory(kVersionA)); | |
| 171 | |
| 172 EXPECT_TRUE(DeleteOldVersions()); | |
| 173 EXPECT_TRUE(GetInstallDirContent().empty()); | |
| 174 } | |
| 175 | |
| 176 // DeleteOldVersions() should return false when it fails to delete a file that | |
| 177 // is in use in a version directory. | |
| 178 TEST_F(DeleteOldVersionsTest, DeleteInUseDirectoryWithoutMatchingExecutable) { | |
| 179 ASSERT_TRUE(CreateInstallDir()); | |
| 180 ASSERT_TRUE(CreateVersionDirectory(kVersionA)); | |
| 181 | |
| 182 base::File file_in_use(install_dir().Append(kVersionA).Append(L"icudtl.dat"), | |
| 183 base::File::FLAG_OPEN | base::File::FLAG_READ); | |
| 184 ASSERT_TRUE(file_in_use.IsValid()); | |
| 185 | |
| 186 EXPECT_FALSE(DeleteOldVersions()); | |
| 187 } | |
| 188 | |
| 189 // A pair of matching old executable/version directory that is not in use should | |
| 190 // be deleted. | |
| 191 TEST_F(DeleteOldVersionsTest, DeleteOldExecutableWithMatchingDirectory) { | |
| 192 ASSERT_TRUE(CreateInstallDir()); | |
| 193 ASSERT_TRUE(CreateExecutable(installer::kChromeOldExe, kVersionA)); | |
| 194 ASSERT_TRUE(CreateVersionDirectory(kVersionA)); | |
| 195 | |
| 196 EXPECT_TRUE(DeleteOldVersions()); | |
| 197 EXPECT_TRUE(GetInstallDirContent().empty()); | |
| 198 } | |
| 199 | |
| 200 // chrome.exe, new_chrome.exe and their matching version directories should | |
| 201 // never be deleted. | |
| 202 TEST_F(DeleteOldVersionsTest, DeleteNewExecutablesWithMatchingDirectory) { | |
| 203 ASSERT_TRUE(CreateInstallDir()); | |
| 204 ASSERT_TRUE(CreateExecutable(installer::kChromeExe, kVersionA)); | |
| 205 ASSERT_TRUE(CreateVersionDirectory(kVersionA)); | |
| 206 ASSERT_TRUE(CreateExecutable(installer::kChromeNewExe, kVersionB)); | |
| 207 ASSERT_TRUE(CreateVersionDirectory(kVersionB)); | |
| 208 | |
| 209 EXPECT_TRUE(DeleteOldVersions()); | |
| 210 | |
| 211 FilePathSet expected_install_dir_content; | |
| 212 expected_install_dir_content.insert(base::FilePath(installer::kChromeExe)); | |
| 213 AddVersionFiles(kVersionA, &expected_install_dir_content); | |
| 214 expected_install_dir_content.insert(base::FilePath(installer::kChromeNewExe)); | |
| 215 AddVersionFiles(kVersionB, &expected_install_dir_content); | |
| 216 EXPECT_EQ(expected_install_dir_content, GetInstallDirContent()); | |
| 217 } | |
| 218 | |
| 219 // chrome.exe, new_chrome.exe and their matching version directories should | |
| 220 // never be deleted, even when files named old_chrome*.exe have the same | |
| 221 // versions as chrome.exe/new_chrome.exe. The old_chrome*.exe files, however, | |
| 222 // should be deleted. | |
| 223 TEST_F(DeleteOldVersionsTest, | |
| 224 DeleteNewExecutablesWithMatchingDirectoryAndOldExecutables) { | |
| 225 ASSERT_TRUE(CreateInstallDir()); | |
| 226 ASSERT_TRUE(CreateExecutable(installer::kChromeExe, kVersionA)); | |
| 227 ASSERT_TRUE(CreateVersionDirectory(kVersionA)); | |
| 228 ASSERT_TRUE(CreateExecutable(installer::kChromeNewExe, kVersionB)); | |
| 229 ASSERT_TRUE(CreateVersionDirectory(kVersionB)); | |
| 230 ASSERT_TRUE(CreateExecutable(L"old_chrome.exe", kVersionA)); | |
| 231 ASSERT_TRUE(CreateExecutable(L"old_chrome2.exe", kVersionB)); | |
| 232 | |
| 233 EXPECT_TRUE(DeleteOldVersions()); | |
| 234 | |
| 235 FilePathSet expected_install_dir_content; | |
| 236 expected_install_dir_content.insert(base::FilePath(installer::kChromeExe)); | |
| 237 AddVersionFiles(kVersionA, &expected_install_dir_content); | |
| 238 expected_install_dir_content.insert(base::FilePath(installer::kChromeNewExe)); | |
| 239 AddVersionFiles(kVersionB, &expected_install_dir_content); | |
| 240 EXPECT_EQ(expected_install_dir_content, GetInstallDirContent()); | |
| 241 } | |
| 242 | |
| 243 // No file should be deleted for a given version if the executable is in use. | |
| 244 TEST_F(DeleteOldVersionsTest, DeleteVersionWithExecutableInUse) { | |
| 245 ASSERT_TRUE(CreateInstallDir()); | |
| 246 ASSERT_TRUE(CreateExecutable(installer::kChromeOldExe, kVersionA)); | |
| 247 ASSERT_TRUE(CreateVersionDirectory(kVersionA)); | |
| 248 | |
| 249 base::File file_in_use(install_dir().Append(installer::kChromeOldExe), | |
| 250 base::File::FLAG_OPEN | base::File::FLAG_READ); | |
| 251 ASSERT_TRUE(file_in_use.IsValid()); | |
| 252 | |
| 253 EXPECT_FALSE(DeleteOldVersions()); | |
| 254 | |
| 255 FilePathSet expected_install_dir_content; | |
| 256 expected_install_dir_content.insert(base::FilePath(installer::kChromeOldExe)); | |
| 257 AddVersionFiles(kVersionA, &expected_install_dir_content); | |
| 258 EXPECT_EQ(expected_install_dir_content, GetInstallDirContent()); | |
| 259 } | |
| 260 | |
| 261 // No file should be deleted for a given version if a .dll file in the version | |
| 262 // directory is in use. | |
| 263 TEST_F(DeleteOldVersionsTest, DeleteVersionWithVersionDirectoryDllInUse) { | |
| 264 ASSERT_TRUE(CreateInstallDir()); | |
| 265 ASSERT_TRUE(CreateExecutable(installer::kChromeOldExe, kVersionA)); | |
| 266 ASSERT_TRUE(CreateVersionDirectory(kVersionA)); | |
| 267 | |
| 268 base::File file_in_use(install_dir().Append(kVersionA).Append(L"chrome.dll"), | |
| 269 base::File::FLAG_OPEN | base::File::FLAG_READ); | |
| 270 ASSERT_TRUE(file_in_use.IsValid()); | |
| 271 | |
| 272 EXPECT_FALSE(DeleteOldVersions()); | |
| 273 | |
| 274 FilePathSet expected_install_dir_content; | |
| 275 expected_install_dir_content.insert(base::FilePath(installer::kChromeOldExe)); | |
| 276 AddVersionFiles(kVersionA, &expected_install_dir_content); | |
| 277 EXPECT_EQ(expected_install_dir_content, GetInstallDirContent()); | |
| 278 } | |
| 279 | |
| 280 // No file should be deleted for a given version if a .exe file in the version | |
| 281 // directory is in use. | |
| 282 TEST_F(DeleteOldVersionsTest, DeleteVersionWithVersionDirectoryExeInUse) { | |
| 283 ASSERT_TRUE(CreateInstallDir()); | |
| 284 ASSERT_TRUE(CreateExecutable(installer::kChromeOldExe, kVersionA)); | |
| 285 ASSERT_TRUE(CreateVersionDirectory(kVersionA)); | |
| 286 | |
| 287 base::File file_in_use( | |
| 288 install_dir().Append(kVersionA).Append(L"Installer\\setup.exe"), | |
| 289 base::File::FLAG_OPEN | base::File::FLAG_READ); | |
| 290 ASSERT_TRUE(file_in_use.IsValid()); | |
| 291 | |
| 292 EXPECT_FALSE(DeleteOldVersions()); | |
| 293 | |
| 294 FilePathSet expected_install_dir_content; | |
| 295 expected_install_dir_content.insert(base::FilePath(installer::kChromeOldExe)); | |
| 296 AddVersionFiles(kVersionA, &expected_install_dir_content); | |
| 297 EXPECT_EQ(expected_install_dir_content, GetInstallDirContent()); | |
| 298 } | |
| 299 | |
| 300 // DeleteOldVersions() should return false when it tries to delete all the files | |
| 301 // of a version but one of them can't be deleted because it is in use. | |
| 302 TEST_F(DeleteOldVersionsTest, DeleteVersionWithUnimportantFileInUse) { | |
| 303 ASSERT_TRUE(CreateInstallDir()); | |
| 304 ASSERT_TRUE(CreateExecutable(installer::kChromeOldExe, kVersionA)); | |
| 305 ASSERT_TRUE(CreateVersionDirectory(kVersionA)); | |
| 306 | |
| 307 base::File file_in_use(install_dir().Append(kVersionA).Append(L"icudtl.dat"), | |
| 308 base::File::FLAG_OPEN | base::File::FLAG_READ); | |
| 309 ASSERT_TRUE(file_in_use.IsValid()); | |
| 310 | |
| 311 EXPECT_FALSE(DeleteOldVersions()); | |
| 312 } | |
| 313 | |
| 314 // If an installation directory contains a file named chrome.exe with a matching | |
| 315 // directory v1 and a file name old_chrome.exe with a matching directory v2, | |
| 316 // old_chrome.exe and v2 should be deleted but chrome.exe and v1 shouldn't. | |
| 317 TEST_F(DeleteOldVersionsTest, TypicalAfterRenameState) { | |
| 318 ASSERT_TRUE(CreateInstallDir()); | |
| 319 ASSERT_TRUE(CreateExecutable(installer::kChromeOldExe, kVersionA)); | |
| 320 ASSERT_TRUE(CreateVersionDirectory(kVersionA)); | |
| 321 ASSERT_TRUE(CreateExecutable(installer::kChromeExe, kVersionB)); | |
| 322 ASSERT_TRUE(CreateVersionDirectory(kVersionB)); | |
| 323 | |
| 324 EXPECT_TRUE(DeleteOldVersions()); | |
| 325 | |
| 326 FilePathSet expected_install_dir_content; | |
| 327 expected_install_dir_content.insert(base::FilePath(installer::kChromeExe)); | |
| 328 AddVersionFiles(kVersionB, &expected_install_dir_content); | |
| 329 EXPECT_EQ(expected_install_dir_content, GetInstallDirContent()); | |
| 330 } | |
| 331 | |
| 332 } // namespace installer | |
| OLD | NEW |