• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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