1 // Copyright 2020 The Chromium Authors
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 "base/files/important_file_writer_cleaner.h"
6
7 #include "base/check.h"
8 #include "base/files/file.h"
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h"
11 #include "base/files/scoped_temp_dir.h"
12 #include "base/task/thread_pool.h"
13 #include "base/test/bind.h"
14 #include "base/test/task_environment.h"
15 #include "base/test/test_waitable_event.h"
16 #include "base/time/time.h"
17 #include "testing/gmock/include/gmock/gmock.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "third_party/abseil-cpp/absl/types/optional.h"
20
21 using ::testing::ElementsAre;
22
23 namespace base {
24
25 class ImportantFileWriterCleanerTest : public ::testing::Test {
26 public:
ImportantFileWriterCleanerTest()27 ImportantFileWriterCleanerTest()
28 : old_file_time_(ImportantFileWriterCleaner::GetInstance()
29 .GetUpperBoundTimeForTest() -
30 Milliseconds(1)) {}
31
32 protected:
33 // Initializes and Starts the global cleaner at construction and Stops it
34 // at destruction. ("Lifetime" refers to its activity rather than existence.)
35 class ScopedCleanerLifetime {
36 public:
ScopedCleanerLifetime()37 ScopedCleanerLifetime() {
38 auto& instance = ImportantFileWriterCleaner::GetInstance();
39 instance.Initialize();
40 instance.Start();
41 }
42 ScopedCleanerLifetime(const ScopedCleanerLifetime&) = delete;
43 ScopedCleanerLifetime& operator=(const ScopedCleanerLifetime&) = delete;
~ScopedCleanerLifetime()44 ~ScopedCleanerLifetime() {
45 ImportantFileWriterCleaner::GetInstance().Stop();
46 }
47 };
48
49 void SetUp() override;
50 void TearDown() override;
51
dir_1() const52 const FilePath& dir_1() const { return dir_1_; }
dir_1_file_new() const53 const FilePath& dir_1_file_new() const { return dir_1_file_new_; }
dir_1_file_old() const54 const FilePath& dir_1_file_old() const { return dir_1_file_old_; }
dir_1_file_other() const55 const FilePath& dir_1_file_other() const { return dir_1_file_other_; }
dir_2() const56 const FilePath& dir_2() const { return dir_2_; }
dir_2_file_new() const57 const FilePath& dir_2_file_new() const { return dir_2_file_new_; }
dir_2_file_old() const58 const FilePath& dir_2_file_old() const { return dir_2_file_old_; }
dir_2_file_other() const59 const FilePath& dir_2_file_other() const { return dir_2_file_other_; }
60
StartCleaner()61 void StartCleaner() {
62 DCHECK(!cleaner_lifetime_.has_value());
63 cleaner_lifetime_.emplace();
64 }
65
StopCleaner()66 void StopCleaner() {
67 DCHECK(cleaner_lifetime_.has_value());
68 cleaner_lifetime_.reset();
69 }
70
CreateNewFileInDir(const FilePath & dir,FilePath & path)71 void CreateNewFileInDir(const FilePath& dir, FilePath& path) {
72 File file = CreateAndOpenTemporaryFileInDir(dir, &path);
73 ASSERT_TRUE(file.IsValid());
74 }
75
CreateOldFileInDir(const FilePath & dir,FilePath & path)76 void CreateOldFileInDir(const FilePath& dir, FilePath& path) {
77 File file = CreateAndOpenTemporaryFileInDir(dir, &path);
78 ASSERT_TRUE(file.IsValid());
79 ASSERT_TRUE(file.SetTimes(Time::Now(), old_file_time_));
80 }
81
CreateOldFile(const FilePath & path)82 void CreateOldFile(const FilePath& path) {
83 File file(path, File::FLAG_CREATE | File::FLAG_WRITE);
84 ASSERT_TRUE(file.IsValid());
85 ASSERT_TRUE(file.SetTimes(Time::Now(), old_file_time_));
86 }
87
88 ScopedTempDir temp_dir_;
89 test::TaskEnvironment task_environment_;
90
91 private:
92 const Time old_file_time_;
93 FilePath dir_1_;
94 FilePath dir_2_;
95 FilePath dir_1_file_new_;
96 FilePath dir_1_file_old_;
97 FilePath dir_1_file_other_;
98 FilePath dir_2_file_new_;
99 FilePath dir_2_file_old_;
100 FilePath dir_2_file_other_;
101 absl::optional<ScopedCleanerLifetime> cleaner_lifetime_;
102 };
103
SetUp()104 void ImportantFileWriterCleanerTest::SetUp() {
105 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
106
107 // Create two directories that will hold files to be cleaned.
108 dir_1_ = temp_dir_.GetPath().Append(FILE_PATH_LITERAL("dir_1"));
109 ASSERT_TRUE(CreateDirectory(dir_1_));
110 dir_2_ = temp_dir_.GetPath().Append(FILE_PATH_LITERAL("dir_2"));
111 ASSERT_TRUE(CreateDirectory(dir_2_));
112
113 // Create some old and new files in each dir.
114 ASSERT_NO_FATAL_FAILURE(CreateNewFileInDir(dir_1_, dir_1_file_new_));
115
116 ASSERT_NO_FATAL_FAILURE(CreateOldFileInDir(dir_1_, dir_1_file_old_));
117
118 dir_1_file_other_ = dir_1_.Append(FILE_PATH_LITERAL("other.nottmp"));
119 ASSERT_NO_FATAL_FAILURE(CreateOldFile(dir_1_file_other_));
120
121 ASSERT_NO_FATAL_FAILURE(CreateNewFileInDir(dir_2_, dir_2_file_new_));
122
123 ASSERT_NO_FATAL_FAILURE(CreateOldFileInDir(dir_2_, dir_2_file_old_));
124
125 dir_2_file_other_ = dir_2_.Append(FILE_PATH_LITERAL("other.nottmp"));
126 ASSERT_NO_FATAL_FAILURE(CreateOldFile(dir_2_file_other_));
127 }
128
TearDown()129 void ImportantFileWriterCleanerTest::TearDown() {
130 cleaner_lifetime_.reset();
131 task_environment_.RunUntilIdle();
132 ImportantFileWriterCleaner::GetInstance().UninitializeForTesting();
133 EXPECT_TRUE(temp_dir_.Delete());
134 }
135
136 // Tests that adding a directory without initializing the cleaner does nothing.
TEST_F(ImportantFileWriterCleanerTest,NotInitializedNoOpAdd)137 TEST_F(ImportantFileWriterCleanerTest, NotInitializedNoOpAdd) {
138 ImportantFileWriterCleaner::AddDirectory(dir_1());
139 task_environment_.RunUntilIdle();
140 EXPECT_TRUE(PathExists(dir_1_file_new()));
141 EXPECT_TRUE(PathExists(dir_1_file_old()));
142 EXPECT_TRUE(PathExists(dir_1_file_other()));
143 EXPECT_TRUE(PathExists(dir_2_file_new()));
144 EXPECT_TRUE(PathExists(dir_2_file_old()));
145 EXPECT_TRUE(PathExists(dir_2_file_other()));
146 }
147
148 // Tests that adding a directory without starting the cleaner does nothing.
TEST_F(ImportantFileWriterCleanerTest,NotStartedNoOpAdd)149 TEST_F(ImportantFileWriterCleanerTest, NotStartedNoOpAdd) {
150 ImportantFileWriterCleaner::GetInstance().Initialize();
151 ImportantFileWriterCleaner::AddDirectory(dir_1());
152 task_environment_.RunUntilIdle();
153 EXPECT_TRUE(PathExists(dir_1_file_new()));
154 EXPECT_TRUE(PathExists(dir_1_file_old()));
155 EXPECT_TRUE(PathExists(dir_1_file_other()));
156 EXPECT_TRUE(PathExists(dir_2_file_new()));
157 EXPECT_TRUE(PathExists(dir_2_file_old()));
158 EXPECT_TRUE(PathExists(dir_2_file_other()));
159 }
160
161 // Tests that starting and stopping does no harm.
TEST_F(ImportantFileWriterCleanerTest,StartStop)162 TEST_F(ImportantFileWriterCleanerTest, StartStop) {
163 StartCleaner();
164 StopCleaner();
165 }
166
167 // Tests that adding a directory then starting the cleaner works.
TEST_F(ImportantFileWriterCleanerTest,AddStart)168 TEST_F(ImportantFileWriterCleanerTest, AddStart) {
169 ImportantFileWriterCleaner::GetInstance().Initialize();
170 ImportantFileWriterCleaner::AddDirectory(dir_1());
171 StartCleaner();
172 task_environment_.RunUntilIdle();
173
174 // The old file should have been cleaned from the added dir.
175 EXPECT_TRUE(PathExists(dir_1_file_new()));
176 EXPECT_FALSE(PathExists(dir_1_file_old()));
177 EXPECT_TRUE(PathExists(dir_1_file_other()));
178 EXPECT_TRUE(PathExists(dir_2_file_new()));
179 EXPECT_TRUE(PathExists(dir_2_file_old()));
180 EXPECT_TRUE(PathExists(dir_2_file_other()));
181 }
182
183 // Tests that adding multiple directories before starting cleans both.
TEST_F(ImportantFileWriterCleanerTest,AddAddStart)184 TEST_F(ImportantFileWriterCleanerTest, AddAddStart) {
185 ImportantFileWriterCleaner::GetInstance().Initialize();
186 ImportantFileWriterCleaner::AddDirectory(dir_1());
187 ImportantFileWriterCleaner::AddDirectory(dir_2());
188 StartCleaner();
189 task_environment_.RunUntilIdle();
190
191 // The old file should have been cleaned from both added dirs.
192 EXPECT_TRUE(PathExists(dir_1_file_new()));
193 EXPECT_FALSE(PathExists(dir_1_file_old()));
194 EXPECT_TRUE(PathExists(dir_1_file_other()));
195 EXPECT_TRUE(PathExists(dir_2_file_new()));
196 EXPECT_FALSE(PathExists(dir_2_file_old()));
197 EXPECT_TRUE(PathExists(dir_2_file_other()));
198 }
199
200 // Tests that starting the cleaner then adding a directory works.
TEST_F(ImportantFileWriterCleanerTest,StartAdd)201 TEST_F(ImportantFileWriterCleanerTest, StartAdd) {
202 StartCleaner();
203 ImportantFileWriterCleaner::AddDirectory(dir_1());
204 task_environment_.RunUntilIdle();
205
206 // The old file should have been cleaned from the added dir.
207 EXPECT_TRUE(PathExists(dir_1_file_new()));
208 EXPECT_FALSE(PathExists(dir_1_file_old()));
209 EXPECT_TRUE(PathExists(dir_1_file_other()));
210 EXPECT_TRUE(PathExists(dir_2_file_new()));
211 EXPECT_TRUE(PathExists(dir_2_file_old()));
212 EXPECT_TRUE(PathExists(dir_2_file_other()));
213 }
214
215 // Tests that starting the cleaner twice doesn't cause it to clean twice.
TEST_F(ImportantFileWriterCleanerTest,StartTwice)216 TEST_F(ImportantFileWriterCleanerTest, StartTwice) {
217 StartCleaner();
218 ImportantFileWriterCleaner::AddDirectory(dir_1());
219 task_environment_.RunUntilIdle();
220
221 // Recreate the old file that was just cleaned.
222 ASSERT_NO_FATAL_FAILURE(CreateOldFile(dir_1_file_old()));
223
224 // Start again and make sure it wasn't cleaned again.
225 ImportantFileWriterCleaner::GetInstance().Start();
226 task_environment_.RunUntilIdle();
227
228 EXPECT_TRUE(PathExists(dir_1_file_old()));
229 }
230
231 // Tests that adding a dir twice doesn't cause it to clean twice.
TEST_F(ImportantFileWriterCleanerTest,AddTwice)232 TEST_F(ImportantFileWriterCleanerTest, AddTwice) {
233 StartCleaner();
234 ImportantFileWriterCleaner::AddDirectory(dir_1());
235 task_environment_.RunUntilIdle();
236
237 // Recreate the old file that was just cleaned.
238 ASSERT_NO_FATAL_FAILURE(CreateOldFile(dir_1_file_old()));
239
240 // Add the directory again and make sure nothing else is cleaned.
241 ImportantFileWriterCleaner::AddDirectory(dir_1());
242 task_environment_.RunUntilIdle();
243
244 EXPECT_TRUE(PathExists(dir_1_file_old()));
245 }
246
247 // Tests that AddDirectory called from another thread properly bounces back to
248 // the main thread for processing.
TEST_F(ImportantFileWriterCleanerTest,StartAddFromOtherThread)249 TEST_F(ImportantFileWriterCleanerTest, StartAddFromOtherThread) {
250 StartCleaner();
251
252 // Add from the ThreadPool and wait for it to finish.
253 TestWaitableEvent waitable_event;
254 ThreadPool::PostTask(FROM_HERE, BindLambdaForTesting([&]() {
255 ImportantFileWriterCleaner::AddDirectory(dir_1());
256 waitable_event.Signal();
257 }));
258 waitable_event.Wait();
259
260 // Allow the cleaner to run.
261 task_environment_.RunUntilIdle();
262
263 // The old file should have been cleaned from the added dir.
264 EXPECT_TRUE(PathExists(dir_1_file_new()));
265 EXPECT_FALSE(PathExists(dir_1_file_old()));
266 EXPECT_TRUE(PathExists(dir_1_file_other()));
267 EXPECT_TRUE(PathExists(dir_2_file_new()));
268 EXPECT_TRUE(PathExists(dir_2_file_old()));
269 EXPECT_TRUE(PathExists(dir_2_file_other()));
270 }
271
272 // Tests that adding a directory while a session is processing a previous
273 // directory works.
TEST_F(ImportantFileWriterCleanerTest,AddStartAdd)274 TEST_F(ImportantFileWriterCleanerTest, AddStartAdd) {
275 ImportantFileWriterCleaner::GetInstance().Initialize();
276 ImportantFileWriterCleaner::AddDirectory(dir_1());
277 StartCleaner();
278 ImportantFileWriterCleaner::AddDirectory(dir_2());
279 task_environment_.RunUntilIdle();
280
281 // The old file should have been cleaned from both added dirs.
282 EXPECT_TRUE(PathExists(dir_1_file_new()));
283 EXPECT_FALSE(PathExists(dir_1_file_old()));
284 EXPECT_TRUE(PathExists(dir_1_file_other()));
285 EXPECT_TRUE(PathExists(dir_2_file_new()));
286 EXPECT_FALSE(PathExists(dir_2_file_old()));
287 EXPECT_TRUE(PathExists(dir_2_file_other()));
288 }
289
290 // Tests stopping while the background task is running.
TEST_F(ImportantFileWriterCleanerTest,StopWhileRunning)291 TEST_F(ImportantFileWriterCleanerTest, StopWhileRunning) {
292 ImportantFileWriterCleaner::GetInstance().Initialize();
293
294 // Create a great many old files in dir1.
295 for (int i = 0; i < 100; ++i) {
296 FilePath path;
297 CreateOldFileInDir(dir_1(), path);
298 }
299
300 ImportantFileWriterCleaner::AddDirectory(dir_1());
301 StartCleaner();
302
303 // It's possible that the background task will quickly delete all 100 files.
304 // In all likelihood, though, the stop flag will be read and processed before
305 // then. Either case is a success.
306 StopCleaner();
307 task_environment_.RunUntilIdle();
308 }
309
310 } // namespace base
311