blob: c65f999a32675088d7feaa706f844c590bd2b431 [file] [log] [blame]
// Copyright 2020 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/browser/downgrade/snapshot_manager.h"
#include "base/containers/adapters.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/strings/string_number_conversions.h"
#include "chrome/browser/downgrade/downgrade_utils.h"
#include "chrome/browser/downgrade/snapshot_file_collector.h"
#include "chrome/browser/downgrade/user_data_downgrade.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace downgrade {
namespace {
constexpr base::FilePath::StringViewType kSomeFolder =
FILE_PATH_LITERAL("Some Folder");
constexpr base::FilePath::StringViewType kSomeFile =
FILE_PATH_LITERAL("Some File");
constexpr base::FilePath::StringViewType kSomeFolderFile =
FILE_PATH_LITERAL("A File");
constexpr base::FilePath::StringViewType kSomeSubFolder =
FILE_PATH_LITERAL("Some Sub Folder");
constexpr base::FilePath::StringViewType kSomeSubFile =
FILE_PATH_LITERAL("Some Sub File");
constexpr base::FilePath::StringViewType kUserDataFolder =
FILE_PATH_LITERAL("User Data Folder");
constexpr base::FilePath::StringViewType kProfileDataFolder =
FILE_PATH_LITERAL("Profile Data Folder");
constexpr base::FilePath::StringViewType kUserDataFile =
FILE_PATH_LITERAL("User Data File");
constexpr base::FilePath::StringViewType kProfileDataFile =
FILE_PATH_LITERAL("Profile Data File");
constexpr base::FilePath::StringViewType kProfileDataJournalFile =
FILE_PATH_LITERAL("Profile Data File-journal");
constexpr base::FilePath::StringViewType kProfileDataExtFile =
FILE_PATH_LITERAL("Profile Data File.ext");
constexpr base::FilePath::StringViewType kProfileDataExtWalFile =
FILE_PATH_LITERAL("Profile Data File.ext-wal");
constexpr base::FilePath::StringViewType kProfileDataExtShmFile =
FILE_PATH_LITERAL("Profile Data File.ext-shm");
constexpr std::array<base::FilePath::StringViewType, 3>
kProfileDirectoryBaseNames = {FILE_PATH_LITERAL("Default"),
FILE_PATH_LITERAL("Profile 1"),
FILE_PATH_LITERAL("Profile 2")};
// Structure containing a folders and files structure for tests.
// root
// |_ SomeFile
// |_ Some Folder
// |_ Some File
// |_ Some Sub Folder
// |_ Some Sub File
class TestFolderAndFiles {
public:
// Creates the files and folders under the provided |root|.
// Cleanup must be done manually.
static void CreateFilesAndFolders(const base::FilePath& root) {
TestFolderAndFiles folders_and_files(root);
ASSERT_TRUE(base::CreateDirectory(folders_and_files.some_sub_folder_path_));
base::File(folders_and_files.some_file_path_,
base::File::FLAG_CREATE | base::File::FLAG_WRITE);
base::File(folders_and_files.some_folder_file_path_,
base::File::FLAG_CREATE | base::File::FLAG_WRITE);
base::File(folders_and_files.some_sub_folder_file_path_,
base::File::FLAG_CREATE | base::File::FLAG_WRITE);
}
static bool AllPathExists(const base::FilePath& root) {
TestFolderAndFiles folders_and_files(root);
return base::DirectoryExists(folders_and_files.some_folder_path_) &&
base::DirectoryExists(folders_and_files.some_sub_folder_path_) &&
base::PathExists(folders_and_files.some_file_path_) &&
base::PathExists(folders_and_files.some_folder_file_path_) &&
base::PathExists(folders_and_files.some_sub_folder_file_path_);
}
static bool NoPathExists(const base::FilePath& root) {
TestFolderAndFiles folders_and_files(root);
return !base::DirectoryExists(folders_and_files.some_folder_path_) &&
!base::DirectoryExists(folders_and_files.some_sub_folder_path_) &&
!base::PathExists(folders_and_files.some_file_path_) &&
!base::PathExists(folders_and_files.some_folder_file_path_) &&
!base::PathExists(folders_and_files.some_sub_folder_file_path_);
}
private:
explicit TestFolderAndFiles(const base::FilePath& root) {
some_folder_path_ = root.Append(kSomeFolder);
some_folder_file_path_ = some_folder_path_.Append(kSomeFile);
some_file_path_ = some_folder_path_.Append(kSomeFolderFile);
some_sub_folder_path_ = some_folder_path_.Append(kSomeSubFolder);
some_sub_folder_file_path_ = some_sub_folder_path_.Append(kSomeSubFile);
}
~TestFolderAndFiles() = default;
base::FilePath some_folder_path_;
base::FilePath some_file_path_;
base::FilePath some_folder_file_path_;
base::FilePath some_sub_folder_path_;
base::FilePath some_sub_folder_file_path_;
};
} // namespace
class TestSnapshotManager : public SnapshotManager {
public:
explicit TestSnapshotManager(base::FilePath path) : SnapshotManager(path) {}
~TestSnapshotManager() = default;
private:
std::vector<SnapshotItemDetails> GetUserSnapshotItemDetails() const override {
return std::vector<SnapshotItemDetails>{
SnapshotItemDetails(base::FilePath(kUserDataFile),
SnapshotItemDetails::ItemType::kFile, 0,
SnapshotItemId::kMaxValue),
SnapshotItemDetails(base::FilePath(kUserDataFolder),
SnapshotItemDetails::ItemType::kDirectory, 0,
SnapshotItemId::kMaxValue)};
}
std::vector<SnapshotItemDetails> GetProfileSnapshotItemDetails()
const override {
return std::vector<SnapshotItemDetails>{
SnapshotItemDetails(base::FilePath(kProfileDataFile),
SnapshotItemDetails::ItemType::kFile, 0,
SnapshotItemId::kMaxValue),
SnapshotItemDetails(base::FilePath(kProfileDataExtFile),
SnapshotItemDetails::ItemType::kFile, 0,
SnapshotItemId::kMaxValue),
SnapshotItemDetails(base::FilePath(kProfileDataFolder),
SnapshotItemDetails::ItemType::kDirectory, 0,
SnapshotItemId::kMaxValue)};
}
};
class SnapshotManagerTest : public testing::Test {
protected:
void SetUp() override { ASSERT_TRUE(user_data_dir_.CreateUniqueTempDir()); }
void TearDown() override { EXPECT_TRUE(user_data_dir_.Delete()); }
base::FilePath user_data_dir() const { return user_data_dir_.GetPath(); }
void CreateProfileDirectories() {
for (const auto& path : kProfileDirectoryBaseNames) {
ASSERT_TRUE(base::CreateDirectory(user_data_dir().Append(path)));
absolute_profile_directories_.push_back(user_data_dir().Append(path));
}
}
const std::vector<base::FilePath>& absolute_profile_directories() const {
return absolute_profile_directories_;
}
base::FilePath GetSnapshotDirectory(const base::Version& version) const {
return user_data_dir()
.Append(kSnapshotsDir)
.AppendASCII(version.GetString());
}
private:
base::ScopedTempDir user_data_dir_;
std::vector<base::FilePath> absolute_profile_directories_;
};
TEST_F(SnapshotManagerTest, TakeSnapshot) {
base::Version version("10.0.0");
ASSERT_NO_FATAL_FAILURE(CreateProfileDirectories());
const auto& absolute_profile_paths = absolute_profile_directories();
// Files and folders at User Data level that should not be snapshotted.
ASSERT_NO_FATAL_FAILURE(
TestFolderAndFiles::CreateFilesAndFolders(user_data_dir()));
// Files and folders at User Data level that should be snapshotted.
base::File user_data_file(user_data_dir().Append(kUserDataFile),
base::File::FLAG_CREATE | base::File::FLAG_WRITE);
ASSERT_NO_FATAL_FAILURE(TestFolderAndFiles::CreateFilesAndFolders(
user_data_dir().Append(kUserDataFolder)));
for (const auto& path : absolute_profile_paths) {
// Files and folders at Profile Data level that should be snapshotted.
base::File file(path.Append(kProfileDataFile),
base::File::FLAG_CREATE | base::File::FLAG_WRITE);
base::File file_ext(path.Append(kProfileDataExtFile),
base::File::FLAG_CREATE | base::File::FLAG_WRITE);
base::File file_journal(path.Append(kProfileDataJournalFile),
base::File::FLAG_CREATE | base::File::FLAG_WRITE);
base::File file_ext_wal(path.Append(kProfileDataExtWalFile),
base::File::FLAG_CREATE | base::File::FLAG_WRITE);
base::File file_ext_shm(path.Append(kProfileDataExtShmFile),
base::File::FLAG_CREATE | base::File::FLAG_WRITE);
ASSERT_NO_FATAL_FAILURE(TestFolderAndFiles::CreateFilesAndFolders(
path.Append(kProfileDataFolder)));
// Files and folders at Profile Data level that should not be snapshotted.
ASSERT_NO_FATAL_FAILURE(TestFolderAndFiles::CreateFilesAndFolders(path));
}
TestSnapshotManager snapshot_manager(user_data_dir());
snapshot_manager.TakeSnapshot(version);
auto snapshot_dir = GetSnapshotDirectory(version);
EXPECT_TRUE(base::PathExists(user_data_dir().Append(kUserDataFile)));
EXPECT_TRUE(base::DirectoryExists(user_data_dir().Append(kUserDataFolder)));
EXPECT_TRUE(base::PathExists(snapshot_dir.Append(kUserDataFile)));
EXPECT_TRUE(base::DirectoryExists(snapshot_dir.Append(kUserDataFolder)));
EXPECT_TRUE(TestFolderAndFiles::AllPathExists(user_data_dir()));
EXPECT_TRUE(TestFolderAndFiles::NoPathExists(snapshot_dir));
for (const auto& path : absolute_profile_paths) {
EXPECT_TRUE(base::PathExists(path.Append(kProfileDataFile)));
EXPECT_TRUE(base::PathExists(path.Append(kProfileDataExtFile)));
EXPECT_TRUE(base::PathExists(path.Append(kProfileDataJournalFile)));
EXPECT_TRUE(base::PathExists(path.Append(kProfileDataExtWalFile)));
EXPECT_TRUE(base::PathExists(path.Append(kProfileDataExtShmFile)));
EXPECT_TRUE(base::DirectoryExists(path.Append(kProfileDataFolder)));
EXPECT_TRUE(TestFolderAndFiles::AllPathExists(path));
}
for (const auto& path : kProfileDirectoryBaseNames) {
EXPECT_TRUE(
base::PathExists(snapshot_dir.Append(path).Append(kProfileDataFile)));
EXPECT_TRUE(base::PathExists(
snapshot_dir.Append(path).Append(kProfileDataJournalFile)));
EXPECT_TRUE(base::PathExists(
snapshot_dir.Append(path).Append(kProfileDataExtFile)));
EXPECT_TRUE(base::PathExists(
snapshot_dir.Append(path).Append(kProfileDataExtWalFile)));
EXPECT_TRUE(base::PathExists(
snapshot_dir.Append(path).Append(kProfileDataExtShmFile)));
EXPECT_TRUE(base::DirectoryExists(
snapshot_dir.Append(path).Append(kProfileDataFolder)));
EXPECT_TRUE(TestFolderAndFiles::NoPathExists(snapshot_dir.Append(path)));
EXPECT_TRUE(TestFolderAndFiles::AllPathExists(
snapshot_dir.Append(path).Append(kProfileDataFolder)));
}
}
TEST_F(SnapshotManagerTest, RestoreSnapshotOlderVersionAvailable) {
base::Version older_version("9.0.0");
base::Version existing_version("10.0.0");
auto older_snapshot_dir = GetSnapshotDirectory(older_version);
auto existing_snapshot_dir = GetSnapshotDirectory(existing_version);
ASSERT_TRUE(base::CreateDirectory(older_snapshot_dir));
ASSERT_NO_FATAL_FAILURE(
TestFolderAndFiles::CreateFilesAndFolders(existing_snapshot_dir));
base::File(older_snapshot_dir.Append(kDowngradeLastVersionFile),
base::File::FLAG_CREATE | base::File::FLAG_WRITE);
base::File(existing_snapshot_dir.Append(kDowngradeLastVersionFile),
base::File::FLAG_CREATE | base::File::FLAG_WRITE);
for (const auto& profile : kProfileDirectoryBaseNames) {
const base::FilePath profile_folder = existing_snapshot_dir.Append(profile);
ASSERT_NO_FATAL_FAILURE(
TestFolderAndFiles::CreateFilesAndFolders(profile_folder));
}
SnapshotManager snapshot_manager(user_data_dir());
snapshot_manager.RestoreSnapshot(base::Version("11.0.0"));
EXPECT_TRUE(TestFolderAndFiles::AllPathExists(user_data_dir()));
EXPECT_TRUE(TestFolderAndFiles::AllPathExists(existing_snapshot_dir));
for (const auto& profile : kProfileDirectoryBaseNames) {
EXPECT_TRUE(
TestFolderAndFiles::AllPathExists(user_data_dir().Append(profile)));
EXPECT_TRUE(TestFolderAndFiles::AllPathExists(
existing_snapshot_dir.Append(profile)));
}
}
TEST_F(SnapshotManagerTest, RestoreSnapshotTargetVersionAvailable) {
base::Version older_version("9.0.0");
base::Version version("10.0.0");
auto existing_snapshot_dir = GetSnapshotDirectory(version);
auto older_snapshot_dir = GetSnapshotDirectory(older_version);
ASSERT_TRUE(base::CreateDirectory(older_snapshot_dir));
ASSERT_NO_FATAL_FAILURE(
TestFolderAndFiles::CreateFilesAndFolders(existing_snapshot_dir));
base::File(older_snapshot_dir.Append(kDowngradeLastVersionFile),
base::File::FLAG_CREATE | base::File::FLAG_WRITE);
base::File(existing_snapshot_dir.Append(kDowngradeLastVersionFile),
base::File::FLAG_CREATE | base::File::FLAG_WRITE);
for (const auto& profile : kProfileDirectoryBaseNames) {
const base::FilePath profile_folder = existing_snapshot_dir.Append(profile);
ASSERT_NO_FATAL_FAILURE(
TestFolderAndFiles::CreateFilesAndFolders(profile_folder));
}
SnapshotManager snapshot_manager(user_data_dir());
snapshot_manager.RestoreSnapshot(version);
EXPECT_TRUE(TestFolderAndFiles::AllPathExists(user_data_dir()));
EXPECT_TRUE(TestFolderAndFiles::NoPathExists(existing_snapshot_dir));
EXPECT_TRUE(
base::PathExists(user_data_dir().Append(kDowngradeLastVersionFile)));
for (const auto& profile : kProfileDirectoryBaseNames) {
EXPECT_TRUE(
TestFolderAndFiles::AllPathExists(user_data_dir().Append(profile)));
EXPECT_TRUE(TestFolderAndFiles::NoPathExists(
existing_snapshot_dir.Append(profile)));
}
}
TEST_F(SnapshotManagerTest, RestoreSnapshotIgnoresIncompleteSnapshots) {
base::Version older_version("9.0.0");
base::Version existing_version("10.0.0");
base::Version newer_version("11.0.0");
auto older_snapshot_dir = GetSnapshotDirectory(older_version);
auto existing_snapshot_dir = GetSnapshotDirectory(existing_version);
auto newer_snapshot_dir = GetSnapshotDirectory(newer_version);
ASSERT_NO_FATAL_FAILURE(
TestFolderAndFiles::CreateFilesAndFolders(older_snapshot_dir));
ASSERT_NO_FATAL_FAILURE(
TestFolderAndFiles::CreateFilesAndFolders(existing_snapshot_dir));
ASSERT_NO_FATAL_FAILURE(
TestFolderAndFiles::CreateFilesAndFolders(newer_snapshot_dir));
// Only the older directory is a complete snapshot.
base::File(older_snapshot_dir.Append(kDowngradeLastVersionFile),
base::File::FLAG_CREATE | base::File::FLAG_WRITE);
for (const auto& profile : kProfileDirectoryBaseNames) {
const base::FilePath profile_folder = older_snapshot_dir.Append(profile);
ASSERT_NO_FATAL_FAILURE(
TestFolderAndFiles::CreateFilesAndFolders(profile_folder));
}
SnapshotManager snapshot_manager(user_data_dir());
snapshot_manager.RestoreSnapshot(newer_version);
EXPECT_TRUE(TestFolderAndFiles::AllPathExists(user_data_dir()));
EXPECT_TRUE(TestFolderAndFiles::AllPathExists(older_snapshot_dir));
EXPECT_TRUE(TestFolderAndFiles::AllPathExists(existing_snapshot_dir));
EXPECT_TRUE(TestFolderAndFiles::AllPathExists(newer_snapshot_dir));
EXPECT_TRUE(
base::PathExists(user_data_dir().Append(kDowngradeLastVersionFile)));
for (const auto& profile : kProfileDirectoryBaseNames) {
EXPECT_TRUE(
TestFolderAndFiles::AllPathExists(user_data_dir().Append(profile)));
EXPECT_TRUE(
TestFolderAndFiles::AllPathExists(older_snapshot_dir.Append(profile)));
}
}
TEST_F(SnapshotManagerTest, PurgeInvalidAndOldSnapshotsKeepsMaxValidSnapshots) {
std::vector<base::FilePath> invalid_snapshot_paths{
user_data_dir().Append(kSnapshotsDir).AppendASCII("Bad format"),
user_data_dir().Append(kSnapshotsDir).AppendASCII("10"),
};
for (const auto& path : invalid_snapshot_paths)
ASSERT_TRUE(base::CreateDirectory(path));
std::vector<base::FilePath> valid_snapshot_paths{
user_data_dir().Append(kSnapshotsDir).AppendASCII("20"),
user_data_dir().Append(kSnapshotsDir).AppendASCII("30"),
user_data_dir().Append(kSnapshotsDir).AppendASCII("40"),
user_data_dir().Append(kSnapshotsDir).AppendASCII("50"),
};
for (const auto& path : valid_snapshot_paths) {
ASSERT_TRUE(base::CreateDirectory(path));
base::File(path.Append(kDowngradeLastVersionFile),
base::File::FLAG_CREATE | base::File::FLAG_WRITE);
}
int max_number_of_snapshots = 3;
SnapshotManager snapshot_manager(user_data_dir());
snapshot_manager.PurgeInvalidAndOldSnapshots(max_number_of_snapshots,
std::nullopt);
const base::FilePath deletion_directory =
user_data_dir()
.Append(kSnapshotsDir)
.AddExtension(kDowngradeDeleteSuffix);
// All invalid snapshots have been movec
for (const auto& path : invalid_snapshot_paths) {
EXPECT_FALSE(base::PathExists(path));
EXPECT_TRUE(base::PathExists(deletion_directory.Append(path.BaseName())));
}
// Only 3 valid snapshots remains
for (const base::FilePath& path : base::Reversed(valid_snapshot_paths)) {
EXPECT_EQ(base::PathExists(path), max_number_of_snapshots != 0);
EXPECT_EQ(!base::PathExists(deletion_directory.Append(path.BaseName())),
max_number_of_snapshots != 0);
--max_number_of_snapshots;
}
}
TEST_F(SnapshotManagerTest, PurgeInvalidAndOldSnapshotsKeepsValidSnapshots) {
std::vector<base::FilePath> valid_snapshot_paths{
user_data_dir().Append(kSnapshotsDir).AppendASCII("20"),
user_data_dir().Append(kSnapshotsDir).AppendASCII("30"),
};
for (const auto& path : valid_snapshot_paths) {
ASSERT_TRUE(base::CreateDirectory(path));
base::File(path.Append(kDowngradeLastVersionFile),
base::File::FLAG_CREATE | base::File::FLAG_WRITE);
}
int max_number_of_snapshots = 3;
SnapshotManager snapshot_manager(user_data_dir());
snapshot_manager.PurgeInvalidAndOldSnapshots(max_number_of_snapshots,
std::nullopt);
for (const auto& path : valid_snapshot_paths)
EXPECT_TRUE(base::PathExists(path));
}
TEST_F(SnapshotManagerTest,
PurgeInvalidAndOldSnapshotsKeepsValidSnapshotsPerMilestone) {
std::vector<base::FilePath> valid_snapshot_paths{
user_data_dir().Append(kSnapshotsDir).AppendASCII("19.0.0"),
user_data_dir().Append(kSnapshotsDir).AppendASCII("20.0.0"),
user_data_dir().Append(kSnapshotsDir).AppendASCII("20.0.1"),
user_data_dir().Append(kSnapshotsDir).AppendASCII("21.0.1"),
};
for (const auto& path : valid_snapshot_paths) {
ASSERT_TRUE(base::CreateDirectory(path));
base::File(path.Append(kDowngradeLastVersionFile),
base::File::FLAG_CREATE | base::File::FLAG_WRITE);
}
int max_number_of_snapshots = 1;
SnapshotManager snapshot_manager(user_data_dir());
snapshot_manager.PurgeInvalidAndOldSnapshots(max_number_of_snapshots, 20);
EXPECT_TRUE(base::PathExists(valid_snapshot_paths[0]));
EXPECT_FALSE(base::PathExists(valid_snapshot_paths[1]));
EXPECT_TRUE(base::PathExists(valid_snapshot_paths[2]));
EXPECT_TRUE(base::PathExists(valid_snapshot_paths[3]));
}
} // namespace downgrade