blob: d8121f44759c74b52fc86f937c1fe789d5d76016 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/files/important_file_writer_cleaner.h"
#include "base/check.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/optional.h"
#include "base/process/process.h"
#include "base/strings/stringprintf.h"
#include "base/task/thread_pool.h"
#include "base/test/bind_test_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "base/test/test_waitable_event.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/time/time.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::ElementsAre;
namespace base {
class ImportantFileWriterCleanerTest : public ::testing::Test {
protected:
// Initializes and Starts the global cleaner at construction and Stops it
// at destruction. ("Lifetime" refers to its activity rather than existence.)
class ScopedCleanerLifetime {
public:
ScopedCleanerLifetime() {
auto& instance = ImportantFileWriterCleaner::GetInstance();
instance.Initialize();
instance.Start();
}
ScopedCleanerLifetime(const ScopedCleanerLifetime&) = delete;
ScopedCleanerLifetime& operator=(const ScopedCleanerLifetime&) = delete;
~ScopedCleanerLifetime() {
ImportantFileWriterCleaner::GetInstance().Stop();
}
};
void SetUp() override;
void TearDown() override;
const FilePath& dir_1() const { return dir_1_; }
const FilePath& dir_1_file_new() const { return dir_1_file_new_; }
const FilePath& dir_1_file_old() const { return dir_1_file_old_; }
const FilePath& dir_1_file_other() const { return dir_1_file_other_; }
const FilePath& dir_2() const { return dir_2_; }
const FilePath& dir_2_file_new() const { return dir_2_file_new_; }
const FilePath& dir_2_file_old() const { return dir_2_file_old_; }
const FilePath& dir_2_file_other() const { return dir_2_file_other_; }
void StartCleaner() {
DCHECK(!cleaner_lifetime_.has_value());
cleaner_lifetime_.emplace();
}
void StopCleaner() {
DCHECK(cleaner_lifetime_.has_value());
cleaner_lifetime_.reset();
}
void CreateNewFile(const FilePath& path) {
File file(path, File::FLAG_CREATE | File::FLAG_WRITE);
ASSERT_TRUE(file.IsValid());
}
void CreateOldFile(const FilePath& path) {
const Time old_time =
Process::Current().CreationTime() - TimeDelta::FromSeconds(1);
File file(path, File::FLAG_CREATE | File::FLAG_WRITE);
ASSERT_TRUE(file.IsValid());
ASSERT_TRUE(file.SetTimes(Time::Now(), old_time));
}
ScopedTempDir temp_dir_;
test::TaskEnvironment task_environment_;
HistogramTester histogram_tester_;
private:
FilePath dir_1_;
FilePath dir_2_;
FilePath dir_1_file_new_;
FilePath dir_1_file_old_;
FilePath dir_1_file_other_;
FilePath dir_2_file_new_;
FilePath dir_2_file_old_;
FilePath dir_2_file_other_;
Optional<ScopedCleanerLifetime> cleaner_lifetime_;
};
void ImportantFileWriterCleanerTest::SetUp() {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
// Create two directories that will hold files to be cleaned.
dir_1_ = temp_dir_.GetPath().Append(FILE_PATH_LITERAL("dir_1"));
ASSERT_TRUE(CreateDirectory(dir_1_));
dir_2_ = temp_dir_.GetPath().Append(FILE_PATH_LITERAL("dir_2"));
ASSERT_TRUE(CreateDirectory(dir_2_));
// Create some old and new files in each dir.
dir_1_file_new_ = dir_1_.Append(FILE_PATH_LITERAL("new.tmp"));
ASSERT_NO_FATAL_FAILURE(CreateNewFile(dir_1_file_new_));
dir_1_file_old_ = dir_1_.Append(FILE_PATH_LITERAL("old.tmp"));
ASSERT_NO_FATAL_FAILURE(CreateOldFile(dir_1_file_old_));
dir_1_file_other_ = dir_1_.Append(FILE_PATH_LITERAL("other.nottmp"));
ASSERT_NO_FATAL_FAILURE(CreateOldFile(dir_1_file_other_));
dir_2_file_new_ = dir_2_.Append(FILE_PATH_LITERAL("new.tmp"));
ASSERT_NO_FATAL_FAILURE(CreateNewFile(dir_2_file_new_));
dir_2_file_old_ = dir_2_.Append(FILE_PATH_LITERAL("old.tmp"));
ASSERT_NO_FATAL_FAILURE(CreateOldFile(dir_2_file_old_));
dir_2_file_other_ = dir_2_.Append(FILE_PATH_LITERAL("other.nottmp"));
ASSERT_NO_FATAL_FAILURE(CreateOldFile(dir_2_file_other_));
}
void ImportantFileWriterCleanerTest::TearDown() {
cleaner_lifetime_.reset();
task_environment_.RunUntilIdle();
ImportantFileWriterCleaner::GetInstance().UninitializeForTesting();
EXPECT_TRUE(temp_dir_.Delete());
}
// Tests that adding a directory without initializing the cleaner does nothing.
TEST_F(ImportantFileWriterCleanerTest, NotInitializedNoOpAdd) {
ImportantFileWriterCleaner::AddDirectory(dir_1());
task_environment_.RunUntilIdle();
EXPECT_TRUE(PathExists(dir_1_file_new()));
EXPECT_TRUE(PathExists(dir_1_file_old()));
EXPECT_TRUE(PathExists(dir_1_file_other()));
EXPECT_TRUE(PathExists(dir_2_file_new()));
EXPECT_TRUE(PathExists(dir_2_file_old()));
EXPECT_TRUE(PathExists(dir_2_file_other()));
}
// Tests that adding a directory without starting the cleaner does nothing.
TEST_F(ImportantFileWriterCleanerTest, NotStartedNoOpAdd) {
ImportantFileWriterCleaner::GetInstance().Initialize();
ImportantFileWriterCleaner::AddDirectory(dir_1());
task_environment_.RunUntilIdle();
EXPECT_TRUE(PathExists(dir_1_file_new()));
EXPECT_TRUE(PathExists(dir_1_file_old()));
EXPECT_TRUE(PathExists(dir_1_file_other()));
EXPECT_TRUE(PathExists(dir_2_file_new()));
EXPECT_TRUE(PathExists(dir_2_file_old()));
EXPECT_TRUE(PathExists(dir_2_file_other()));
}
// Tests that starting and stopping does no harm.
TEST_F(ImportantFileWriterCleanerTest, StartStop) {
StartCleaner();
StopCleaner();
}
// Tests that adding a directory then starting the cleaner works.
TEST_F(ImportantFileWriterCleanerTest, AddStart) {
ImportantFileWriterCleaner::GetInstance().Initialize();
ImportantFileWriterCleaner::AddDirectory(dir_1());
StartCleaner();
task_environment_.RunUntilIdle();
// The old file should have been cleaned from the added dir.
EXPECT_TRUE(PathExists(dir_1_file_new()));
EXPECT_FALSE(PathExists(dir_1_file_old()));
EXPECT_TRUE(PathExists(dir_1_file_other()));
EXPECT_TRUE(PathExists(dir_2_file_new()));
EXPECT_TRUE(PathExists(dir_2_file_old()));
EXPECT_TRUE(PathExists(dir_2_file_other()));
// There should be 1 success and 0 failure logged for the one dir.
histogram_tester_.ExpectUniqueSample("Windows.TmpFileDeleter.SuccessCount", 1,
1);
histogram_tester_.ExpectUniqueSample("Windows.TmpFileDeleter.FailCount", 0,
1);
}
// Tests that adding multiple directories before starting cleans both.
TEST_F(ImportantFileWriterCleanerTest, AddAddStart) {
ImportantFileWriterCleaner::GetInstance().Initialize();
ImportantFileWriterCleaner::AddDirectory(dir_1());
ImportantFileWriterCleaner::AddDirectory(dir_2());
StartCleaner();
task_environment_.RunUntilIdle();
// The old file should have been cleaned from both added dirs.
EXPECT_TRUE(PathExists(dir_1_file_new()));
EXPECT_FALSE(PathExists(dir_1_file_old()));
EXPECT_TRUE(PathExists(dir_1_file_other()));
EXPECT_TRUE(PathExists(dir_2_file_new()));
EXPECT_FALSE(PathExists(dir_2_file_old()));
EXPECT_TRUE(PathExists(dir_2_file_other()));
// There should be 2 success and 2 failure samples (one for each dir).
histogram_tester_.ExpectUniqueSample("Windows.TmpFileDeleter.SuccessCount", 1,
2);
histogram_tester_.ExpectUniqueSample("Windows.TmpFileDeleter.FailCount", 0,
2);
}
// Tests that starting the cleaner then adding a directory works.
TEST_F(ImportantFileWriterCleanerTest, StartAdd) {
StartCleaner();
ImportantFileWriterCleaner::AddDirectory(dir_1());
task_environment_.RunUntilIdle();
// The old file should have been cleaned from the added dir.
EXPECT_TRUE(PathExists(dir_1_file_new()));
EXPECT_FALSE(PathExists(dir_1_file_old()));
EXPECT_TRUE(PathExists(dir_1_file_other()));
EXPECT_TRUE(PathExists(dir_2_file_new()));
EXPECT_TRUE(PathExists(dir_2_file_old()));
EXPECT_TRUE(PathExists(dir_2_file_other()));
// There should be 1 success and 0 failure logged for the one dir.
histogram_tester_.ExpectUniqueSample("Windows.TmpFileDeleter.SuccessCount", 1,
1);
histogram_tester_.ExpectUniqueSample("Windows.TmpFileDeleter.FailCount", 0,
1);
}
// Tests that starting the cleaner twice doesn't cause it to clean twice.
TEST_F(ImportantFileWriterCleanerTest, StartTwice) {
StartCleaner();
ImportantFileWriterCleaner::AddDirectory(dir_1());
task_environment_.RunUntilIdle();
// Recreate the old file that was just cleaned.
ASSERT_NO_FATAL_FAILURE(CreateOldFile(dir_1_file_old()));
// Start again and make sure it wasn't cleaned again.
ImportantFileWriterCleaner::GetInstance().Start();
task_environment_.RunUntilIdle();
EXPECT_TRUE(PathExists(dir_1_file_old()));
histogram_tester_.ExpectUniqueSample("Windows.TmpFileDeleter.SuccessCount", 1,
1);
histogram_tester_.ExpectUniqueSample("Windows.TmpFileDeleter.FailCount", 0,
1);
}
// Tests that adding a dir twice doesn't cause it to clean twice.
TEST_F(ImportantFileWriterCleanerTest, AddTwice) {
StartCleaner();
ImportantFileWriterCleaner::AddDirectory(dir_1());
task_environment_.RunUntilIdle();
// Recreate the old file that was just cleaned.
ASSERT_NO_FATAL_FAILURE(CreateOldFile(dir_1_file_old()));
// Add the directory again and make sure nothing else is cleaned.
ImportantFileWriterCleaner::AddDirectory(dir_1());
task_environment_.RunUntilIdle();
EXPECT_TRUE(PathExists(dir_1_file_old()));
histogram_tester_.ExpectUniqueSample("Windows.TmpFileDeleter.SuccessCount", 1,
1);
histogram_tester_.ExpectUniqueSample("Windows.TmpFileDeleter.FailCount", 0,
1);
}
// Tests that AddDirectory called from another thread properly bounces back to
// the main thread for processing.
TEST_F(ImportantFileWriterCleanerTest, StartAddFromOtherThread) {
StartCleaner();
// Add from the ThreadPool and wait for it to finish.
TestWaitableEvent waitable_event;
ThreadPool::PostTask(FROM_HERE, BindLambdaForTesting([&]() {
ImportantFileWriterCleaner::AddDirectory(dir_1());
waitable_event.Signal();
}));
waitable_event.Wait();
// Allow the cleaner to run.
task_environment_.RunUntilIdle();
// The old file should have been cleaned from the added dir.
EXPECT_TRUE(PathExists(dir_1_file_new()));
EXPECT_FALSE(PathExists(dir_1_file_old()));
EXPECT_TRUE(PathExists(dir_1_file_other()));
EXPECT_TRUE(PathExists(dir_2_file_new()));
EXPECT_TRUE(PathExists(dir_2_file_old()));
EXPECT_TRUE(PathExists(dir_2_file_other()));
// There should be 1 success and 0 failure logged for the one dir.
histogram_tester_.ExpectUniqueSample("Windows.TmpFileDeleter.SuccessCount", 1,
1);
histogram_tester_.ExpectUniqueSample("Windows.TmpFileDeleter.FailCount", 0,
1);
}
// Tests that adding a directory while a session is processing a previous
// directory works.
TEST_F(ImportantFileWriterCleanerTest, AddStartAdd) {
ImportantFileWriterCleaner::GetInstance().Initialize();
ImportantFileWriterCleaner::AddDirectory(dir_1());
StartCleaner();
ImportantFileWriterCleaner::AddDirectory(dir_2());
task_environment_.RunUntilIdle();
// The old file should have been cleaned from both added dirs.
EXPECT_TRUE(PathExists(dir_1_file_new()));
EXPECT_FALSE(PathExists(dir_1_file_old()));
EXPECT_TRUE(PathExists(dir_1_file_other()));
EXPECT_TRUE(PathExists(dir_2_file_new()));
EXPECT_FALSE(PathExists(dir_2_file_old()));
EXPECT_TRUE(PathExists(dir_2_file_other()));
// There should be 2 success and 2 failure samples (one for each dir).
histogram_tester_.ExpectUniqueSample("Windows.TmpFileDeleter.SuccessCount", 1,
2);
histogram_tester_.ExpectUniqueSample("Windows.TmpFileDeleter.FailCount", 0,
2);
}
// Tests that stopping while the background task is running results in at least
// recording of partial metrics.
TEST_F(ImportantFileWriterCleanerTest, StopWhileRunning) {
ImportantFileWriterCleaner::GetInstance().Initialize();
// Create a great many old files in dir1.
for (int i = 0; i < 100; ++i) {
CreateOldFile(
dir_1().Append(StringPrintf(FILE_PATH_LITERAL("oldie%d.tmp"), i)));
}
ImportantFileWriterCleaner::AddDirectory(dir_1());
StartCleaner();
// It's possible that the background task will quickly delete all 100 files.
// In all likelihood, though, the stop flag will be read and processed before
// then. Either case is a success.
StopCleaner();
task_environment_.RunUntilIdle();
// Expect a single sample indicating that one or more files were deleted.
histogram_tester_.ExpectTotalCount("Windows.TmpFileDeleter.SuccessCount", 1);
}
} // namespace base