forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdelete_old_versions_unittest.cc
284 lines (236 loc) · 11.4 KB
/
delete_old_versions_unittest.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
// Copyright 2016 The Chromium Authors
// 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 <string>
#include "base/check.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/files/scoped_temp_dir.h"
#include "base/path_service.h"
#include "base/strings/string_util.h"
#include "base/version.h"
#include "chrome/installer/test/alternate_version_generator.h"
#include "chrome/installer/util/util_constants.h"
#include "testing/gtest/include/gtest/gtest.h"
using upgrade_test::Direction;
namespace installer {
namespace {
class DeleteOldVersionsTest : public testing::Test {
public:
DeleteOldVersionsTest(const DeleteOldVersionsTest&) = delete;
DeleteOldVersionsTest& operator=(const DeleteOldVersionsTest&) = delete;
protected:
DeleteOldVersionsTest() = default;
void SetUp() override { ASSERT_TRUE(install_dir_.CreateUniqueTempDir()); }
// Creates a copy of the current executable with a distinct version of name
// |name| in |install_dir_|. Depending on |direction|, the version of the
// created executable is higher or lower than the version of the current
// executable.
std::wstring CreateExecutable(const std::wstring& name, Direction direction) {
base::FilePath current_exe_path;
if (!base::PathService::Get(base::FILE_EXE, ¤t_exe_path))
return std::wstring();
return upgrade_test::GenerateAlternatePEFileVersion(
current_exe_path, install_dir().Append(name), direction);
}
// Creates in |install_dir_| a directory named |name| containing a subset of
// dummy files impersonating a Chrome version directory.
bool CreateVersionDirectory(const std::wstring& name) {
static constexpr 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) &&
base::WriteFile(version_dir_path.Append(L"icudtl.dat"),
kDummyContent) &&
base::WriteFile(version_dir_path.Append(L"Installer\\setup.exe"),
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 std::wstring& 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"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_.GetPath(); }
private:
base::ScopedTempDir install_dir_;
};
} // namespace
// An old executable without a matching directory should be deleted.
TEST_F(DeleteOldVersionsTest, DeleteOldExecutableWithoutMatchingDirectory) {
ASSERT_FALSE(
CreateExecutable(installer::kChromeOldExe, Direction::PREVIOUS_VERSION)
.empty());
DeleteOldVersions(install_dir());
EXPECT_TRUE(GetInstallDirContent().empty());
}
// chrome.exe and new_chrome.exe should never be deleted.
TEST_F(DeleteOldVersionsTest, DeleteNewExecutablesWithoutMatchingDirectory) {
ASSERT_FALSE(
CreateExecutable(installer::kChromeExe, Direction::PREVIOUS_VERSION)
.empty());
ASSERT_FALSE(
CreateExecutable(installer::kChromeNewExe, Direction::NEXT_VERSION)
.empty());
DeleteOldVersions(install_dir());
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) {
static constexpr wchar_t kVersion[] = L"48.0.0.0";
ASSERT_TRUE(CreateVersionDirectory(kVersion));
DeleteOldVersions(install_dir());
EXPECT_TRUE(GetInstallDirContent().empty());
}
// A pair of matching old executable/version directory that is not in use should
// be deleted.
TEST_F(DeleteOldVersionsTest, DeleteOldExecutableWithMatchingDirectory) {
const std::wstring version_a =
CreateExecutable(installer::kChromeOldExe, Direction::PREVIOUS_VERSION);
ASSERT_FALSE(version_a.empty());
ASSERT_TRUE(CreateVersionDirectory(version_a));
DeleteOldVersions(install_dir());
EXPECT_TRUE(GetInstallDirContent().empty());
}
// chrome.exe, new_chrome.exe and their matching version directories should
// never be deleted.
TEST_F(DeleteOldVersionsTest, DeleteNewExecutablesWithMatchingDirectory) {
const std::wstring version_a =
CreateExecutable(installer::kChromeExe, Direction::PREVIOUS_VERSION);
ASSERT_FALSE(version_a.empty());
ASSERT_TRUE(CreateVersionDirectory(version_a));
const std::wstring version_b =
CreateExecutable(installer::kChromeNewExe, Direction::NEXT_VERSION);
ASSERT_FALSE(version_b.empty());
ASSERT_TRUE(CreateVersionDirectory(version_b));
DeleteOldVersions(install_dir());
FilePathSet expected_install_dir_content;
expected_install_dir_content.insert(base::FilePath(installer::kChromeExe));
AddVersionFiles(version_a, &expected_install_dir_content);
expected_install_dir_content.insert(base::FilePath(installer::kChromeNewExe));
AddVersionFiles(version_b, &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) {
const std::wstring version_a =
CreateExecutable(installer::kChromeExe, Direction::PREVIOUS_VERSION);
ASSERT_FALSE(version_a.empty());
ASSERT_TRUE(CreateVersionDirectory(version_a));
const std::wstring version_b =
CreateExecutable(installer::kChromeNewExe, Direction::NEXT_VERSION);
ASSERT_FALSE(version_b.empty());
ASSERT_TRUE(CreateVersionDirectory(version_b));
EXPECT_EQ(version_a,
CreateExecutable(L"old_chrome.exe", Direction::PREVIOUS_VERSION));
EXPECT_EQ(version_b,
CreateExecutable(L"old_chrome2.exe", Direction::NEXT_VERSION));
DeleteOldVersions(install_dir());
FilePathSet expected_install_dir_content;
expected_install_dir_content.insert(base::FilePath(installer::kChromeExe));
AddVersionFiles(version_a, &expected_install_dir_content);
expected_install_dir_content.insert(base::FilePath(installer::kChromeNewExe));
AddVersionFiles(version_b, &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) {
const std::wstring version_a =
CreateExecutable(installer::kChromeOldExe, Direction::PREVIOUS_VERSION);
ASSERT_FALSE(version_a.empty());
ASSERT_TRUE(CreateVersionDirectory(version_a));
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(install_dir());
FilePathSet expected_install_dir_content;
expected_install_dir_content.insert(base::FilePath(installer::kChromeOldExe));
AddVersionFiles(version_a, &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) {
const std::wstring version_a =
CreateExecutable(installer::kChromeOldExe, Direction::PREVIOUS_VERSION);
ASSERT_FALSE(version_a.empty());
ASSERT_TRUE(CreateVersionDirectory(version_a));
base::File file_in_use(install_dir().Append(version_a).Append(L"chrome.dll"),
base::File::FLAG_OPEN | base::File::FLAG_READ);
ASSERT_TRUE(file_in_use.IsValid());
DeleteOldVersions(install_dir());
FilePathSet expected_install_dir_content;
expected_install_dir_content.insert(base::FilePath(installer::kChromeOldExe));
AddVersionFiles(version_a, &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) {
const std::wstring version_a =
CreateExecutable(installer::kChromeOldExe, Direction::PREVIOUS_VERSION);
ASSERT_FALSE(version_a.empty());
ASSERT_TRUE(CreateVersionDirectory(version_a));
base::File file_in_use(
install_dir().Append(version_a).Append(L"Installer\\setup.exe"),
base::File::FLAG_OPEN | base::File::FLAG_READ);
ASSERT_TRUE(file_in_use.IsValid());
DeleteOldVersions(install_dir());
FilePathSet expected_install_dir_content;
expected_install_dir_content.insert(base::FilePath(installer::kChromeOldExe));
AddVersionFiles(version_a, &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 named 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) {
const std::wstring version_a =
CreateExecutable(installer::kChromeOldExe, Direction::PREVIOUS_VERSION);
ASSERT_FALSE(version_a.empty());
ASSERT_TRUE(CreateVersionDirectory(version_a));
const std::wstring version_b =
CreateExecutable(installer::kChromeExe, Direction::NEXT_VERSION);
ASSERT_FALSE(version_b.empty());
ASSERT_TRUE(CreateVersionDirectory(version_b));
DeleteOldVersions(install_dir());
FilePathSet expected_install_dir_content;
expected_install_dir_content.insert(base::FilePath(installer::kChromeExe));
AddVersionFiles(version_b, &expected_install_dir_content);
EXPECT_EQ(expected_install_dir_content, GetInstallDirContent());
}
} // namespace installer