blob: 855161a00a2bdc427eba5985baccdab229540286 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/browser_sync/sync_client_utils.h"
#include <string>
#include <utility>
#include <vector>
#include "base/memory/raw_ptr.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_clock.h"
#include "base/test/task_environment.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/bookmarks/test/test_bookmark_client.h"
#include "components/password_manager/core/browser/password_store/test_password_store.h"
#include "components/reading_list/core/dual_reading_list_model.h"
#include "components/reading_list/core/fake_reading_list_model_storage.h"
#include "components/reading_list/core/reading_list_model_impl.h"
#include "components/sync/base/features.h"
#include "components/sync/service/local_data_description.h"
#include "components/sync/test/mock_model_type_change_processor.h"
#include "components/sync_bookmarks/bookmark_model_view.h"
#include "components/sync_bookmarks/bookmark_sync_service.h"
#include "components/undo/bookmark_undo_service.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace browser_sync {
namespace {
password_manager::PasswordForm CreateTestPassword(
const std::string& url,
password_manager::PasswordForm::Store store =
password_manager::PasswordForm::Store::kProfileStore,
base::Time last_used_time = base::Time::UnixEpoch()) {
password_manager::PasswordForm form;
form.signon_realm = url;
form.url = GURL(url);
form.username_value = u"test@gmail.com";
form.password_value = u"test";
form.in_store = store;
form.date_last_used = last_used_time;
return form;
}
syncer::LocalDataDescription CreateLocalDataDescription(
syncer::ModelType type,
int item_count,
const std::vector<std::string>& domains,
int domain_count) {
syncer::LocalDataDescription desc;
desc.type = type;
desc.item_count = item_count;
desc.domains = domains;
desc.domain_count = domain_count;
return desc;
}
class LocalDataQueryHelperTest : public testing::Test {
public:
LocalDataQueryHelperTest()
: local_bookmark_sync_service_(
&bookmark_undo_service_,
syncer::WipeModelUponSyncDisabledBehavior::kNever),
account_bookmark_sync_service_(
&bookmark_undo_service_,
syncer::WipeModelUponSyncDisabledBehavior::kNever) {
local_password_store_->Init(/*prefs=*/nullptr,
/*affiliated_match_helper=*/nullptr);
account_password_store_->Init(/*prefs=*/nullptr,
/*affiliated_match_helper=*/nullptr);
auto local_bookmark_client =
std::make_unique<bookmarks::TestBookmarkClient>();
managed_node_ = local_bookmark_client->EnableManagedNode();
bookmark_model_ = bookmarks::TestBookmarkClient::CreateModelWithClient(
std::move(local_bookmark_client));
bookmark_model_->CreateAccountPermanentFolders();
// Make sure BookmarkSyncService is aware of bookmarks having been loaded.
local_bookmark_sync_service_.DecodeBookmarkSyncMetadata(
/*metadata_str=*/"",
/*schedule_save_closure=*/base::DoNothing(),
std::make_unique<
sync_bookmarks::BookmarkModelViewUsingLocalOrSyncableNodes>(
bookmark_model_.get()));
account_bookmark_sync_service_.DecodeBookmarkSyncMetadata(
/*metadata_str=*/"",
/*schedule_save_closure=*/base::DoNothing(),
std::make_unique<sync_bookmarks::BookmarkModelViewUsingAccountNodes>(
bookmark_model_.get()));
// TODO(crbug.com/40065374): Simplify by wrapping into a helper.
auto local_reading_list_storage =
std::make_unique<FakeReadingListModelStorage>();
auto* local_reading_list_storage_ptr = local_reading_list_storage.get();
auto local_reading_list_model = std::make_unique<ReadingListModelImpl>(
std::move(local_reading_list_storage),
syncer::StorageType::kUnspecified,
syncer::WipeModelUponSyncDisabledBehavior::kNever, &clock_);
local_reading_list_model_ = local_reading_list_model.get();
auto account_reading_list_storage =
std::make_unique<FakeReadingListModelStorage>();
auto* account_reading_list_storage_ptr = account_reading_list_storage.get();
auto account_reading_list_model = ReadingListModelImpl::BuildNewForTest(
std::move(account_reading_list_storage), syncer::StorageType::kAccount,
syncer::WipeModelUponSyncDisabledBehavior::kAlways, &clock_,
processor_.CreateForwardingProcessor());
account_reading_list_model_ = account_reading_list_model.get();
dual_reading_list_model_ =
std::make_unique<reading_list::DualReadingListModel>(
std::move(local_reading_list_model),
std::move(account_reading_list_model));
local_reading_list_storage_ptr->TriggerLoadCompletion();
account_reading_list_storage_ptr->TriggerLoadCompletion();
local_data_query_helper_ =
std::make_unique<browser_sync::LocalDataQueryHelper>(
/*profile_password_store=*/local_password_store_.get(),
/*account_password_store=*/account_password_store_.get(),
/*local_bookmark_sync_service=*/&local_bookmark_sync_service_,
/*account_bookmark_sync_service=*/&account_bookmark_sync_service_,
/*dual_reading_list_model=*/dual_reading_list_model_.get());
// Make sure PasswordStore is fully initialized.
RunAllPendingTasks();
}
~LocalDataQueryHelperTest() override {
local_password_store_->ShutdownOnUIThread();
account_password_store_->ShutdownOnUIThread();
}
void RunAllPendingTasks() { task_environment_.RunUntilIdle(); }
protected:
base::test::ScopedFeatureList features_{
syncer::kEnableBookmarkFoldersForAccountStorage};
base::test::SingleThreadTaskEnvironment task_environment_;
base::SimpleTestClock clock_;
scoped_refptr<password_manager::TestPasswordStore> local_password_store_ =
base::MakeRefCounted<password_manager::TestPasswordStore>();
scoped_refptr<password_manager::TestPasswordStore> account_password_store_ =
base::MakeRefCounted<password_manager::TestPasswordStore>(
password_manager::IsAccountStore{true});
std::unique_ptr<bookmarks::BookmarkModel> bookmark_model_ =
bookmarks::TestBookmarkClient::CreateModel();
raw_ptr<bookmarks::BookmarkNode> managed_node_;
BookmarkUndoService bookmark_undo_service_; // Needed by BookmarkSyncService.
sync_bookmarks::BookmarkSyncService local_bookmark_sync_service_;
sync_bookmarks::BookmarkSyncService account_bookmark_sync_service_;
testing::NiceMock<syncer::MockModelTypeChangeProcessor> processor_;
std::unique_ptr<reading_list::DualReadingListModel> dual_reading_list_model_;
raw_ptr<ReadingListModel> local_reading_list_model_;
raw_ptr<ReadingListModel> account_reading_list_model_;
std::unique_ptr<LocalDataQueryHelper> local_data_query_helper_;
};
TEST_F(LocalDataQueryHelperTest, ShouldHandleZeroTypes) {
base::MockOnceCallback<void(
std::map<syncer::ModelType, syncer::LocalDataDescription>)>
callback;
EXPECT_CALL(callback, Run(::testing::IsEmpty()));
local_data_query_helper_->Run(syncer::ModelTypeSet(), callback.Get());
}
TEST_F(LocalDataQueryHelperTest, ShouldHandleUnusableTypes) {
base::HistogramTester histogram_tester;
base::MockOnceCallback<void(
std::map<syncer::ModelType, syncer::LocalDataDescription>)>
callback;
EXPECT_CALL(callback, Run(::testing::IsEmpty()));
ASSERT_TRUE(task_environment_.MainThreadIsIdle());
LocalDataQueryHelper helper(
/*profile_password_store=*/nullptr,
/*account_password_store=*/nullptr,
/*local_bookmark_sync_service=*/nullptr,
/*account_bookmark_sync_service=*/nullptr,
/*dual_reading_list_model=*/nullptr);
helper.Run(syncer::ModelTypeSet(
{syncer::PASSWORDS, syncer::BOOKMARKS, syncer::READING_LIST}),
callback.Get());
EXPECT_TRUE(task_environment_.MainThreadIsIdle());
}
TEST_F(LocalDataQueryHelperTest, ShouldReturnLocalPasswordsViaCallback) {
// Add test data to local store.
local_password_store_->AddLogin(CreateTestPassword("https://www.amazon.de"));
local_password_store_->AddLogin(
CreateTestPassword("https://www.facebook.com"));
RunAllPendingTasks();
base::MockOnceCallback<void(
std::map<syncer::ModelType, syncer::LocalDataDescription>)>
callback;
std::map<syncer::ModelType, syncer::LocalDataDescription> expected = {
{syncer::PASSWORDS,
CreateLocalDataDescription(syncer::PASSWORDS, 2,
{"amazon.de", "facebook.com"}, 2)}};
EXPECT_CALL(callback, Run(expected));
local_data_query_helper_->Run(syncer::ModelTypeSet({syncer::PASSWORDS}),
callback.Get());
RunAllPendingTasks();
}
TEST_F(LocalDataQueryHelperTest, ShouldReturnCountOfDistinctDomains) {
// Add test data to local store.
local_password_store_->AddLogin(CreateTestPassword("https://www.amazon.de"));
local_password_store_->AddLogin(
CreateTestPassword("https://www.facebook.com"));
// Another password with the same domain as an existing password.
local_password_store_->AddLogin(
CreateTestPassword("https://www.amazon.de/login"));
RunAllPendingTasks();
base::MockOnceCallback<void(
std::map<syncer::ModelType, syncer::LocalDataDescription>)>
callback;
std::map<syncer::ModelType, syncer::LocalDataDescription> expected = {
{syncer::PASSWORDS, CreateLocalDataDescription(
syncer::PASSWORDS,
// Total passwords = 3.
/*item_count=*/3, {"amazon.de", "facebook.com"},
// Total distinct domains = 2.
/*domain_count=*/2)}};
EXPECT_CALL(callback, Run(expected));
local_data_query_helper_->Run(syncer::ModelTypeSet({syncer::PASSWORDS}),
callback.Get());
RunAllPendingTasks();
}
TEST_F(LocalDataQueryHelperTest, ShouldHandleMultipleRequests) {
// Add test data to local store.
local_password_store_->AddLogin(CreateTestPassword("https://www.amazon.de"));
local_password_store_->AddLogin(
CreateTestPassword("https://www.facebook.com"));
RunAllPendingTasks();
base::MockOnceCallback<void(
std::map<syncer::ModelType, syncer::LocalDataDescription>)>
callback1;
base::MockOnceCallback<void(
std::map<syncer::ModelType, syncer::LocalDataDescription>)>
callback2;
std::map<syncer::ModelType, syncer::LocalDataDescription> expected = {
{syncer::PASSWORDS,
CreateLocalDataDescription(syncer::PASSWORDS, 2,
{"amazon.de", "facebook.com"}, 2)}};
// Request #1.
EXPECT_CALL(callback1, Run(expected));
local_data_query_helper_->Run(syncer::ModelTypeSet({syncer::PASSWORDS}),
callback1.Get());
// Request #2.
EXPECT_CALL(callback2, Run(expected));
local_data_query_helper_->Run(syncer::ModelTypeSet({syncer::PASSWORDS}),
callback2.Get());
RunAllPendingTasks();
}
TEST_F(LocalDataQueryHelperTest, ShouldReturnLocalBookmarksViaCallback) {
// -------- The local model --------
// bookmark_bar
// |- folder 1
// |- url1(https://www.amazon.de)
// |- url2(http://www.facebook.com)
// |- folder 2
// |- url3(http://www.amazon.de/faq)
const bookmarks::BookmarkNode* bookmark_bar_node =
bookmark_model_->bookmark_bar_node();
const bookmarks::BookmarkNode* folder1 =
bookmark_model_->AddFolder(bookmark_bar_node, /*index=*/0,
base::UTF8ToUTF16(std::string("folder1")));
const bookmarks::BookmarkNode* folder2 =
bookmark_model_->AddFolder(bookmark_bar_node, /*index=*/1,
base::UTF8ToUTF16(std::string("folder2")));
ASSERT_EQ(2u, bookmark_bar_node->children().size());
bookmark_model_->AddURL(folder1, /*index=*/0,
base::UTF8ToUTF16(std::string("url1")),
GURL("https://www.amazon.de"));
bookmark_model_->AddURL(folder1, /*index=*/1,
base::UTF8ToUTF16(std::string("url2")),
GURL("https://www.facebook.com"));
ASSERT_EQ(2u, folder1->children().size());
bookmark_model_->AddURL(folder2, /*index=*/0,
base::UTF8ToUTF16(std::string("url3")),
GURL("https://www.amazon.de/faq"));
ASSERT_EQ(1u, folder2->children().size());
base::MockOnceCallback<void(
std::map<syncer::ModelType, syncer::LocalDataDescription>)>
callback;
std::map<syncer::ModelType, syncer::LocalDataDescription> expected = {
{syncer::BOOKMARKS,
CreateLocalDataDescription(syncer::BOOKMARKS, 3,
{"amazon.de", "facebook.com"}, 2)}};
EXPECT_CALL(callback, Run(expected));
local_data_query_helper_->Run(syncer::ModelTypeSet({syncer::BOOKMARKS}),
callback.Get());
RunAllPendingTasks();
}
TEST_F(LocalDataQueryHelperTest, ShouldIgnoreManagedBookmarks) {
// -------- The local model --------
// bookmark_bar
// |- url1(https://www.amazon.de)
// managed_bookmarks
// |- url2(http://www.facebook.com)
const bookmarks::BookmarkNode* bookmark_bar_node =
bookmark_model_->bookmark_bar_node();
bookmark_model_->AddURL(bookmark_bar_node, /*index=*/0,
base::UTF8ToUTF16(std::string("url1")),
GURL("https://www.amazon.de"));
bookmark_model_->AddURL(managed_node_, /*index=*/0,
base::UTF8ToUTF16(std::string("url2")),
GURL("https://www.facebook.com"));
ASSERT_EQ(1u, bookmark_bar_node->children().size());
ASSERT_EQ(1u, managed_node_->children().size());
base::MockOnceCallback<void(
std::map<syncer::ModelType, syncer::LocalDataDescription>)>
callback;
std::map<syncer::ModelType, syncer::LocalDataDescription> expected = {
{syncer::BOOKMARKS,
CreateLocalDataDescription(syncer::BOOKMARKS, 1, {"amazon.de"}, 1)}};
EXPECT_CALL(callback, Run(expected));
local_data_query_helper_->Run(syncer::ModelTypeSet({syncer::BOOKMARKS}),
callback.Get());
RunAllPendingTasks();
}
TEST_F(LocalDataQueryHelperTest,
ShouldOnlyTriggerCallbackWhenAllTypesHaveReturned) {
// Add test data to local store.
local_password_store_->AddLogin(CreateTestPassword("https://www.amazon.de"));
bookmark_model_->AddURL(bookmark_model_->bookmark_bar_node(), /*index=*/0,
base::UTF8ToUTF16(std::string("url1")),
GURL("https://www.facebook.com"));
RunAllPendingTasks();
base::MockOnceCallback<void(
std::map<syncer::ModelType, syncer::LocalDataDescription>)>
callback;
std::map<syncer::ModelType, syncer::LocalDataDescription> expected = {
{syncer::PASSWORDS,
CreateLocalDataDescription(syncer::PASSWORDS, 1, {"amazon.de"}, 1)},
{syncer::BOOKMARKS,
CreateLocalDataDescription(syncer::BOOKMARKS, 1, {"facebook.com"}, 1)},
};
EXPECT_CALL(callback, Run(expected));
local_data_query_helper_->Run(
syncer::ModelTypeSet({syncer::PASSWORDS, syncer::BOOKMARKS}),
callback.Get());
RunAllPendingTasks();
}
TEST_F(LocalDataQueryHelperTest,
ShouldHandleMultipleRequestsForDifferentTypes) {
// Add test data to local store.
local_password_store_->AddLogin(CreateTestPassword("https://www.amazon.de"));
bookmark_model_->AddURL(bookmark_model_->bookmark_bar_node(), /*index=*/0,
base::UTF8ToUTF16(std::string("url1")),
GURL("https://www.facebook.com"));
RunAllPendingTasks();
// Request #1.
base::MockOnceCallback<void(
std::map<syncer::ModelType, syncer::LocalDataDescription>)>
callback1;
std::map<syncer::ModelType, syncer::LocalDataDescription> expected1 = {
{syncer::PASSWORDS,
CreateLocalDataDescription(syncer::PASSWORDS, 1, {"amazon.de"}, 1)},
};
EXPECT_CALL(callback1, Run(expected1));
// Request #2.
base::MockOnceCallback<void(
std::map<syncer::ModelType, syncer::LocalDataDescription>)>
callback2;
std::map<syncer::ModelType, syncer::LocalDataDescription> expected2 = {
{syncer::BOOKMARKS,
CreateLocalDataDescription(syncer::BOOKMARKS, 1, {"facebook.com"}, 1)},
};
EXPECT_CALL(callback2, Run(expected2));
local_data_query_helper_->Run(syncer::ModelTypeSet({syncer::PASSWORDS}),
callback1.Get());
local_data_query_helper_->Run(syncer::ModelTypeSet({syncer::BOOKMARKS}),
callback2.Get());
RunAllPendingTasks();
}
TEST_F(LocalDataQueryHelperTest, ShouldReturnLocalReadingListViaCallback) {
// Add test data to local model.
local_reading_list_model_->AddOrReplaceEntry(
GURL("https://www.amazon.de"), "url1",
reading_list::ADDED_VIA_CURRENT_APP,
/*estimated_read_time=*/base::TimeDelta());
local_reading_list_model_->AddOrReplaceEntry(
GURL("https://www.facebook.com"), "url2",
reading_list::ADDED_VIA_CURRENT_APP,
/*estimated_read_time=*/base::TimeDelta());
local_reading_list_model_->AddOrReplaceEntry(
GURL("https://www.amazon.de/faq"), "url3",
reading_list::ADDED_VIA_CURRENT_APP,
/*estimated_read_time=*/base::TimeDelta());
ASSERT_TRUE(local_reading_list_model_->loaded());
base::MockOnceCallback<void(
std::map<syncer::ModelType, syncer::LocalDataDescription>)>
callback;
std::map<syncer::ModelType, syncer::LocalDataDescription> expected = {
{syncer::READING_LIST,
CreateLocalDataDescription(syncer::READING_LIST, 3,
{"amazon.de", "facebook.com"}, 2)}};
EXPECT_CALL(callback, Run(expected));
local_data_query_helper_->Run(syncer::ModelTypeSet({syncer::READING_LIST}),
callback.Get());
RunAllPendingTasks();
}
TEST_F(LocalDataQueryHelperTest, ShouldWorkForUrlsWithNoTLD) {
// Add test data to local store.
local_password_store_->AddLogin(CreateTestPassword("chrome://flags"));
local_password_store_->AddLogin(CreateTestPassword("https://test"));
RunAllPendingTasks();
base::MockOnceCallback<void(
std::map<syncer::ModelType, syncer::LocalDataDescription>)>
callback;
std::map<syncer::ModelType, syncer::LocalDataDescription> expected = {
{syncer::PASSWORDS,
CreateLocalDataDescription(syncer::PASSWORDS, 2,
{"chrome://flags", "test"}, 2)}};
EXPECT_CALL(callback, Run(expected));
local_data_query_helper_->Run(syncer::ModelTypeSet({syncer::PASSWORDS}),
callback.Get());
RunAllPendingTasks();
}
class LocalDataMigrationHelperTest : public testing::Test {
public:
LocalDataMigrationHelperTest()
: local_bookmark_sync_service_(
&bookmark_undo_service_,
syncer::WipeModelUponSyncDisabledBehavior::kNever),
account_bookmark_sync_service_(
&bookmark_undo_service_,
syncer::WipeModelUponSyncDisabledBehavior::kNever) {
local_password_store_->Init(/*prefs=*/nullptr,
/*affiliated_match_helper=*/nullptr);
account_password_store_->Init(/*prefs=*/nullptr,
/*affiliated_match_helper=*/nullptr);
auto local_bookmark_client =
std::make_unique<bookmarks::TestBookmarkClient>();
managed_node_ = local_bookmark_client->EnableManagedNode();
bookmark_model_ = bookmarks::TestBookmarkClient::CreateModelWithClient(
std::move(local_bookmark_client));
bookmark_model_->CreateAccountPermanentFolders();
// Make sure BookmarkSyncService is aware of bookmarks having been loaded.
local_bookmark_sync_service_.DecodeBookmarkSyncMetadata(
/*metadata_str=*/"",
/*schedule_save_closure=*/base::DoNothing(),
std::make_unique<
sync_bookmarks::BookmarkModelViewUsingLocalOrSyncableNodes>(
bookmark_model_.get()));
account_bookmark_sync_service_.DecodeBookmarkSyncMetadata(
/*metadata_str=*/"",
/*schedule_save_closure=*/base::DoNothing(),
std::make_unique<sync_bookmarks::BookmarkModelViewUsingAccountNodes>(
bookmark_model_.get()));
// TODO(crbug.com/40065374): Simplify by wrapping into a helper.
auto local_reading_list_storage =
std::make_unique<FakeReadingListModelStorage>();
auto* local_reading_list_storage_ptr = local_reading_list_storage.get();
auto local_reading_list_model = std::make_unique<ReadingListModelImpl>(
std::move(local_reading_list_storage),
syncer::StorageType::kUnspecified,
syncer::WipeModelUponSyncDisabledBehavior::kNever, &clock_);
local_reading_list_model_ = local_reading_list_model.get();
auto account_reading_list_storage =
std::make_unique<FakeReadingListModelStorage>();
auto* account_reading_list_storage_ptr = account_reading_list_storage.get();
auto account_reading_list_model = ReadingListModelImpl::BuildNewForTest(
std::move(account_reading_list_storage), syncer::StorageType::kAccount,
syncer::WipeModelUponSyncDisabledBehavior::kAlways, &clock_,
processor_.CreateForwardingProcessor());
account_reading_list_model_ = account_reading_list_model.get();
dual_reading_list_model_ =
std::make_unique<reading_list::DualReadingListModel>(
std::move(local_reading_list_model),
std::move(account_reading_list_model));
local_reading_list_storage_ptr->TriggerLoadCompletion();
account_reading_list_storage_ptr->TriggerLoadCompletion();
local_data_migration_helper_ =
std::make_unique<browser_sync::LocalDataMigrationHelper>(
/*profile_password_store=*/local_password_store_.get(),
/*account_password_store=*/account_password_store_.get(),
/*local_bookmark_sync_service=*/&local_bookmark_sync_service_,
/*account_bookmark_sync_service=*/&account_bookmark_sync_service_,
/*dual_reading_list_model=*/dual_reading_list_model_.get());
// Make sure PasswordStore is fully initialized.
RunAllPendingTasks();
}
~LocalDataMigrationHelperTest() override {
local_password_store_->ShutdownOnUIThread();
account_password_store_->ShutdownOnUIThread();
}
void RunAllPendingTasks() { task_environment_.RunUntilIdle(); }
protected:
base::test::ScopedFeatureList features_{
syncer::kEnableBookmarkFoldersForAccountStorage};
base::test::SingleThreadTaskEnvironment task_environment_;
base::SimpleTestClock clock_;
scoped_refptr<password_manager::TestPasswordStore> local_password_store_ =
base::MakeRefCounted<password_manager::TestPasswordStore>();
scoped_refptr<password_manager::TestPasswordStore> account_password_store_ =
base::MakeRefCounted<password_manager::TestPasswordStore>(
password_manager::IsAccountStore{true});
std::unique_ptr<bookmarks::BookmarkModel> bookmark_model_;
raw_ptr<bookmarks::BookmarkNode> managed_node_;
BookmarkUndoService bookmark_undo_service_; // Needed by BookmarkSyncService.
sync_bookmarks::BookmarkSyncService local_bookmark_sync_service_;
sync_bookmarks::BookmarkSyncService account_bookmark_sync_service_;
testing::NiceMock<syncer::MockModelTypeChangeProcessor> processor_;
std::unique_ptr<reading_list::DualReadingListModel> dual_reading_list_model_;
raw_ptr<ReadingListModel> local_reading_list_model_;
raw_ptr<ReadingListModel> account_reading_list_model_;
std::unique_ptr<LocalDataMigrationHelper> local_data_migration_helper_;
};
TEST_F(LocalDataMigrationHelperTest, ShouldLogRequestsToHistogram) {
{
base::HistogramTester histogram_tester;
local_data_migration_helper_->Run(syncer::ModelTypeSet());
// Nothing logged to histogram.
histogram_tester.ExpectTotalCount("Sync.BatchUpload.Requests2", 0);
}
{
base::HistogramTester histogram_tester;
local_data_migration_helper_->Run(
syncer::ModelTypeSet({syncer::PASSWORDS}));
histogram_tester.ExpectUniqueSample(
"Sync.BatchUpload.Requests2",
syncer::ModelTypeForHistograms::kPasswords, 1);
}
{
base::HistogramTester histogram_tester;
// Required by MarkAllForUploadToSyncServerIfNeeded() to work.
ON_CALL(processor_, IsTrackingMetadata)
.WillByDefault(::testing::Return(true));
local_data_migration_helper_->Run(syncer::ModelTypeSet(
{syncer::PASSWORDS, syncer::BOOKMARKS, syncer::READING_LIST}));
histogram_tester.ExpectTotalCount("Sync.BatchUpload.Requests2", 3);
histogram_tester.ExpectBucketCount(
"Sync.BatchUpload.Requests2",
syncer::ModelTypeForHistograms::kPasswords, 1);
histogram_tester.ExpectBucketCount(
"Sync.BatchUpload.Requests2",
syncer::ModelTypeForHistograms::kBookmarks, 1);
histogram_tester.ExpectBucketCount(
"Sync.BatchUpload.Requests2",
syncer::ModelTypeForHistograms::kReadingList, 1);
}
}
TEST_F(LocalDataMigrationHelperTest,
ShouldNotLogUnsupportedDataTypesRequestToHistogram) {
base::HistogramTester histogram_tester;
local_data_migration_helper_->Run(
syncer::ModelTypeSet({syncer::PASSWORDS, syncer::DEVICE_INFO}));
// Only the request for PASSWORDS is logged.
histogram_tester.ExpectUniqueSample(
"Sync.BatchUpload.Requests2", syncer::ModelTypeForHistograms::kPasswords,
1);
}
TEST_F(LocalDataMigrationHelperTest, ShouldHandleZeroTypes) {
// Just checks that there's no crash.
local_data_migration_helper_->Run(syncer::ModelTypeSet());
}
TEST_F(LocalDataMigrationHelperTest, ShouldHandleUnusableTypes) {
base::HistogramTester histogram_tester;
ASSERT_TRUE(task_environment_.MainThreadIsIdle());
LocalDataMigrationHelper helper(
/*profile_password_store=*/nullptr,
/*account_password_store=*/nullptr,
/*local_bookmark_model=*/nullptr,
/*account_bookmark_model=*/nullptr,
/*dual_reading_list_model=*/nullptr);
helper.Run(syncer::ModelTypeSet(
{syncer::PASSWORDS, syncer::BOOKMARKS, syncer::READING_LIST}));
EXPECT_TRUE(task_environment_.MainThreadIsIdle());
}
TEST_F(LocalDataMigrationHelperTest, ShouldMovePasswordsToAccountStore) {
// Add test data to local store.
auto form1 = CreateTestPassword("https://www.amazon.de");
auto form2 = CreateTestPassword("https://www.facebook.com");
local_password_store_->AddLogin(form1);
local_password_store_->AddLogin(form2);
RunAllPendingTasks();
ASSERT_EQ(
local_password_store_->stored_passwords(),
password_manager::TestPasswordStore::PasswordMap(
{{form1.signon_realm, {form1}}, {form2.signon_realm, {form2}}}));
base::HistogramTester histogram_tester;
local_data_migration_helper_->Run(syncer::ModelTypeSet({syncer::PASSWORDS}));
RunAllPendingTasks();
EXPECT_EQ(2, histogram_tester.GetTotalSum("Sync.PasswordsBatchUpload.Count"));
// Passwords have been moved to the account store.
form1.in_store = password_manager::PasswordForm::Store::kAccountStore;
form2.in_store = password_manager::PasswordForm::Store::kAccountStore;
EXPECT_EQ(
account_password_store_->stored_passwords(),
password_manager::TestPasswordStore::PasswordMap(
{{form1.signon_realm, {form1}}, {form2.signon_realm, {form2}}}));
// Local password store is empty.
EXPECT_TRUE(local_password_store_->IsEmpty());
}
TEST_F(LocalDataMigrationHelperTest, ShouldNotUploadSamePassword) {
// Add test password to local store.
auto local_form = CreateTestPassword("https://www.amazon.de");
local_form.times_used_in_html_form = 10;
local_password_store_->AddLogin(local_form);
// Add the same password to the account store, with slight different
// non-identifying details.
auto account_form = local_form;
account_form.in_store = password_manager::PasswordForm::Store::kAccountStore;
account_form.times_used_in_html_form = 5;
account_password_store_->AddLogin(account_form);
RunAllPendingTasks();
ASSERT_EQ(local_password_store_->stored_passwords(),
password_manager::TestPasswordStore::PasswordMap(
{{local_form.signon_realm, {local_form}}}));
ASSERT_EQ(account_password_store_->stored_passwords(),
password_manager::TestPasswordStore::PasswordMap(
{{account_form.signon_realm, {account_form}}}));
base::HistogramTester histogram_tester;
local_data_migration_helper_->Run(syncer::ModelTypeSet({syncer::PASSWORDS}));
RunAllPendingTasks();
EXPECT_EQ(0, histogram_tester.GetTotalSum("Sync.PasswordsBatchUpload.Count"));
// No new password is added to the account store.
EXPECT_EQ(account_password_store_->stored_passwords(),
password_manager::TestPasswordStore::PasswordMap(
{{account_form.signon_realm, {account_form}}}));
// The password is removed from the local store.
EXPECT_TRUE(local_password_store_->IsEmpty());
}
TEST_F(LocalDataMigrationHelperTest,
ShouldUploadConflictingPasswordIfMoreRecentlyUsed) {
// Add test password to local store, with last used time set to (time for
// epoch in Unix + 1 second).
auto local_form =
CreateTestPassword("https://www.amazon.de",
password_manager::PasswordForm::Store::kProfileStore,
base::Time::UnixEpoch() + base::Seconds(1));
local_form.password_value = u"local_value";
local_password_store_->AddLogin(local_form);
// Add same credential with a different password to the account store, with
// last used time set to time for epoch in Unix.
auto account_form =
CreateTestPassword("https://www.amazon.de",
password_manager::PasswordForm::Store::kAccountStore,
base::Time::UnixEpoch());
account_form.password_value = u"account_value";
account_password_store_->AddLogin(account_form);
RunAllPendingTasks();
ASSERT_EQ(local_password_store_->stored_passwords(),
password_manager::TestPasswordStore::PasswordMap(
{{local_form.signon_realm, {local_form}}}));
ASSERT_EQ(account_password_store_->stored_passwords(),
password_manager::TestPasswordStore::PasswordMap(
{{account_form.signon_realm, {account_form}}}));
base::HistogramTester histogram_tester;
local_data_migration_helper_->Run(syncer::ModelTypeSet({syncer::PASSWORDS}));
RunAllPendingTasks();
EXPECT_EQ(1, histogram_tester.GetTotalSum("Sync.PasswordsBatchUpload.Count"));
// Since local password has a more recent last used date, it is moved to the
// account store.
local_form.in_store = password_manager::PasswordForm::Store::kAccountStore;
EXPECT_EQ(account_password_store_->stored_passwords(),
password_manager::TestPasswordStore::PasswordMap(
{{local_form.signon_realm, {local_form}}}));
// Local password store is now empty.
EXPECT_TRUE(local_password_store_->IsEmpty());
}
TEST_F(LocalDataMigrationHelperTest,
ShouldNotUploadConflictingPasswordIfLessRecentlyUsed) {
// Add test password to local store, with last used time set to time for epoch
// in Unix.
auto local_form =
CreateTestPassword("https://www.amazon.de",
password_manager::PasswordForm::Store::kProfileStore,
base::Time::UnixEpoch());
local_form.password_value = u"local_value";
local_password_store_->AddLogin(local_form);
// Add same credential with a different password to the account store, with
// last used time set to (time for epoch in Unix + 1 second).
auto account_form =
CreateTestPassword("https://www.amazon.de",
password_manager::PasswordForm::Store::kAccountStore,
base::Time::UnixEpoch() + base::Seconds(1));
account_form.password_value = u"account_value";
account_password_store_->AddLogin(account_form);
RunAllPendingTasks();
ASSERT_EQ(local_password_store_->stored_passwords(),
password_manager::TestPasswordStore::PasswordMap(
{{local_form.signon_realm, {local_form}}}));
ASSERT_EQ(account_password_store_->stored_passwords(),
password_manager::TestPasswordStore::PasswordMap(
{{account_form.signon_realm, {account_form}}}));
base::HistogramTester histogram_tester;
local_data_migration_helper_->Run(syncer::ModelTypeSet({syncer::PASSWORDS}));
RunAllPendingTasks();
EXPECT_EQ(0, histogram_tester.GetTotalSum("Sync.PasswordsBatchUpload.Count"));
// Since account password has a more recent last used date, it wins over the
// local password.
EXPECT_EQ(account_password_store_->stored_passwords(),
password_manager::TestPasswordStore::PasswordMap(
{{local_form.signon_realm, {account_form}}}));
// Local password is removed from the local store.
EXPECT_TRUE(local_password_store_->IsEmpty());
}
TEST_F(LocalDataMigrationHelperTest,
ShouldUploadConflictingPasswordIfMoreRecentlyUpdated) {
// Add test password to local store, with last updated time set to (time for
// epoch in Linux + 1 second).
auto local_form =
CreateTestPassword("https://www.amazon.de",
password_manager::PasswordForm::Store::kProfileStore);
local_form.password_value = u"local_value";
local_form.date_password_modified =
base::Time::UnixEpoch() + base::Seconds(1);
local_password_store_->AddLogin(local_form);
// Add same credential with a different password to the account store, with
// last updated time set to time for epoch in Unix.
auto account_form =
CreateTestPassword("https://www.amazon.de",
password_manager::PasswordForm::Store::kAccountStore);
account_form.password_value = u"account_value";
account_form.date_password_modified = base::Time::UnixEpoch();
account_password_store_->AddLogin(account_form);
RunAllPendingTasks();
ASSERT_EQ(local_password_store_->stored_passwords(),
password_manager::TestPasswordStore::PasswordMap(
{{local_form.signon_realm, {local_form}}}));
ASSERT_EQ(account_password_store_->stored_passwords(),
password_manager::TestPasswordStore::PasswordMap(
{{account_form.signon_realm, {account_form}}}));
local_data_migration_helper_->Run(syncer::ModelTypeSet({syncer::PASSWORDS}));
RunAllPendingTasks();
// Since local password has a more recent last modified date, it is moved to
// the account store.
local_form.in_store = password_manager::PasswordForm::Store::kAccountStore;
EXPECT_EQ(account_password_store_->stored_passwords(),
password_manager::TestPasswordStore::PasswordMap(
{{local_form.signon_realm, {local_form}}}));
// Local password store is now empty.
EXPECT_TRUE(local_password_store_->IsEmpty());
}
TEST_F(LocalDataMigrationHelperTest,
ShouldNotUploadConflictingPasswordIfLessRecentlyUpdated) {
// Add test password to local store, with last updated time set to time for
// epoch in Unix.
auto local_form =
CreateTestPassword("https://www.amazon.de",
password_manager::PasswordForm::Store::kProfileStore);
local_form.password_value = u"local_value";
local_form.date_password_modified = base::Time::UnixEpoch();
local_password_store_->AddLogin(local_form);
// Add same credential with a different password to the account store, with
// last updated time set to (time for epoch in Unix + 1 second).
auto account_form =
CreateTestPassword("https://www.amazon.de",
password_manager::PasswordForm::Store::kAccountStore);
account_form.password_value = u"account_value";
account_form.date_password_modified =
base::Time::UnixEpoch() + base::Seconds(1);
account_password_store_->AddLogin(account_form);
RunAllPendingTasks();
ASSERT_EQ(local_password_store_->stored_passwords(),
password_manager::TestPasswordStore::PasswordMap(
{{local_form.signon_realm, {local_form}}}));
ASSERT_EQ(account_password_store_->stored_passwords(),
password_manager::TestPasswordStore::PasswordMap(
{{account_form.signon_realm, {account_form}}}));
local_data_migration_helper_->Run(syncer::ModelTypeSet({syncer::PASSWORDS}));
RunAllPendingTasks();
// Since account password has a more recent last modified date, it wins over
// the local password.
EXPECT_EQ(account_password_store_->stored_passwords(),
password_manager::TestPasswordStore::PasswordMap(
{{local_form.signon_realm, {account_form}}}));
// Local password is removed from the local store.
EXPECT_TRUE(local_password_store_->IsEmpty());
}
TEST_F(LocalDataMigrationHelperTest,
ShouldUploadConflictingPasswordIfMoreRecentlyCreated) {
// Add test password to local store, with creation time set to (time for
// epoch in Unix + 1 second).
auto local_form =
CreateTestPassword("https://www.amazon.de",
password_manager::PasswordForm::Store::kProfileStore);
local_form.password_value = u"local_value";
local_form.date_created = base::Time::UnixEpoch() + base::Seconds(1);
local_password_store_->AddLogin(local_form);
// Add same credential with a different password to the account store, with
// creation time set to time for epoch in Unix.
auto account_form =
CreateTestPassword("https://www.amazon.de",
password_manager::PasswordForm::Store::kAccountStore);
account_form.password_value = u"account_value";
account_form.date_created = base::Time::UnixEpoch();
account_password_store_->AddLogin(account_form);
RunAllPendingTasks();
ASSERT_EQ(local_password_store_->stored_passwords(),
password_manager::TestPasswordStore::PasswordMap(
{{local_form.signon_realm, {local_form}}}));
ASSERT_EQ(account_password_store_->stored_passwords(),
password_manager::TestPasswordStore::PasswordMap(
{{account_form.signon_realm, {account_form}}}));
local_data_migration_helper_->Run(syncer::ModelTypeSet({syncer::PASSWORDS}));
RunAllPendingTasks();
// Since local password has a more recent creation time, it is moved to the
// account store.
local_form.in_store = password_manager::PasswordForm::Store::kAccountStore;
EXPECT_EQ(account_password_store_->stored_passwords(),
password_manager::TestPasswordStore::PasswordMap(
{{local_form.signon_realm, {local_form}}}));
// Local password store is now empty.
EXPECT_TRUE(local_password_store_->IsEmpty());
}
TEST_F(LocalDataMigrationHelperTest,
ShouldNotUploadConflictingPasswordIfLessRecentlyCreated) {
// Add test password to local store, with creation time set to time for epoch
// in Unix.
auto local_form =
CreateTestPassword("https://www.amazon.de",
password_manager::PasswordForm::Store::kProfileStore);
local_form.password_value = u"local_value";
local_form.date_created = base::Time::UnixEpoch();
local_password_store_->AddLogin(local_form);
// Add same credential with a different password to the account store, with
// creation time set to (time for epoch in Unix + 1 second).
auto account_form =
CreateTestPassword("https://www.amazon.de",
password_manager::PasswordForm::Store::kAccountStore);
account_form.password_value = u"account_value";
account_form.date_created = base::Time::UnixEpoch() + base::Seconds(1);
account_password_store_->AddLogin(account_form);
RunAllPendingTasks();
ASSERT_EQ(local_password_store_->stored_passwords(),
password_manager::TestPasswordStore::PasswordMap(
{{local_form.signon_realm, {local_form}}}));
ASSERT_EQ(account_password_store_->stored_passwords(),
password_manager::TestPasswordStore::PasswordMap(
{{account_form.signon_realm, {account_form}}}));
local_data_migration_helper_->Run(syncer::ModelTypeSet({syncer::PASSWORDS}));
RunAllPendingTasks();
// Since account password has a more recent creation time, it wins over the
// local password.
EXPECT_EQ(account_password_store_->stored_passwords(),
password_manager::TestPasswordStore::PasswordMap(
{{local_form.signon_realm, {account_form}}}));
// Local password is removed from the local store.
EXPECT_TRUE(local_password_store_->IsEmpty());
}
TEST_F(LocalDataMigrationHelperTest, ShouldMoveBookmarksToAccountStore) {
// -------- The local bookmarks --------
// bookmark_bar
// |- url1(https://www.amazon.de)
bookmark_model_->AddURL(bookmark_model_->bookmark_bar_node(), /*index=*/0,
base::UTF8ToUTF16(std::string("url1")),
GURL("https://www.amazon.de"));
// -------- The account bookmarks --------
// account_bookmark_bar
// |- url2(http://www.google.com)
bookmark_model_->AddURL(bookmark_model_->account_bookmark_bar_node(),
/*index=*/0, base::UTF8ToUTF16(std::string("url2")),
GURL("https://www.google.com"));
local_data_migration_helper_->Run(syncer::ModelTypeSet({syncer::BOOKMARKS}));
// -------- The expected merge outcome --------
// account_bookmark_bar
// |- url1(https://www.amazon.de)
// |- url2(http://www.google.com)
EXPECT_EQ(2u,
bookmark_model_->account_bookmark_bar_node()->children().size());
EXPECT_EQ(0u, bookmark_model_->bookmark_bar_node()->children().size());
}
TEST_F(LocalDataMigrationHelperTest, ShouldClearBookmarksFromLocalStore) {
// -------- The local bookmarks --------
// bookmark_bar
// |- url1(https://www.google.com)
bookmark_model_->AddURL(bookmark_model_->bookmark_bar_node(), /*index=*/0,
base::UTF8ToUTF16(std::string("url1")),
GURL("https://www.google.com"));
// -------- The account bookmarks --------
// account_bookmark_bar
// |- url1(https://www.google.com)
bookmark_model_->AddURL(bookmark_model_->account_bookmark_bar_node(),
/*index=*/0, base::UTF8ToUTF16(std::string("url1")),
GURL("https://www.google.com"));
local_data_migration_helper_->Run(syncer::ModelTypeSet({syncer::BOOKMARKS}));
// No actual move happens since the data already exists in the account store.
// -------- The expected merge outcome --------
// account_bookmark_bar
// |- url1(https://www.google.com)
EXPECT_EQ(1u,
bookmark_model_->account_bookmark_bar_node()->children().size());
// The data is still cleared from the local store.
EXPECT_EQ(0u, bookmark_model_->bookmark_bar_node()->children().size());
}
TEST_F(LocalDataMigrationHelperTest,
ShouldDoNothingIfAccountBookmarksDontExist) {
bookmark_model_->RemoveAccountPermanentFolders();
// -------- The local bookmarks --------
// bookmark_bar
// |- url1(https://www.amazon.de)
bookmark_model_->AddURL(bookmark_model_->bookmark_bar_node(), /*index=*/0,
base::UTF8ToUTF16(std::string("url1")),
GURL("https://www.amazon.de"));
// -------- The account bookmarks don't exist --------
ASSERT_EQ(nullptr, bookmark_model_->account_bookmark_bar_node());
local_data_migration_helper_->Run(syncer::ModelTypeSet({syncer::BOOKMARKS}));
// -------- The expected merge outcome --------
// bookmark_bar
// |- url1(https://www.amazon.de)
EXPECT_EQ(1u, bookmark_model_->bookmark_bar_node()->children().size());
EXPECT_EQ(nullptr, bookmark_model_->account_bookmark_bar_node());
}
TEST_F(LocalDataMigrationHelperTest, ShouldIgnoreManagedBookmarks) {
// -------- The local bookmarks --------
// bookmark_bar
// |- url1(https://www.amazon.de)
// managed_bookmarks
// |- url2(http://www.facebook.com)
const bookmarks::BookmarkNode* bookmark_bar_node =
bookmark_model_->bookmark_bar_node();
bookmark_model_->AddURL(bookmark_bar_node, /*index=*/0,
base::UTF8ToUTF16(std::string("url1")),
GURL("https://www.amazon.de"));
bookmark_model_->AddURL(managed_node_, /*index=*/0,
base::UTF8ToUTF16(std::string("url2")),
GURL("https://www.facebook.com"));
// The account bookmark bar is empty.
EXPECT_EQ(0u,
bookmark_model_->account_bookmark_bar_node()->children().size());
local_data_migration_helper_->Run(syncer::ModelTypeSet({syncer::BOOKMARKS}));
// -------- The expected merge outcome --------
// managed_bookmarks
// |- url2(http://www.facebook.com)
// account_bookmark_bar
// |- url1(https://www.amazon.de)
EXPECT_EQ(1u,
bookmark_model_->account_bookmark_bar_node()->children().size());
EXPECT_EQ(1u, managed_node_->children().size());
EXPECT_EQ(0u, bookmark_model_->bookmark_bar_node()->children().size());
}
TEST_F(LocalDataMigrationHelperTest,
ShouldHandleMultipleRequestsForDifferentTypes) {
// Add test data to local store.
auto form = CreateTestPassword("https://amazon.de");
local_password_store_->AddLogin(form);
bookmark_model_->AddURL(bookmark_model_->bookmark_bar_node(), /*index=*/0,
base::UTF8ToUTF16(std::string("url1")),
GURL("https://www.facebook.com"));
RunAllPendingTasks();
// Account stores/models are empty.
ASSERT_TRUE(account_password_store_->IsEmpty());
ASSERT_EQ(0u,
bookmark_model_->account_bookmark_bar_node()->children().size());
// Request #1.
local_data_migration_helper_->Run(syncer::ModelTypeSet({syncer::PASSWORDS}));
// Request #2.
local_data_migration_helper_->Run(syncer::ModelTypeSet({syncer::BOOKMARKS}));
RunAllPendingTasks();
// The local data has been moved to the account store/model.
form.in_store = password_manager::PasswordForm::Store::kAccountStore;
EXPECT_EQ(account_password_store_->stored_passwords(),
password_manager::TestPasswordStore::PasswordMap(
{{form.signon_realm, {form}}}));
EXPECT_TRUE(local_password_store_->IsEmpty());
EXPECT_EQ(1u,
bookmark_model_->account_bookmark_bar_node()->children().size());
}
TEST_F(LocalDataMigrationHelperTest, ShouldHandleMultipleRequestsForPasswords) {
// Add test data to local store.
auto local_form1 =
CreateTestPassword("https://www.amazon.de",
password_manager::PasswordForm::Store::kProfileStore,
base::Time::UnixEpoch() + base::Seconds(1));
local_form1.password_value = u"local_value";
auto local_form2 = CreateTestPassword("https://www.facebook.com");
local_password_store_->AddLogin(local_form1);
local_password_store_->AddLogin(local_form2);
// Add test data to account store.
auto account_form1 =
CreateTestPassword("https://www.amazon.de",
password_manager::PasswordForm::Store::kAccountStore,
base::Time::UnixEpoch());
account_form1.password_value = u"account_value";
RunAllPendingTasks();
// Request #1.
local_data_migration_helper_->Run(syncer::ModelTypeSet({syncer::PASSWORDS}));
// Request #2.
local_data_migration_helper_->Run(syncer::ModelTypeSet({syncer::PASSWORDS}));
RunAllPendingTasks();
// Passwords have been moved to the account store.
local_form1.in_store = password_manager::PasswordForm::Store::kAccountStore;
local_form2.in_store = password_manager::PasswordForm::Store::kAccountStore;
EXPECT_EQ(account_password_store_->stored_passwords(),
password_manager::TestPasswordStore::PasswordMap({
{local_form1.signon_realm, {local_form1}},
{local_form2.signon_realm, {local_form2}},
}));
// Local password store is empty.
EXPECT_TRUE(local_password_store_->IsEmpty());
}
TEST_F(LocalDataMigrationHelperTest, ShouldMoveReadingListToAccountStore) {
// Required by MarkAllForUploadToSyncServerIfNeeded() to work.
ON_CALL(processor_, IsTrackingMetadata)
.WillByDefault(::testing::Return(true));
// Add test data.
local_reading_list_model_->AddOrReplaceEntry(
GURL("https://www.amazon.de"), "url1",
reading_list::ADDED_VIA_CURRENT_APP,
/*estimated_read_time=*/base::TimeDelta());
account_reading_list_model_->AddOrReplaceEntry(
GURL("https://www.facebook.com"), "url2",
reading_list::ADDED_VIA_CURRENT_APP,
/*estimated_read_time=*/base::TimeDelta());
local_data_migration_helper_->Run(
syncer::ModelTypeSet({syncer::READING_LIST}));
RunAllPendingTasks();
EXPECT_EQ(2u, account_reading_list_model_->size());
EXPECT_EQ(0u, local_reading_list_model_->size());
}
TEST_F(LocalDataMigrationHelperTest, ShouldClearReadingListFromLocalStore) {
// Required by MarkAllForUploadToSyncServerIfNeeded() to work.
ON_CALL(processor_, IsTrackingMetadata)
.WillByDefault(::testing::Return(true));
const GURL kCommonUrl("http://common_url.com/");
// Add test data.
local_reading_list_model_->AddOrReplaceEntry(
kCommonUrl, "url1", reading_list::ADDED_VIA_CURRENT_APP,
/*estimated_read_time=*/base::TimeDelta());
// Same data exists in the account store.
account_reading_list_model_->AddOrReplaceEntry(
kCommonUrl, "url1", reading_list::ADDED_VIA_CURRENT_APP,
/*estimated_read_time=*/base::TimeDelta());
ASSERT_EQ(
dual_reading_list_model_->GetStorageStateForURLForTesting(kCommonUrl),
reading_list::DualReadingListModel::StorageStateForTesting::
kExistsInBothModels);
local_data_migration_helper_->Run(
syncer::ModelTypeSet({syncer::READING_LIST}));
RunAllPendingTasks();
// No new data in the account reading list since it already existed.
EXPECT_EQ(1u, account_reading_list_model_->size());
// Local store is still cleared.
EXPECT_EQ(0u, local_reading_list_model_->size());
}
} // namespace
} // namespace browser_sync