// Copyright 2022 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/ash/crosapi/move_migrator.h"
#include <errno.h>
#include <map>
#include <memory>
#include <string>
#include <string_view>
#include "base/containers/contains.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/json/json_writer.h"
#include "base/memory/scoped_refptr.h"
#include "base/strings/string_util.h"
#include "base/system/sys_info.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "chrome/browser/ash/crosapi/browser_data_migrator.h"
#include "chrome/browser/ash/crosapi/browser_data_migrator_util.h"
#include "chrome/browser/extensions/extension_keeplist_chromeos.h"
#include "chrome/common/chrome_constants.h"
#include "chromeos/ash/components/standalone_browser/fake_migration_progress_tracker.h"
#include "components/prefs/testing_pref_service.h"
#include "components/sync/base/storage_type.h"
#include "components/sync/model/blocking_data_type_store_impl.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/leveldatabase/env_chromium.h"
#include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
namespace ash {
namespace {
constexpr char kBookmarksFilePath[] = "Bookmarks"; // lacros
constexpr char kCookiesFilePath[] = "Cookies"; // lacros
constexpr char kDownloadsFilePath[] = "Downloads"; // remain in ash
constexpr char kExtensionStateFilePath[] = "Extension State"; // split
constexpr char kSharedProtoDBPath[] = "shared_proto_db"; // need copy
constexpr char kCacheFilePath[] = "Cache"; // deletable
constexpr char kDataFilePath[] = "Data";
constexpr char kDataContent[] = "Hello, World!";
// ID of an extension that will be moved from Ash to Lacros.
// NOTE: we use a sequence of characters that can't be an
// actual AppId here, so we can be sure that it won't be
// included in `kExtensionsAshOnly`.
constexpr char kMoveExtensionId[] = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
// ID of an extension that runs in both Lacros and Ash chrome.
std::string_view GetBothChromesExtensionId() {
// Any id from the Ash allowlist works for tests. Pick the first
// element of the allowlist for convenience.
DCHECK(
!extensions::GetExtensionsAndAppsRunInOSAndStandaloneBrowser().empty());
return extensions::GetExtensionsAndAppsRunInOSAndStandaloneBrowser()[0];
}
constexpr syncer::DataType kAshSyncDataType =
browser_data_migrator_util::kAshOnlySyncDataTypesForLacrosMigration[0];
constexpr syncer::DataType kLacrosSyncDataType = syncer::DataType::WEB_APPS;
static_assert(!base::Contains(
browser_data_migrator_util::kAshOnlySyncDataTypesForLacrosMigration,
kLacrosSyncDataType));
constexpr int64_t kRequiredDiskSpaceForBot =
browser_data_migrator_util::kBuffer * 2;
base::FilePath GetNigoriPath(const base::FilePath& profile_path) {
return profile_path.Append(browser_data_migrator_util::kSyncDataFilePath)
.Append(browser_data_migrator_util::kSyncDataNigoriFileName);
}
// Setup the `Extensions` folder inside a profile.
// If `ash_only` is true, it will only generate data associated to extensions
// that have to be kept in Ash. Otherwise, it will generate data for both
// categories of extensions.
void SetUpExtensions(const base::FilePath& profile_path,
bool ash = true,
bool lacros = true,
bool both = true) {
base::FilePath path =
profile_path.Append(browser_data_migrator_util::kExtensionsFilePath);
// Generate data for an extension that has to be moved to Lacros.
if (lacros) {
ASSERT_TRUE(base::CreateDirectory(path.Append(kMoveExtensionId)));
ASSERT_TRUE(base::WriteFile(
path.Append(kMoveExtensionId).Append(kDataFilePath), kDataContent));
}
// Generate data for an extension that has to stay in Ash.
if (ash) {
std::string keep_extension_id =
browser_data_migrator_util::kExtensionsAshOnly[0];
ASSERT_TRUE(base::CreateDirectory(path.Append(keep_extension_id)));
ASSERT_TRUE(base::WriteFile(
path.Append(keep_extension_id).Append(kDataFilePath), kDataContent));
}
// Generate data for an extension that has to be in both Ash and Lacros.
if (both) {
std::string_view both_extension_id = GetBothChromesExtensionId();
ASSERT_TRUE(base::CreateDirectory(path.Append(both_extension_id)));
ASSERT_TRUE(base::WriteFile(
path.Append(both_extension_id).Append(kDataFilePath), kDataContent));
}
}
// Setup the `Storage` folder inside a profile.
// If `ash_only` is true, it will only generate data associated to extensions
// that have to be kept in Ash. Otherwise, it will generate data for both
// categories of extensions.
void SetUpStorage(const base::FilePath& profile_path,
bool ash = true,
bool lacros = true,
bool both = true) {
base::FilePath path =
profile_path.Append(browser_data_migrator_util::kStorageFilePath)
.Append(browser_data_migrator_util::kStorageExtFilePath);
// Generate data for an extension that has to be moved to Lacros.
if (lacros) {
ASSERT_TRUE(base::CreateDirectory(path.Append(kMoveExtensionId)));
ASSERT_TRUE(base::WriteFile(
path.Append(kMoveExtensionId).Append(kDataFilePath), kDataContent));
}
// Generate data for an extension that has to stay in Ash.
if (ash) {
std::string keep_extension_id =
browser_data_migrator_util::kExtensionsAshOnly[0];
ASSERT_TRUE(base::CreateDirectory(path.Append(keep_extension_id)));
ASSERT_TRUE(base::WriteFile(
path.Append(keep_extension_id).Append(kDataFilePath), kDataContent));
}
// Generate data for an extension that has to be in both Ash and Lacros.
if (both) {
std::string both_extension_id = std::string(GetBothChromesExtensionId());
ASSERT_TRUE(base::CreateDirectory(path.Append(both_extension_id)));
ASSERT_TRUE(base::WriteFile(
path.Append(both_extension_id).Append(kDataFilePath), kDataContent));
}
}
// Setup the `Local Storage` folder inside a profile.
// If `ash_only` is true, it will only generate data associated to extensions
// that have to be kept in Ash. Otherwise, it will generate data for both
// categories of extensions.
void SetUpLocalStorage(const base::FilePath& profile_path,
bool ash_only = false) {
using std::string_literals::operator""s;
base::FilePath path =
profile_path.Append(browser_data_migrator_util::kLocalStorageFilePath)
.Append(browser_data_migrator_util::kLocalStorageLeveldbName);
// Open a new LevelDB database.
leveldb_env::Options options;
options.create_if_missing = true;
std::unique_ptr<leveldb::DB> db;
leveldb::Status status = leveldb_env::OpenDB(options, path.value(), &db);
ASSERT_TRUE(status.ok());
// Part of the LocalStorage schema.
leveldb::WriteBatch batch;
batch.Put("VERSION", "1");
// Generate data for an extension that has to be moved to Lacros.
std::string key;
if (!ash_only) {
batch.Put("META:chrome-extension://" + std::string(kMoveExtensionId),
"meta");
batch.Put(
"_chrome-extension://" + std::string(kMoveExtensionId) + "\x00key"s,
"value");
}
// Generate data for an extension that has to stay in Ash.
std::string keep_extension_id =
browser_data_migrator_util::kExtensionsAshOnly[0];
batch.Put("META:chrome-extension://" + keep_extension_id, "meta");
batch.Put("_chrome-extension://" + keep_extension_id + "\x00key"s, "value");
// Generate data for an extension that has to be in both Ash and Lacros.
std::string both_extension_id = std::string(GetBothChromesExtensionId());
batch.Put("META:chrome-extension://" + both_extension_id, "meta");
batch.Put("_chrome-extension://" + both_extension_id + "\x00key"s, "value");
leveldb::WriteOptions write_options;
write_options.sync = true;
status = db->Write(write_options, &batch);
ASSERT_TRUE(status.ok());
}
// Setup the `Extension State` folder inside a profile directory.
void SetUpExtensionState(const base::FilePath& profile_path) {
base::FilePath path = profile_path.Append(kExtensionStateFilePath);
leveldb_env::Options options;
options.create_if_missing = true;
std::unique_ptr<leveldb::DB> db;
leveldb::Status status = leveldb_env::OpenDB(options, path.value(), &db);
ASSERT_TRUE(status.ok());
std::string keep_extension_id =
browser_data_migrator_util::kExtensionsAshOnly[0];
std::string both_extension_id = std::string(GetBothChromesExtensionId());
leveldb::WriteBatch batch;
batch.Put(std::string(kMoveExtensionId) + ".key", "value");
batch.Put(keep_extension_id + ".key", "value");
batch.Put(both_extension_id + ".key", "value");
leveldb::WriteOptions write_options;
write_options.sync = true;
status = db->Write(write_options, &batch);
ASSERT_TRUE(status.ok());
}
void SetUpIndexedDB(const base::FilePath& profile_path,
bool ash = true,
bool lacros = true,
bool both = true) {
if (lacros) {
const auto [move_extension_blob_path, move_extension_leveldb_path] =
browser_data_migrator_util::GetIndexedDBPaths(profile_path,
kMoveExtensionId);
ASSERT_TRUE(base::CreateDirectory(move_extension_blob_path));
ASSERT_TRUE(base::CreateDirectory(move_extension_leveldb_path));
ASSERT_TRUE(base::WriteFile(move_extension_blob_path.Append(kDataFilePath),
kDataContent));
ASSERT_TRUE(base::WriteFile(
move_extension_leveldb_path.Append(kDataFilePath), kDataContent));
}
if (ash) {
const char* keep_extension_id =
browser_data_migrator_util::kExtensionsAshOnly[0];
const auto [keep_extension_blob_path, keep_extension_leveldb_path] =
browser_data_migrator_util::GetIndexedDBPaths(profile_path,
keep_extension_id);
ASSERT_TRUE(base::CreateDirectory(keep_extension_blob_path));
ASSERT_TRUE(base::CreateDirectory(keep_extension_leveldb_path));
ASSERT_TRUE(base::WriteFile(keep_extension_blob_path.Append(kDataFilePath),
kDataContent));
ASSERT_TRUE(base::WriteFile(
keep_extension_leveldb_path.Append(kDataFilePath), kDataContent));
}
if (both) {
const char* both_extension_id = GetBothChromesExtensionId().data();
const auto [both_extension_blob_path, both_extension_leveldb_path] =
browser_data_migrator_util::GetIndexedDBPaths(profile_path,
both_extension_id);
ASSERT_TRUE(base::CreateDirectory(both_extension_blob_path));
ASSERT_TRUE(base::CreateDirectory(both_extension_leveldb_path));
ASSERT_TRUE(base::WriteFile(both_extension_blob_path.Append(kDataFilePath),
kDataContent));
ASSERT_TRUE(base::WriteFile(
both_extension_leveldb_path.Append(kDataFilePath), kDataContent));
}
}
void SetUpPreferences(const base::FilePath& profile_path,
bool ash = true,
bool lacros = true) {
base::FilePath path = profile_path.Append(chrome::kPreferencesFilename);
std::string contents;
base::Value::Dict dict;
if (ash) {
dict.SetByDottedPath(browser_data_migrator_util::kAshOnlyPreferencesKeys[0],
"test1");
}
if (lacros) {
dict.SetByDottedPath(
browser_data_migrator_util::kLacrosOnlyPreferencesKeys[0], "test2");
}
dict.SetByDottedPath("unrelated.key", "test3");
ASSERT_TRUE(base::JSONWriter::Write(dict, &contents));
ASSERT_TRUE(base::WriteFile(path, contents));
}
void SetUpSyncDataLevelDB(const base::FilePath& profile_path,
bool ash = true,
bool lacros = true) {
base::FilePath leveldb_path =
profile_path.Append(browser_data_migrator_util::kSyncDataFilePath)
.Append(browser_data_migrator_util::kSyncDataLeveldbName);
leveldb_env::Options options;
options.create_if_missing = true;
std::unique_ptr<leveldb::DB> db;
leveldb::Status status =
leveldb_env::OpenDB(options, leveldb_path.value(), &db);
ASSERT_TRUE(status.ok());
leveldb::WriteBatch batch;
if (ash) {
batch.Put(syncer::FormatDataPrefix(kAshSyncDataType,
syncer::StorageType::kUnspecified) +
kMoveExtensionId,
"ash_data");
batch.Put(syncer::FormatMetaPrefix(kAshSyncDataType,
syncer::StorageType::kUnspecified) +
kMoveExtensionId,
"ash_metadata");
batch.Put(syncer::FormatGlobalMetadataKey(
kAshSyncDataType, syncer::StorageType::kUnspecified),
"ash_globalmetadata");
}
if (lacros) {
batch.Put(syncer::FormatDataPrefix(kLacrosSyncDataType,
syncer::StorageType::kUnspecified) +
kMoveExtensionId,
"lacros_data");
batch.Put(syncer::FormatMetaPrefix(kLacrosSyncDataType,
syncer::StorageType::kUnspecified) +
kMoveExtensionId,
"lacros_metadata");
batch.Put(syncer::FormatGlobalMetadataKey(
kLacrosSyncDataType, syncer::StorageType::kUnspecified),
"lacros_globalmetadata");
}
leveldb::WriteOptions write_options;
write_options.sync = true;
status = db->Write(write_options, &batch);
ASSERT_TRUE(status.ok());
}
void SetUpProfileDirectory(const base::FilePath& path) {
// Setup `path` as below.
// |- Bookmarks/
// |- Cache/
// |- Cookies
// |- Downloads/
// |- Extension State/
// |- Extensions/
// |- IndexedDB/
// |- Local Storage/
// |- Login Data/
// |- shared_proto_db/
// |- Preferences
// |- Storage/
// |- Sync Data/
// |- LevelDB
// |- Nigori.bin
ASSERT_TRUE(base::CreateDirectory(path.Append(kCacheFilePath)));
ASSERT_TRUE(base::WriteFile(path.Append(kCacheFilePath).Append(kDataFilePath),
kDataContent));
ASSERT_TRUE(base::CreateDirectory(path.Append(kDownloadsFilePath)));
ASSERT_TRUE(base::WriteFile(
path.Append(kDownloadsFilePath).Append(kDataFilePath), kDataContent));
ASSERT_TRUE(base::CreateDirectory(path.Append(kBookmarksFilePath)));
ASSERT_TRUE(base::WriteFile(
path.Append(kBookmarksFilePath).Append(kDataFilePath), kDataContent));
ASSERT_TRUE(base::WriteFile(path.Append(kCookiesFilePath), kDataContent));
ASSERT_TRUE(base::CreateDirectory(path.Append(kSharedProtoDBPath)));
ASSERT_TRUE(base::WriteFile(
path.Append(kSharedProtoDBPath).Append(kDataFilePath), kDataContent));
ASSERT_TRUE(base::CreateDirectory(
path.Append(browser_data_migrator_util::kSyncDataFilePath)));
ASSERT_TRUE(base::WriteFile(GetNigoriPath(path), kDataContent));
SetUpExtensions(path);
SetUpStorage(path);
SetUpLocalStorage(path);
SetUpExtensionState(path);
SetUpIndexedDB(path);
SetUpPreferences(path);
SetUpSyncDataLevelDB(path);
}
std::map<std::string, std::string> ReadLevelDB(const base::FilePath& path) {
leveldb_env::Options options;
options.create_if_missing = false;
std::unique_ptr<leveldb::DB> db;
leveldb::Status status = leveldb_env::OpenDB(options, path.value(), &db);
EXPECT_TRUE(status.ok());
std::map<std::string, std::string> db_map;
std::unique_ptr<leveldb::Iterator> it(
db->NewIterator(leveldb::ReadOptions()));
for (it->SeekToFirst(); it->Valid(); it->Next()) {
db_map.emplace(it->key().ToString(), it->value().ToString());
}
return db_map;
}
} // namespace
TEST(MoveMigratorTest, PreMigrationCleanUp) {
base::ScopedTempDir scoped_temp_dir;
ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
ASSERT_GE(base::SysInfo::AmountOfFreeDiskSpace(scoped_temp_dir.GetPath()),
kRequiredDiskSpaceForBot);
// `PreMigrationCleanUp()` returns true if there is nothing to delete.
const base::FilePath original_profile_dir_1 =
scoped_temp_dir.GetPath().Append("user1");
EXPECT_TRUE(base::CreateDirectory(original_profile_dir_1));
MoveMigrator::TaskResult result_1 =
MoveMigrator::PreMigrationCleanUp(original_profile_dir_1);
ASSERT_EQ(result_1.status, MoveMigrator::TaskStatus::kSucceeded);
EXPECT_FALSE(result_1.extra_bytes_required_to_be_freed.has_value());
// `PreMigrationCleanUp()` deletes any `.../lacros/` directory and returns
// true.
const base::FilePath original_profile_dir_2 =
scoped_temp_dir.GetPath().Append("user2");
EXPECT_TRUE(base::CreateDirectory(original_profile_dir_2));
EXPECT_TRUE(base::CreateDirectory(
original_profile_dir_2.Append(browser_data_migrator_util::kLacrosDir)));
ASSERT_TRUE(base::WriteFile(
original_profile_dir_2.Append(browser_data_migrator_util::kLacrosDir)
.Append(chrome::kFirstRunSentinel),
std::string_view()));
MoveMigrator::TaskResult result_2 =
MoveMigrator::PreMigrationCleanUp(original_profile_dir_2);
ASSERT_EQ(result_2.status, MoveMigrator::TaskStatus::kSucceeded);
EXPECT_FALSE(result_2.extra_bytes_required_to_be_freed.has_value());
EXPECT_FALSE(base::PathExists(
original_profile_dir_2.Append(browser_data_migrator_util::kLacrosDir)));
// `PreMigrationCleanUp()` deletes any deletable item and returns true.
const base::FilePath original_profile_dir_3 =
scoped_temp_dir.GetPath().Append("user3");
EXPECT_TRUE(base::CreateDirectory(original_profile_dir_3));
ASSERT_TRUE(base::WriteFile(original_profile_dir_3.Append(kCacheFilePath),
kDataContent));
MoveMigrator::TaskResult result_3 =
MoveMigrator::PreMigrationCleanUp(original_profile_dir_3);
ASSERT_EQ(result_3.status, MoveMigrator::TaskStatus::kSucceeded);
EXPECT_FALSE(result_3.extra_bytes_required_to_be_freed.has_value());
EXPECT_FALSE(base::PathExists(
original_profile_dir_3.Append(browser_data_migrator_util::kLacrosDir)));
EXPECT_FALSE(base::PathExists(original_profile_dir_3.Append(kCacheFilePath)));
// `PreMigrationCleanUp()` deletes any temporary split directory and returns
// true.
const base::FilePath original_profile_dir_4 =
scoped_temp_dir.GetPath().Append("user4");
EXPECT_TRUE(base::CreateDirectory(original_profile_dir_4));
EXPECT_TRUE(base::CreateDirectory(
original_profile_dir_4.Append(browser_data_migrator_util::kSplitTmpDir)));
ASSERT_TRUE(base::WriteFile(
original_profile_dir_4.Append(browser_data_migrator_util::kSplitTmpDir)
.Append("TestFile"),
kDataContent));
MoveMigrator::TaskResult result_4 =
MoveMigrator::PreMigrationCleanUp(original_profile_dir_4);
ASSERT_EQ(result_4.status, MoveMigrator::TaskStatus::kSucceeded);
EXPECT_FALSE(result_4.extra_bytes_required_to_be_freed.has_value());
EXPECT_FALSE(base::PathExists(
original_profile_dir_4.Append(browser_data_migrator_util::kSplitTmpDir)));
}
TEST(MoveMigratorTest, SetupLacrosDir) {
base::ScopedTempDir scoped_temp_dir;
ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
const base::FilePath original_profile_dir = scoped_temp_dir.GetPath();
SetUpProfileDirectory(original_profile_dir);
std::unique_ptr<standalone_browser::MigrationProgressTracker>
progress_tracker =
std::make_unique<standalone_browser::FakeMigrationProgressTracker>();
scoped_refptr<browser_data_migrator_util::CancelFlag> cancel_flag =
base::MakeRefCounted<browser_data_migrator_util::CancelFlag>();
MoveMigrator::TaskResult result = MoveMigrator::SetupLacrosDir(
original_profile_dir, std::move(progress_tracker), cancel_flag);
ASSERT_EQ(result.status, MoveMigrator::TaskStatus::kSucceeded);
const base::FilePath tmp_user_dir =
original_profile_dir.Append(browser_data_migrator_util::kMoveTmpDir);
const base::FilePath tmp_profile_dir =
tmp_user_dir.Append(browser_data_migrator_util::kLacrosProfilePath);
// Check chrome::kFirstRunSentinel, need copy item and lacros item exist in
// lacros dir.
EXPECT_TRUE(base::PathExists(tmp_user_dir.Append(chrome::kFirstRunSentinel)));
EXPECT_TRUE(base::PathExists(tmp_profile_dir.Append(kSharedProtoDBPath)));
}
TEST(MoveMigratorTest, MoveLacrosItemsToNewDir) {
base::ScopedTempDir scoped_temp_dir;
ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
const base::FilePath original_profile_dir = scoped_temp_dir.GetPath();
SetUpProfileDirectory(original_profile_dir);
const base::FilePath tmp_profile_dir =
original_profile_dir.Append(browser_data_migrator_util::kMoveTmpDir)
.Append(browser_data_migrator_util::kLacrosProfilePath);
ASSERT_TRUE(base::CreateDirectory(tmp_profile_dir));
ASSERT_EQ(MoveMigrator::MoveLacrosItemsToNewDir(original_profile_dir).status,
MoveMigrator::TaskStatus::kSucceeded);
EXPECT_FALSE(
base::PathExists(original_profile_dir.Append(kBookmarksFilePath)));
EXPECT_FALSE(base::PathExists(original_profile_dir.Append(kCookiesFilePath)));
EXPECT_FALSE(base::PathExists(GetNigoriPath(original_profile_dir)));
EXPECT_TRUE(base::PathExists(tmp_profile_dir.Append(kBookmarksFilePath)));
EXPECT_TRUE(base::PathExists(tmp_profile_dir.Append(kCookiesFilePath)));
EXPECT_TRUE(base::PathExists(
tmp_profile_dir.Append(browser_data_migrator_util::kExtensionsFilePath)));
EXPECT_TRUE(base::PathExists(
tmp_profile_dir.Append(browser_data_migrator_util::kIndexedDBFilePath)));
EXPECT_TRUE(base::PathExists(GetNigoriPath(tmp_profile_dir)));
}
TEST(MoveMigratorTest, MoveLacrosItemsToNewDirFailIfNoWritePermForLacrosItem) {
base::ScopedTempDir scoped_temp_dir;
ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
const base::FilePath original_profile_dir = scoped_temp_dir.GetPath();
SetUpProfileDirectory(original_profile_dir);
// Remove write permission from a lacros item.
base::SetPosixFilePermissions(original_profile_dir.Append(kBookmarksFilePath),
0500);
MoveMigrator::TaskResult result =
MoveMigrator::MoveLacrosItemsToNewDir(original_profile_dir);
ASSERT_EQ(result.status,
MoveMigrator::TaskStatus::kMoveLacrosItemsToNewDirNoWritePerm);
ASSERT_TRUE(result.posix_errno.has_value());
ASSERT_EQ(result.posix_errno.value(), EACCES);
}
TEST(MoveMigratorTest, SetupAshSplitDir) {
using std::string_literals::operator""s;
base::ScopedTempDir scoped_temp_dir;
ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
const base::FilePath original_profile_dir = scoped_temp_dir.GetPath();
SetUpProfileDirectory(original_profile_dir);
const base::FilePath tmp_user_dir =
original_profile_dir.Append(browser_data_migrator_util::kMoveTmpDir);
const base::FilePath tmp_profile_dir =
tmp_user_dir.Append(browser_data_migrator_util::kLacrosProfilePath);
ASSERT_TRUE(base::CreateDirectory(tmp_user_dir));
ASSERT_TRUE(base::CreateDirectory(tmp_profile_dir));
EXPECT_EQ(MoveMigrator::SetupAshSplitDir(original_profile_dir, 0).status,
MoveMigrator::TaskStatus::kSucceeded);
const base::FilePath tmp_split_dir =
original_profile_dir.Append(browser_data_migrator_util::kSplitTmpDir);
// Check `Extensions` is present in the split directory.
base::FilePath path =
tmp_split_dir.Append(browser_data_migrator_util::kExtensionsFilePath);
EXPECT_TRUE(base::PathExists(path));
// Check `Extensions` contains only extensions that have to stay in both Ash
// and Lacros at this stage.
std::string keep_extension_id =
browser_data_migrator_util::kExtensionsAshOnly[0];
std::string both_extension_id = std::string(GetBothChromesExtensionId());
EXPECT_FALSE(base::PathExists(path.Append(keep_extension_id)));
EXPECT_TRUE(base::PathExists(path.Append(both_extension_id)));
EXPECT_FALSE(base::PathExists(path.Append(kMoveExtensionId)));
// Check `IndexedDB` contains only extensions that have to stay in both Ash
// and Lacros at this stage.
const auto [keep_extension_blob_path, keep_extension_leveldb_path] =
browser_data_migrator_util::GetIndexedDBPaths(tmp_split_dir,
keep_extension_id.c_str());
const auto [both_extension_blob_path, both_extension_leveldb_path] =
browser_data_migrator_util::GetIndexedDBPaths(tmp_split_dir,
both_extension_id.c_str());
const auto [move_extension_blob_path, move_extension_leveldb_path] =
browser_data_migrator_util::GetIndexedDBPaths(tmp_split_dir,
kMoveExtensionId);
EXPECT_FALSE(
base::PathExists(keep_extension_blob_path.Append(kDataFilePath)));
EXPECT_FALSE(
base::PathExists(keep_extension_leveldb_path.Append(kDataFilePath)));
EXPECT_TRUE(base::PathExists(both_extension_blob_path.Append(kDataFilePath)));
EXPECT_TRUE(
base::PathExists(both_extension_leveldb_path.Append(kDataFilePath)));
EXPECT_FALSE(
base::PathExists(move_extension_blob_path.Append(kDataFilePath)));
EXPECT_FALSE(
base::PathExists(move_extension_leveldb_path.Append(kDataFilePath)));
// Check `Local Storage` is present in the split directory.
path = tmp_split_dir.Append(browser_data_migrator_util::kLocalStorageFilePath)
.Append(browser_data_migrator_util::kLocalStorageLeveldbName);
EXPECT_TRUE(base::PathExists(path));
// Check the content of the leveldb database. It should contain only
// extensions in the keep list.
auto db_map = ReadLevelDB(path);
EXPECT_EQ(5u, db_map.size());
EXPECT_EQ("1", db_map["VERSION"]);
std::string key = "_chrome-extension://" + keep_extension_id + "\x00key"s;
EXPECT_EQ("meta", db_map["META:chrome-extension://" + keep_extension_id]);
EXPECT_EQ("value", db_map[key]);
key = "_chrome-extension://" + both_extension_id + "\x00key"s;
EXPECT_EQ("meta", db_map["META:chrome-extension://" + both_extension_id]);
EXPECT_EQ("value", db_map[key]);
// Check `Extension State` is present in the split directory.
path = tmp_split_dir.Append(kExtensionStateFilePath);
EXPECT_TRUE(base::PathExists(path));
// Check the content of the leveldb database. It should contain only
// extensions in the keep list.
db_map = ReadLevelDB(path);
EXPECT_EQ(2u, db_map.size());
EXPECT_EQ("value", db_map[keep_extension_id + ".key"]);
// Check Preferences is present in both tmp_profile_dir and tmp_split_dir.
path = tmp_split_dir.Append(chrome::kPreferencesFilename);
EXPECT_TRUE(base::PathExists(path));
base::FilePath lacros_path =
tmp_profile_dir.Append(chrome::kPreferencesFilename);
EXPECT_TRUE(base::PathExists(lacros_path));
// Check `Sync Data`/LevelDB is present in both tmp_profile_dir and
// tmp_split_dir.
path = tmp_split_dir.Append(browser_data_migrator_util::kSyncDataFilePath)
.Append(browser_data_migrator_util::kSyncDataLeveldbName);
EXPECT_TRUE(base::PathExists(path));
lacros_path =
tmp_profile_dir.Append(browser_data_migrator_util::kSyncDataFilePath)
.Append(browser_data_migrator_util::kSyncDataLeveldbName);
EXPECT_TRUE(base::PathExists(lacros_path));
}
TEST(MoveMigratorTest, ResumeRequired) {
const std::string user_id_hash = "abcd";
TestingPrefServiceSimple pref_service;
MoveMigrator::RegisterLocalStatePrefs(pref_service.registry());
EXPECT_FALSE(MoveMigrator::ResumeRequired(&pref_service, user_id_hash));
MoveMigrator::SetResumeStep(&pref_service, user_id_hash,
MoveMigrator::ResumeStep::kMoveLacrosItems);
EXPECT_TRUE(MoveMigrator::ResumeRequired(&pref_service, user_id_hash));
MoveMigrator::SetResumeStep(&pref_service, user_id_hash,
MoveMigrator::ResumeStep::kMoveSplitItems);
EXPECT_TRUE(MoveMigrator::ResumeRequired(&pref_service, user_id_hash));
MoveMigrator::SetResumeStep(&pref_service, user_id_hash,
MoveMigrator::ResumeStep::kMoveTmpDir);
EXPECT_TRUE(MoveMigrator::ResumeRequired(&pref_service, user_id_hash));
MoveMigrator::SetResumeStep(&pref_service, user_id_hash,
MoveMigrator::ResumeStep::kCompleted);
EXPECT_FALSE(MoveMigrator::ResumeRequired(&pref_service, user_id_hash));
}
TEST(MoveMigratorTest, RecordPosixErrnoUMA) {
base::HistogramTester histogram_tester;
MoveMigrator::RecordPosixErrnoUMA(
MoveMigrator::TaskStatus::kMoveLacrosItemsToNewDirNoWritePerm, EPERM);
std::string uma_name =
"Ash.BrowserDataMigrator.MoveMigrator.PosixErrno."
"MoveLacrosItemsToNewDirNoWritePerm";
histogram_tester.ExpectBucketCount(uma_name, EPERM, 1);
}
class MoveMigratorMigrateTest : public ::testing::Test {
public:
MoveMigratorMigrateTest() : user_id_hash_("abcd") {}
void SetUp() override {
ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
ASSERT_GE(base::SysInfo::AmountOfFreeDiskSpace(scoped_temp_dir_.GetPath()),
kRequiredDiskSpaceForBot);
original_profile_dir_ = scoped_temp_dir_.GetPath();
SetUpProfileDirectory(original_profile_dir_);
std::unique_ptr<standalone_browser::MigrationProgressTracker>
progress_tracker = std::make_unique<
standalone_browser::FakeMigrationProgressTracker>();
scoped_refptr<browser_data_migrator_util::CancelFlag> cancel_flag =
base::MakeRefCounted<browser_data_migrator_util::CancelFlag>();
migrator_ = std::make_unique<MoveMigrator>(
original_profile_dir_, user_id_hash_, std::move(progress_tracker),
cancel_flag, &pref_service_, migrate_result_.GetCallback());
MoveMigrator::RegisterLocalStatePrefs(pref_service_.registry());
}
void CheckProfileDirFinalState() {
// Check that `original_profile_dir_` is as below as a result of the
// migration.
// |- Downloads
// |- Extensions
// |- IndexedDB
// |- Local Storage
// |- Login Data
// |- shared_proto_db
// |- Preferences
// |- Storage/
// |- Sync Data/LevelDB
// |- lacros/First Run
// |- lacros/Default/
// |- Bookmarks
// |- Cookies
// |- Extensions
// |- IndexedDB
// |- Local Storage
// |- shared_proto_db
// |- Preferences
// |- Storage/
// |- Sync Data/
// |- LevelDB
// |- Nigori.bin
const base::FilePath new_user_dir =
original_profile_dir_.Append(browser_data_migrator_util::kLacrosDir);
const base::FilePath new_profile_dir =
new_user_dir.Append(browser_data_migrator_util::kLacrosProfilePath);
EXPECT_TRUE(
base::PathExists(new_user_dir.Append(chrome::kFirstRunSentinel)));
EXPECT_FALSE(
base::PathExists(original_profile_dir_.Append(kBookmarksFilePath)));
EXPECT_TRUE(base::PathExists(new_profile_dir.Append(kBookmarksFilePath)));
EXPECT_FALSE(
base::PathExists(original_profile_dir_.Append(kCookiesFilePath)));
EXPECT_TRUE(base::PathExists(new_profile_dir.Append(kCookiesFilePath)));
EXPECT_TRUE(
base::PathExists(original_profile_dir_.Append(kSharedProtoDBPath)));
EXPECT_TRUE(base::PathExists(new_profile_dir.Append(kSharedProtoDBPath)));
EXPECT_TRUE(
base::PathExists(original_profile_dir_.Append(kDownloadsFilePath)));
EXPECT_FALSE(base::PathExists(new_profile_dir.Append(kDownloadsFilePath)));
EXPECT_FALSE(
base::PathExists(original_profile_dir_.Append(kCacheFilePath)));
EXPECT_FALSE(base::PathExists(new_profile_dir.Append(kCacheFilePath)));
// Extensions.
std::string keep_extension_id =
browser_data_migrator_util::kExtensionsAshOnly[0];
std::string_view both_extension_id = GetBothChromesExtensionId();
EXPECT_TRUE(base::PathExists(
original_profile_dir_
.Append(browser_data_migrator_util::kExtensionsFilePath)
.Append(keep_extension_id)));
EXPECT_TRUE(base::PathExists(
original_profile_dir_
.Append(browser_data_migrator_util::kExtensionsFilePath)
.Append(both_extension_id)));
EXPECT_FALSE(base::PathExists(
original_profile_dir_
.Append(browser_data_migrator_util::kExtensionsFilePath)
.Append(kMoveExtensionId)));
EXPECT_TRUE(base::PathExists(
new_profile_dir.Append(browser_data_migrator_util::kExtensionsFilePath)
.Append(kMoveExtensionId)));
// Storage.
EXPECT_TRUE(base::PathExists(
original_profile_dir_
.Append(browser_data_migrator_util::kStorageFilePath)
.Append(browser_data_migrator_util::kStorageExtFilePath)
.Append(keep_extension_id)));
EXPECT_TRUE(base::PathExists(
original_profile_dir_
.Append(browser_data_migrator_util::kStorageFilePath)
.Append(browser_data_migrator_util::kStorageExtFilePath)
.Append(both_extension_id)));
EXPECT_FALSE(base::PathExists(
original_profile_dir_
.Append(browser_data_migrator_util::kStorageFilePath)
.Append(browser_data_migrator_util::kStorageExtFilePath)
.Append(kMoveExtensionId)));
EXPECT_TRUE(base::PathExists(
new_profile_dir.Append(browser_data_migrator_util::kStorageFilePath)
.Append(browser_data_migrator_util::kStorageExtFilePath)
.Append(kMoveExtensionId)));
// Local Storage.
const base::FilePath ash_local_storage_path =
original_profile_dir_
.Append(browser_data_migrator_util::kLocalStorageFilePath)
.Append(browser_data_migrator_util::kLocalStorageLeveldbName);
const base::FilePath lacros_local_storage_path =
new_profile_dir
.Append(browser_data_migrator_util::kLocalStorageFilePath)
.Append(browser_data_migrator_util::kLocalStorageLeveldbName);
EXPECT_TRUE(base::PathExists(ash_local_storage_path));
EXPECT_TRUE(base::PathExists(lacros_local_storage_path));
// Ash contains only keys relevant to the extension keep list.
auto ash_local_storage = ReadLevelDB(ash_local_storage_path);
EXPECT_EQ(5u, ash_local_storage.size());
// Lacros contains all the keys.
auto lacros_local_storage = ReadLevelDB(lacros_local_storage_path);
EXPECT_EQ(7u, lacros_local_storage.size());
// Ash contains only IndexedDB folders of extensions in keeplist.
{
const auto [keep_extension_blob_path, keep_extension_leveldb_path] =
browser_data_migrator_util::GetIndexedDBPaths(
original_profile_dir_, keep_extension_id.c_str());
const auto [both_extension_blob_path, both_extension_leveldb_path] =
browser_data_migrator_util::GetIndexedDBPaths(
original_profile_dir_, both_extension_id.data());
const auto [move_extension_blob_path, move_extension_leveldb_path] =
browser_data_migrator_util::GetIndexedDBPaths(original_profile_dir_,
kMoveExtensionId);
EXPECT_TRUE(
base::PathExists(keep_extension_blob_path.Append(kDataFilePath)));
EXPECT_TRUE(
base::PathExists(keep_extension_leveldb_path.Append(kDataFilePath)));
EXPECT_TRUE(
base::PathExists(both_extension_blob_path.Append(kDataFilePath)));
EXPECT_TRUE(
base::PathExists(both_extension_leveldb_path.Append(kDataFilePath)));
EXPECT_FALSE(base::PathExists(move_extension_blob_path));
EXPECT_FALSE(base::PathExists(move_extension_leveldb_path));
}
// Lacros contains only IndexedDB folders of extensions not in keeplist.
{
const auto [keep_extension_blob_path, keep_extension_leveldb_path] =
browser_data_migrator_util::GetIndexedDBPaths(
new_profile_dir, keep_extension_id.c_str());
const auto [both_extension_blob_path, both_extension_leveldb_path] =
browser_data_migrator_util::GetIndexedDBPaths(
original_profile_dir_, both_extension_id.data());
const auto [move_extension_blob_path, move_extension_leveldb_path] =
browser_data_migrator_util::GetIndexedDBPaths(new_profile_dir,
kMoveExtensionId);
EXPECT_FALSE(base::PathExists(keep_extension_blob_path));
EXPECT_FALSE(base::PathExists(keep_extension_leveldb_path));
EXPECT_TRUE(
base::PathExists(both_extension_blob_path.Append(kDataFilePath)));
EXPECT_TRUE(
base::PathExists(both_extension_leveldb_path.Append(kDataFilePath)));
EXPECT_TRUE(
base::PathExists(move_extension_blob_path.Append(kDataFilePath)));
EXPECT_TRUE(
base::PathExists(move_extension_leveldb_path.Append(kDataFilePath)));
}
// Preferences.
const base::FilePath ash_preferences_path =
original_profile_dir_.Append(chrome::kPreferencesFilename);
const base::FilePath lacros_preferences_path =
new_profile_dir.Append(chrome::kPreferencesFilename);
EXPECT_TRUE(base::PathExists(ash_preferences_path));
EXPECT_TRUE(base::PathExists(lacros_preferences_path));
// Sync Data/LevelDB.
const base::FilePath ash_syncdata_leveldb_path =
original_profile_dir_
.Append(browser_data_migrator_util::kSyncDataFilePath)
.Append(browser_data_migrator_util::kSyncDataLeveldbName);
const base::FilePath lacros_syncdata_leveldb_path =
new_profile_dir.Append(browser_data_migrator_util::kSyncDataFilePath)
.Append(browser_data_migrator_util::kSyncDataLeveldbName);
EXPECT_TRUE(base::PathExists(ash_syncdata_leveldb_path));
EXPECT_TRUE(base::PathExists(lacros_syncdata_leveldb_path));
// Sync Data/Nigori.bin
EXPECT_FALSE(base::PathExists(GetNigoriPath(original_profile_dir_)));
EXPECT_TRUE(base::PathExists(GetNigoriPath(new_profile_dir)));
}
void TearDown() override { EXPECT_TRUE(scoped_temp_dir_.Delete()); }
base::test::TaskEnvironment task_environment_;
base::test::TestFuture<BrowserDataMigratorImpl::MigrationResult>
migrate_result_;
base::ScopedTempDir scoped_temp_dir_;
base::FilePath original_profile_dir_;
TestingPrefServiceSimple pref_service_;
std::string user_id_hash_;
std::unique_ptr<MoveMigrator> migrator_;
};
TEST_F(MoveMigratorMigrateTest, Migrate) {
migrator_->Migrate();
EXPECT_EQ(migrate_result_.Get().data_wipe_result,
BrowserDataMigratorImpl::DataWipeResult::kSucceeded);
EXPECT_EQ(migrate_result_.Get().data_migration_result.kind,
BrowserDataMigrator::ResultKind::kSucceeded);
CheckProfileDirFinalState();
}
TEST_F(MoveMigratorMigrateTest, MigrateResumeFromMoveLacrosItems) {
MoveMigrator::SetResumeStep(&pref_service_, user_id_hash_,
MoveMigrator::ResumeStep::kMoveLacrosItems);
// Setup `original_profile_dir_` as below.
// |- Cookies
// |- Downloads
// |- shared_proto_db
// |- move_migrator/First Run
// |- move_migrator/Default/
// |- Bookmarks
// |- Extensions
// |- IndexedDB
// |- Local Storage
// |- shared_proto_db
// |- Preferences
// |- Storage/
// |- Sync Data/
// |- LevelDB
// |- Nigori.bin
// |- move_migrator_split/
// |- Extensions
// |- IndexedDB
// |- Local Storage
// |- Preferences
// |- Storage/
// |- Sync Data/LevelDB
const base::FilePath tmp_user_dir =
original_profile_dir_.Append(browser_data_migrator_util::kMoveTmpDir);
const base::FilePath tmp_profile_dir =
tmp_user_dir.Append(browser_data_migrator_util::kLacrosProfilePath);
const base::FilePath tmp_split_dir =
original_profile_dir_.Append(browser_data_migrator_util::kSplitTmpDir);
ASSERT_TRUE(base::DeletePathRecursively(
original_profile_dir_.Append(kCacheFilePath)));
ASSERT_TRUE(base::CreateDirectory(tmp_user_dir));
ASSERT_TRUE(base::WriteFile(tmp_user_dir.Append(chrome::kFirstRunSentinel),
std::string_view()));
ASSERT_TRUE(base::CreateDirectory(tmp_profile_dir));
ASSERT_TRUE(base::CreateDirectory(tmp_split_dir));
ASSERT_TRUE(
base::CopyDirectory(original_profile_dir_.Append(kSharedProtoDBPath),
tmp_profile_dir.Append(kSharedProtoDBPath),
/*recursive=*/true));
ASSERT_TRUE(base::Move(original_profile_dir_.Append(kBookmarksFilePath),
tmp_profile_dir.Append(kBookmarksFilePath)));
ASSERT_TRUE(base::CreateDirectory(
tmp_profile_dir.Append(browser_data_migrator_util::kSyncDataFilePath)));
ASSERT_TRUE(base::Move(GetNigoriPath(original_profile_dir_),
GetNigoriPath(tmp_profile_dir)));
// Extensions that have to stay in both Ash and Lacros were copied to the
// split dir.
SetUpExtensions(tmp_split_dir, /*ash=*/false, /*lacros=*/false,
/*both=*/true);
// Extensions have been moved to Lacros's tmp dir.
ASSERT_TRUE(base::Move(
original_profile_dir_.Append(
browser_data_migrator_util::kExtensionsFilePath),
tmp_profile_dir.Append(browser_data_migrator_util::kExtensionsFilePath)));
// Storage objects that have to stay in both Ash and Lacros were copied to the
// split dir.
SetUpStorage(tmp_split_dir, /*ash=*/false, /*lacros=*/false,
/*both=*/true);
// Storage objects have been moved to Lacros's tmp dir.
ASSERT_TRUE(base::Move(
original_profile_dir_.Append(
browser_data_migrator_util::kStorageFilePath),
tmp_profile_dir.Append(browser_data_migrator_util::kStorageFilePath)));
// IndexedDB objects that have to stay in both Ash and Lacros were copied to
// the split dir.
SetUpIndexedDB(tmp_split_dir, /*ash=*/false, /*lacros=*/false, /*both=*/true);
// IndexedDB has been moved to Lacros's tmp dir.
ASSERT_TRUE(base::Move(
original_profile_dir_.Append(
browser_data_migrator_util::kIndexedDBFilePath),
tmp_profile_dir.Append(browser_data_migrator_util::kIndexedDBFilePath)));
// Local Storage has been split.
ASSERT_TRUE(
base::Move(original_profile_dir_.Append(
browser_data_migrator_util::kLocalStorageFilePath),
tmp_profile_dir.Append(
browser_data_migrator_util::kLocalStorageFilePath)));
SetUpLocalStorage(tmp_split_dir, /*ash_only=*/true);
// Preferences has been split.
SetUpPreferences(tmp_profile_dir, /*ash=*/false, /*lacros=*/true);
SetUpPreferences(tmp_split_dir, /*ash=*/true, /*lacros=*/false);
// `Sync Data`/LevelDB has been split.
SetUpSyncDataLevelDB(tmp_profile_dir, /*ash=*/false, /*lacros=*/true);
SetUpSyncDataLevelDB(tmp_split_dir, /*ash=*/true, /*lacros=*/false);
migrator_->Migrate();
EXPECT_EQ(migrate_result_.Get().data_wipe_result,
BrowserDataMigratorImpl::DataWipeResult::kSucceeded);
EXPECT_EQ(migrate_result_.Get().data_migration_result.kind,
BrowserDataMigrator::ResultKind::kSucceeded);
CheckProfileDirFinalState();
}
TEST_F(MoveMigratorMigrateTest, MigrateResumeFromMoveSplitItems) {
MoveMigrator::SetResumeStep(&pref_service_, user_id_hash_,
MoveMigrator::ResumeStep::kMoveSplitItems);
// Setup `original_profile_dir_` as below.
// |- Downloads
// |- shared_proto_db
// |- move_migrator/First Run
// |- move_migrator/Default/
// |- Bookmarks
// |- Cookies
// |- Extensions
// |- IndexedDB
// |- Local Storage
// |- shared_proto_db
// |- Preferences
// |- Storage/
// |- Sync Data/
// |- LevelDB
// |- Nigori.bin
// |- move_migrator_split/
// |- Extensions
// |- IndexedDB
// |- Local Storage
// |- Preferences
// |- Storage/
// |- Sync Data/LevelDB
const base::FilePath tmp_user_dir =
original_profile_dir_.Append(browser_data_migrator_util::kMoveTmpDir);
const base::FilePath tmp_profile_dir =
tmp_user_dir.Append(browser_data_migrator_util::kLacrosProfilePath);
const base::FilePath tmp_split_dir =
original_profile_dir_.Append(browser_data_migrator_util::kSplitTmpDir);
ASSERT_TRUE(base::DeletePathRecursively(
original_profile_dir_.Append(kCacheFilePath)));
ASSERT_TRUE(base::CreateDirectory(tmp_user_dir));
ASSERT_TRUE(base::WriteFile(tmp_user_dir.Append(chrome::kFirstRunSentinel),
std::string_view()));
ASSERT_TRUE(base::CreateDirectory(tmp_profile_dir));
ASSERT_TRUE(base::CreateDirectory(tmp_split_dir));
ASSERT_TRUE(
base::CopyDirectory(original_profile_dir_.Append(kSharedProtoDBPath),
tmp_profile_dir.Append(kSharedProtoDBPath),
/*recursive=*/true));
ASSERT_TRUE(base::Move(original_profile_dir_.Append(kBookmarksFilePath),
tmp_profile_dir.Append(kBookmarksFilePath)));
ASSERT_TRUE(base::Move(original_profile_dir_.Append(kCookiesFilePath),
tmp_profile_dir.Append(kCookiesFilePath)));
ASSERT_TRUE(base::CreateDirectory(
tmp_profile_dir.Append(browser_data_migrator_util::kSyncDataFilePath)));
ASSERT_TRUE(base::Move(GetNigoriPath(original_profile_dir_),
GetNigoriPath(tmp_profile_dir)));
// Extensions that have to stay in both Ash and Lacros were copied to the
// split dir.
SetUpExtensions(tmp_split_dir, /*ash=*/false, /*lacros=*/false,
/*both=*/true);
// Extensions have been moved to Lacros's tmp dir, but not yet split and moved
// to Ash profile dir.
ASSERT_TRUE(base::Move(
original_profile_dir_.Append(
browser_data_migrator_util::kExtensionsFilePath),
tmp_profile_dir.Append(browser_data_migrator_util::kExtensionsFilePath)));
// Storage objects that have to stay in both Ash and Lacros were copied to the
// split dir.
SetUpStorage(tmp_split_dir, /*ash=*/false, /*lacros=*/false,
/*both=*/true);
// Storage objects have been moved to Lacros's tmp dir, but not yet split and
// moved to Ash profile dir.
ASSERT_TRUE(base::Move(
original_profile_dir_.Append(
browser_data_migrator_util::kStorageFilePath),
tmp_profile_dir.Append(browser_data_migrator_util::kStorageFilePath)));
// IndexedDB objects that have to stay in both Ash and Lacros were copied to
// the split dir.
SetUpIndexedDB(tmp_split_dir, /*ash=*/false, /*lacros=*/false, /*both=*/true);
// IndexedDB objects have been moved to Lacros's tmp dir, but not yet split
// and moved to Ash profile dir.
ASSERT_TRUE(base::Move(
original_profile_dir_.Append(
browser_data_migrator_util::kIndexedDBFilePath),
tmp_profile_dir.Append(browser_data_migrator_util::kIndexedDBFilePath)));
// Local Storage has been split, but not yet moved to Ash profile dir.
ASSERT_TRUE(
base::Move(original_profile_dir_.Append(
browser_data_migrator_util::kLocalStorageFilePath),
tmp_profile_dir.Append(
browser_data_migrator_util::kLocalStorageFilePath)));
SetUpLocalStorage(tmp_split_dir, /*ash_only=*/true);
// Preferences has been split, but not yet moved to Ash profile dir.
SetUpPreferences(tmp_profile_dir, /*ash=*/false, /*lacros=*/true);
SetUpPreferences(tmp_split_dir, /*ash=*/true, /*lacros=*/false);
// `Sync Data`/LevelDB has been split, but not yet moved to Ash profile dir.
SetUpSyncDataLevelDB(tmp_profile_dir, /*ash=*/false, /*lacros=*/true);
SetUpSyncDataLevelDB(tmp_split_dir, /*ash=*/true, /*lacros=*/false);
migrator_->Migrate();
EXPECT_EQ(migrate_result_.Get().data_wipe_result,
BrowserDataMigratorImpl::DataWipeResult::kSucceeded);
EXPECT_EQ(migrate_result_.Get().data_migration_result.kind,
BrowserDataMigrator::ResultKind::kSucceeded);
CheckProfileDirFinalState();
}
TEST_F(MoveMigratorMigrateTest, MigrateResumeFromMoveTmpDir) {
MoveMigrator::SetResumeStep(&pref_service_, user_id_hash_,
MoveMigrator::ResumeStep::kMoveTmpDir);
// Setup `original_profile_dir_` as below.
// |- Downloads
// |- Extensions
// |- Local Storage
// |- shared_proto_db
// |- Preferences
// |- Storage/
// |- Sync Data/LevelDB
// |- move_migrator/First Run
// |- move_migrator/Default/
// |- Bookmarks
// |- Cookies
// |- Extensions
// |- Local Storage
// |- shared_proto_db
// |- Preferences
// |- Storage/
// |- Sync Data/
// |- LevelDB
// |- Nigori.bin
const base::FilePath tmp_user_dir =
original_profile_dir_.Append(browser_data_migrator_util::kMoveTmpDir);
const base::FilePath tmp_profile_dir =
tmp_user_dir.Append(browser_data_migrator_util::kLacrosProfilePath);
ASSERT_TRUE(base::DeletePathRecursively(
original_profile_dir_.Append(kCacheFilePath)));
ASSERT_TRUE(base::CreateDirectory(tmp_user_dir));
ASSERT_TRUE(base::WriteFile(tmp_user_dir.Append(chrome::kFirstRunSentinel),
std::string_view()));
ASSERT_TRUE(base::CreateDirectory(tmp_profile_dir));
ASSERT_TRUE(
base::CopyDirectory(original_profile_dir_.Append(kSharedProtoDBPath),
tmp_profile_dir.Append(kSharedProtoDBPath),
/*recursive=*/true));
ASSERT_TRUE(base::Move(original_profile_dir_.Append(kBookmarksFilePath),
tmp_profile_dir.Append(kBookmarksFilePath)));
ASSERT_TRUE(base::Move(original_profile_dir_.Append(kCookiesFilePath),
tmp_profile_dir.Append(kCookiesFilePath)));
ASSERT_TRUE(base::CreateDirectory(
tmp_profile_dir.Append(browser_data_migrator_util::kSyncDataFilePath)));
ASSERT_TRUE(base::Move(GetNigoriPath(original_profile_dir_),
GetNigoriPath(tmp_profile_dir)));
// Extensions have been split, and Ash's version is in its final place.
ASSERT_TRUE(base::DeletePathRecursively(original_profile_dir_.Append(
browser_data_migrator_util::kExtensionsFilePath)));
SetUpExtensions(tmp_profile_dir, /*ash=*/false, /*lacros=*/true);
SetUpExtensions(original_profile_dir_, /*ash=*/true, /*lacros=*/false);
// Storage objects have been split, and Ash's version is in its final place.
ASSERT_TRUE(base::DeletePathRecursively(original_profile_dir_.Append(
browser_data_migrator_util::kStorageFilePath)));
SetUpStorage(tmp_profile_dir, /*ash=*/false, /*lacros=*/true);
SetUpStorage(original_profile_dir_, /*ash=*/true, /*lacros=*/false);
// IndexedDB has been split, and Ash's version is in its final place.
ASSERT_TRUE(base::DeletePathRecursively(original_profile_dir_.Append(
browser_data_migrator_util::kIndexedDBFilePath)));
SetUpIndexedDB(tmp_profile_dir, /*ash=*/false, /*lacros=*/true);
SetUpIndexedDB(original_profile_dir_, /*ash=*/true, /*lacros=*/false);
// Local Storage has been split, and Ash's version is in its final place.
ASSERT_TRUE(
base::Move(original_profile_dir_.Append(
browser_data_migrator_util::kLocalStorageFilePath),
tmp_profile_dir.Append(
browser_data_migrator_util::kLocalStorageFilePath)));
SetUpLocalStorage(original_profile_dir_, /*ash_only=*/true);
// Preferences has been split.
SetUpPreferences(tmp_profile_dir, /*ash=*/false, /*lacros=*/true);
SetUpPreferences(original_profile_dir_, /*ash=*/true, /*lacros=*/false);
// `Sync Data`/LevelDB has been split.
SetUpSyncDataLevelDB(tmp_profile_dir, /*ash=*/false, /*lacros=*/true);
SetUpSyncDataLevelDB(original_profile_dir_, /*ash=*/true, /*lacros=*/false);
migrator_->Migrate();
EXPECT_EQ(migrate_result_.Get().data_wipe_result,
BrowserDataMigratorImpl::DataWipeResult::kSucceeded);
EXPECT_EQ(migrate_result_.Get().data_migration_result.kind,
BrowserDataMigrator::ResultKind::kSucceeded);
CheckProfileDirFinalState();
}
TEST_F(MoveMigratorMigrateTest, MigrateOutOfDisk) {
// Emulate the situation of out-of-disk.
browser_data_migrator_util::ScopedExtraBytesRequiredToBeFreedForTesting
scoped_extra_bytes(100);
migrator_->Migrate();
EXPECT_EQ(migrate_result_.Get().data_wipe_result,
BrowserDataMigratorImpl::DataWipeResult::kSucceeded);
EXPECT_EQ(migrate_result_.Get().data_migration_result.kind,
BrowserDataMigrator::ResultKind::kFailed);
EXPECT_EQ(100u, migrate_result_.Get().data_migration_result.required_size);
}
} // namespace ash