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 |