[go: nahoru, domu]

blob: 7ee6d58402cc785936181efaa1a6322d01705df4 [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/password_manager/core/browser/built_in_backend_to_android_backend_migrator.h"
#include "base/feature_list.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/task_environment.h"
#include "base/time/time.h"
#include "components/password_manager/core/browser/fake_password_store_backend.h"
#include "components/password_manager/core/browser/mock_password_store_backend.h"
#include "components/password_manager/core/browser/password_manager_test_utils.h"
#include "components/password_manager/core/common/password_manager_features.h"
#include "components/password_manager/core/common/password_manager_pref_names.h"
#include "components/prefs/pref_registry.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/testing_pref_service.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::Eq;
using ::testing::Invoke;
using ::testing::Pointee;
using ::testing::Return;
using ::testing::UnorderedElementsAreArray;
using ::testing::WithArg;
namespace password_manager {
namespace {
constexpr base::TimeDelta kLatencyDelta = base::Milliseconds(123u);
PasswordForm CreateTestPasswordForm(int index = 0) {
PasswordForm form;
form.url = GURL("https://test" + base::NumberToString(index) + ".com");
form.signon_realm = form.url.spec();
form.username_value = u"username" + base::NumberToString16(index);
form.password_value = u"password" + base::NumberToString16(index);
form.in_store = PasswordForm::Store::kProfileStore;
return form;
}
} // namespace
// Checks that initial/rolling migration is started only when all the conditions
// are satisfied. It also check that migration result is properly recorded in
// prefs.
class BuiltInBackendToAndroidBackendMigratorTest : public testing::Test {
protected:
BuiltInBackendToAndroidBackendMigratorTest() = default;
~BuiltInBackendToAndroidBackendMigratorTest() override = default;
void Init(int current_migration_version = 0) {
prefs_.registry()->RegisterIntegerPref(
prefs::kCurrentMigrationVersionToGoogleMobileServices, 0);
prefs_.SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices,
current_migration_version);
prefs_.registry()->RegisterDoublePref(prefs::kTimeOfLastMigrationAttempt,
0.0);
migrator_ = std::make_unique<BuiltInBackendToAndroidBackendMigrator>(
&built_in_backend_, &android_backend_, &prefs_, &sync_delegate_);
}
MockPasswordBackendSyncDelegate& sync_delegate() { return sync_delegate_; }
PasswordStoreBackend& built_in_backend() { return built_in_backend_; }
PasswordStoreBackend& android_backend() { return android_backend_; }
base::test::ScopedFeatureList& feature_list() { return feature_list_; }
TestingPrefServiceSimple* prefs() { return &prefs_; }
BuiltInBackendToAndroidBackendMigrator* migrator() { return migrator_.get(); }
void RunUntilIdle() { task_env_.RunUntilIdle(); }
void FastForwardBy(base::TimeDelta delta) { task_env_.FastForwardBy(delta); }
protected:
testing::StrictMock<MockPasswordBackendSyncDelegate> sync_delegate_;
private:
base::test::SingleThreadTaskEnvironment task_env_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
base::test::ScopedFeatureList feature_list_;
TestingPrefServiceSimple prefs_;
FakePasswordStoreBackend built_in_backend_;
FakePasswordStoreBackend android_backend_{
IsAccountStore(false),
FakePasswordStoreBackend::UpdateAlwaysSucceeds(true)};
std::unique_ptr<BuiltInBackendToAndroidBackendMigrator> migrator_;
};
TEST_F(BuiltInBackendToAndroidBackendMigratorTest,
CurrentMigrationVersionIsUpdatedWhenMigrationIsNeeded_SyncOn) {
feature_list().InitAndEnableFeatureWithParameters(
/*enabled_feature=*/features::kUnifiedPasswordManagerAndroid,
{{"migration_version", "1"}, {"stage", "0"}});
Init();
EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
.WillRepeatedly(Return(true));
migrator()->StartMigrationIfNecessary();
RunUntilIdle();
EXPECT_EQ(1, prefs()->GetInteger(
prefs::kCurrentMigrationVersionToGoogleMobileServices));
EXPECT_EQ(
base::Time::Now().ToDoubleT(),
prefs()->GetDouble(password_manager::prefs::kTimeOfLastMigrationAttempt));
}
TEST_F(BuiltInBackendToAndroidBackendMigratorTest,
PrefsUnchangedWhenMigrationIsNeeded_SyncOff) {
feature_list().InitAndEnableFeatureWithParameters(
/*enabled_feature=*/features::kUnifiedPasswordManagerAndroid,
{{"migration_version", "1"}, {"stage", "0"}});
Init();
EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
.WillRepeatedly(Return(false));
migrator()->StartMigrationIfNecessary();
RunUntilIdle();
EXPECT_EQ(0, prefs()->GetInteger(
prefs::kCurrentMigrationVersionToGoogleMobileServices));
EXPECT_EQ(0, prefs()->GetDouble(
password_manager::prefs::kTimeOfLastMigrationAttempt));
}
TEST_F(BuiltInBackendToAndroidBackendMigratorTest,
DISABLED_AllPrefsAreUpdatedWhenMigrationIsNeeded_SyncOff) {
feature_list().InitAndEnableFeatureWithParameters(
/*enabled_feature=*/features::kUnifiedPasswordManagerAndroid,
{{"migration_version", "1"}, {"stage", "0"}});
Init();
EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
.WillRepeatedly(Return(false));
migrator()->StartMigrationIfNecessary();
RunUntilIdle();
EXPECT_EQ(1, prefs()->GetInteger(
prefs::kCurrentMigrationVersionToGoogleMobileServices));
EXPECT_EQ(
base::Time::Now().ToDoubleT(),
prefs()->GetDouble(password_manager::prefs::kTimeOfLastMigrationAttempt));
}
TEST_F(BuiltInBackendToAndroidBackendMigratorTest,
PrefsUnchangedWhenAttemptedMigrationEarlierToday) {
feature_list().InitAndEnableFeatureWithParameters(
features::kUnifiedPasswordManagerAndroid,
{{"migration_version", "1"}, {"stage", "0"}});
Init();
prefs()->SetDouble(password_manager::prefs::kTimeOfLastMigrationAttempt,
(base::Time::Now() - base::Hours(2)).ToDoubleT());
migrator()->StartMigrationIfNecessary();
RunUntilIdle();
EXPECT_EQ(0, prefs()->GetInteger(
prefs::kCurrentMigrationVersionToGoogleMobileServices));
EXPECT_EQ(
(base::Time::Now() - base::Hours(2)).ToDoubleT(),
prefs()->GetDouble(password_manager::prefs::kTimeOfLastMigrationAttempt));
}
TEST_F(BuiltInBackendToAndroidBackendMigratorTest,
LastAttemptUnchangedWhenRollingMigrationDisabled) {
// Setup the pref to indicate that the initial migration has happened already.
feature_list().InitWithFeaturesAndParameters(
/*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid,
{{"migration_version", "1"}, {"stage", "1"}}}},
/*disabled_features=*/{});
Init(/*current_migration_version=*/1);
migrator()->StartMigrationIfNecessary();
RunUntilIdle();
EXPECT_EQ(1, prefs()->GetInteger(
prefs::kCurrentMigrationVersionToGoogleMobileServices));
EXPECT_EQ(0, prefs()->GetDouble(
password_manager::prefs::kTimeOfLastMigrationAttempt));
}
// TODO(crbug.com/1306001): Reenable rolling migration or clean up.
TEST_F(BuiltInBackendToAndroidBackendMigratorTest,
DISABLED_LastAttemptUpdatedInPrefsWhenRollingMigrationEnabled) {
// Setup the pref to indicate that the initial migration has happened already.
feature_list().InitWithFeaturesAndParameters(
/*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid,
{{"migration_version", "1"}, {"stage", "0"}}}},
/*disabled_features=*/{});
Init(/*current_migration_version=*/1);
migrator()->StartMigrationIfNecessary();
RunUntilIdle();
EXPECT_EQ(1, prefs()->GetInteger(
prefs::kCurrentMigrationVersionToGoogleMobileServices));
EXPECT_EQ(
base::Time::Now().ToDoubleT(),
prefs()->GetDouble(password_manager::prefs::kTimeOfLastMigrationAttempt));
}
TEST_F(BuiltInBackendToAndroidBackendMigratorTest,
InitialMigrationNeverStartedMetrics) {
base::HistogramTester histogram_tester;
const char kMigrationFinishedMetric[] =
"PasswordManager.UnifiedPasswordManager.WasMigrationDone";
feature_list().InitAndEnableFeatureWithParameters(
/*enabled_feature=*/features::kUnifiedPasswordManagerAndroid,
{{"migration_version", "1"}, {"stage", "0"}});
Init();
histogram_tester.ExpectTotalCount(kMigrationFinishedMetric, 1);
histogram_tester.ExpectBucketCount(kMigrationFinishedMetric, false, 1);
}
TEST_F(BuiltInBackendToAndroidBackendMigratorTest,
InitialMigrationFinishedMetrics) {
base::HistogramTester histogram_tester;
const char kMigrationFinishedMetric[] =
"PasswordManager.UnifiedPasswordManager.WasMigrationDone";
feature_list().InitAndEnableFeatureWithParameters(
/*enabled_feature=*/features::kUnifiedPasswordManagerAndroid,
{{"migration_version", "1"}, {"stage", "0"}});
Init(/*current_migration_version=*/1);
histogram_tester.ExpectTotalCount(kMigrationFinishedMetric, 1);
histogram_tester.ExpectBucketCount(kMigrationFinishedMetric, true, 1);
}
TEST_F(BuiltInBackendToAndroidBackendMigratorTest,
InitialMigrationNeedsRestartMetrics) {
base::HistogramTester histogram_tester;
const char kMigrationFinishedMetric[] =
"PasswordManager.UnifiedPasswordManager.WasMigrationDone";
feature_list().InitAndEnableFeatureWithParameters(
/*enabled_feature=*/features::kUnifiedPasswordManagerAndroid,
{{"migration_version", "2"}, {"stage", "0"}});
Init(/*current_migration_version=*/1);
histogram_tester.ExpectTotalCount(kMigrationFinishedMetric, 1);
histogram_tester.ExpectBucketCount(kMigrationFinishedMetric, false, 1);
}
TEST_F(BuiltInBackendToAndroidBackendMigratorTest,
InitialMigrationForSyncingUserShouldMoveLocalOnlyDataToAndroidBackend) {
feature_list().InitAndEnableFeatureWithParameters(
/*enabled_feature=*/features::kUnifiedPasswordManagerAndroid,
{{"migration_version", "1"}, {"stage", "0"}});
EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
.WillRepeatedly(Return(true));
Init();
PasswordForm form = CreateTestPasswordForm();
android_backend().AddLoginAsync(form, base::DoNothing());
// 'skip_zero_click' is a local only field in PasswordForm and hence not
// available in Android backend before the migration.
PasswordForm form_with_local_data = form;
form_with_local_data.skip_zero_click = true;
built_in_backend().AddLoginAsync(form_with_local_data, base::DoNothing());
migrator()->StartMigrationIfNecessary();
RunUntilIdle();
base::MockCallback<LoginsOrErrorReply> mock_reply;
std::vector<std::unique_ptr<PasswordForm>> expected_logins_android_backend;
expected_logins_android_backend.push_back(
std::make_unique<PasswordForm>(form_with_local_data));
EXPECT_CALL(mock_reply,
Run(LoginsResultsOrErrorAre(&expected_logins_android_backend)));
android_backend().GetAllLoginsAsync(mock_reply.Get());
RunUntilIdle();
}
// Holds the built in and android backend's logins and the expected result after
// the migration.
struct MigrationParam {
struct Entry {
Entry(int index,
std::string password = "",
base::TimeDelta date_created = base::TimeDelta())
: index(index), password(password), date_created(date_created) {}
std::unique_ptr<PasswordForm> ToPasswordForm() const {
PasswordForm form = CreateTestPasswordForm(index);
form.password_value = base::ASCIIToUTF16(password);
form.date_created = base::Time() + date_created;
return std::make_unique<PasswordForm>(form);
}
int index;
std::string password;
base::TimeDelta date_created;
};
std::vector<std::unique_ptr<PasswordForm>> GetBuiltInLogins() const {
return EntriesToPasswordForms(built_in_logins);
}
std::vector<std::unique_ptr<PasswordForm>> GetAndroidLogins() const {
return EntriesToPasswordForms(android_logins);
}
std::vector<std::unique_ptr<PasswordForm>> GetMergedLogins() const {
return EntriesToPasswordForms(merged_logins);
}
std::vector<std::unique_ptr<PasswordForm>> GetUpdatedAndroidLogins() const {
return EntriesToPasswordForms(updated_android_logins);
}
std::vector<std::unique_ptr<PasswordForm>> EntriesToPasswordForms(
const std::vector<Entry>& entries) const {
std::vector<std::unique_ptr<PasswordForm>> v;
base::ranges::transform(entries, std::back_inserter(v),
&Entry::ToPasswordForm);
return v;
}
std::vector<Entry> built_in_logins;
std::vector<Entry> android_logins;
std::vector<Entry> merged_logins;
std::vector<Entry> updated_android_logins;
};
// Tests that initial and rolling migration actually works by comparing
// passwords in built-in/android backend before and after migration.
class BuiltInBackendToAndroidBackendMigratorTestWithMigrationParams
: public BuiltInBackendToAndroidBackendMigratorTest,
public testing::WithParamInterface<MigrationParam> {};
// Tests the initial migration result.
TEST_P(BuiltInBackendToAndroidBackendMigratorTestWithMigrationParams,
InitialMigrationForSyncingUsers) {
BuiltInBackendToAndroidBackendMigratorTest::Init();
EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
.WillRepeatedly(Return(true));
feature_list().InitAndEnableFeatureWithParameters(
/*enabled_feature=*/features::kUnifiedPasswordManagerAndroid,
{{"migration_version", "1"}, {"stage", "0"}});
const MigrationParam& p = GetParam();
for (const auto& login : p.GetBuiltInLogins()) {
built_in_backend().AddLoginAsync(*login, base::DoNothing());
}
for (const auto& login : p.GetAndroidLogins()) {
android_backend().AddLoginAsync(*login, base::DoNothing());
}
RunUntilIdle();
migrator()->StartMigrationIfNecessary();
RunUntilIdle();
// The built-in logins should not be affected.
base::MockCallback<LoginsOrErrorReply> built_in_reply;
auto built_in_logins = p.GetBuiltInLogins();
EXPECT_CALL(built_in_reply, Run(LoginsResultsOrErrorAre(&built_in_logins)));
built_in_backend().GetAllLoginsAsync(built_in_reply.Get());
// The android logins are updated. Existing logins are retained.
base::MockCallback<LoginsOrErrorReply> android_reply;
auto updated_logins = p.GetUpdatedAndroidLogins();
EXPECT_CALL(android_reply, Run(LoginsResultsOrErrorAre(&updated_logins)));
android_backend().GetAllLoginsAsync(android_reply.Get());
RunUntilIdle();
}
// Tests the initial migration result.
TEST_P(BuiltInBackendToAndroidBackendMigratorTestWithMigrationParams,
DISABLED_InitialMigration) {
BuiltInBackendToAndroidBackendMigratorTest::Init();
EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
.WillRepeatedly(Return(false));
feature_list().InitAndEnableFeatureWithParameters(
/*enabled_feature=*/features::kUnifiedPasswordManagerAndroid,
{{"migration_version", "1"}, {"stage", "0"}});
const MigrationParam& p = GetParam();
for (const auto& login : p.GetBuiltInLogins()) {
built_in_backend().AddLoginAsync(*login, base::DoNothing());
}
for (const auto& login : p.GetAndroidLogins()) {
android_backend().AddLoginAsync(*login, base::DoNothing());
}
RunUntilIdle();
migrator()->StartMigrationIfNecessary();
RunUntilIdle();
for (auto* const backend : {&android_backend(), &built_in_backend()}) {
base::MockCallback<LoginsOrErrorReply> mock_reply;
auto expected_logins = p.GetMergedLogins();
EXPECT_CALL(mock_reply, Run(LoginsResultsOrErrorAre(&expected_logins)));
backend->GetAllLoginsAsync(mock_reply.Get());
RunUntilIdle();
}
}
// TODO(crbug.com/1306001): Reenable or clean up for local-only users.
TEST_P(BuiltInBackendToAndroidBackendMigratorTestWithMigrationParams,
DISABLED_RollingMigration) {
// Setup the pref to indicate that the initial migration has happened already.
// This implies that rolling migration will take place!
feature_list().InitWithFeaturesAndParameters(
/*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid,
{{"migration_version", "1"}, {"stage", "0"}}}},
/*disabled_features=*/{});
BuiltInBackendToAndroidBackendMigratorTest::Init(
/*current_migration_version=*/1);
const MigrationParam& p = GetParam();
for (const auto& login : p.GetBuiltInLogins()) {
built_in_backend().AddLoginAsync(*login, base::DoNothing());
}
for (const auto& login : p.GetAndroidLogins()) {
android_backend().AddLoginAsync(*login, base::DoNothing());
}
RunUntilIdle();
migrator()->StartMigrationIfNecessary();
RunUntilIdle();
for (auto* const backend : {&android_backend(), &built_in_backend()}) {
base::MockCallback<LoginsOrErrorReply> mock_reply;
auto expected_logins = p.GetAndroidLogins();
EXPECT_CALL(mock_reply, Run(LoginsResultsOrErrorAre(&expected_logins)));
backend->GetAllLoginsAsync(mock_reply.Get());
RunUntilIdle();
}
}
INSTANTIATE_TEST_SUITE_P(
BuiltInBackendToAndroidBackendMigratorTest,
BuiltInBackendToAndroidBackendMigratorTestWithMigrationParams,
testing::Values(
MigrationParam{.built_in_logins = {},
.android_logins = {},
.merged_logins = {},
.updated_android_logins = {}},
MigrationParam{.built_in_logins = {{1}, {2}},
.android_logins = {},
.merged_logins = {{1}, {2}},
.updated_android_logins = {{1}, {2}}},
MigrationParam{.built_in_logins = {},
.android_logins = {{1}, {2}},
.merged_logins = {{1}, {2}},
.updated_android_logins = {{1}, {2}}},
MigrationParam{.built_in_logins = {{1}, {2}},
.android_logins = {{3}},
.merged_logins = {{1}, {2}, {3}},
.updated_android_logins = {{1}, {2}, {3}}},
MigrationParam{.built_in_logins = {{1}, {2}, {3}},
.android_logins = {{1}, {2}, {3}},
.merged_logins = {{1}, {2}, {3}},
.updated_android_logins = {{1}, {2}, {3}}},
MigrationParam{
.built_in_logins = {{1, "old_password", base::Days(1)}, {2}},
.android_logins = {{1, "new_password", base::Days(2)}, {3}},
.merged_logins = {{1, "new_password", base::Days(2)}, {2}, {3}},
.updated_android_logins = {{1, "old_password", base::Days(1)},
{2},
{3}}},
MigrationParam{
.built_in_logins = {{1, "new_password", base::Days(2)}, {2}},
.android_logins = {{1, "old_password", base::Days(1)}, {3}},
.merged_logins = {{1, "new_password", base::Days(2)}, {2}, {3}},
.updated_android_logins = {{1, "new_password", base::Days(2)},
{2},
{3}}}));
struct MigrationParamForMetrics {
// Whether this is initial or rolling migration.
bool is_initial_migration;
// Whether this migration only affects local-only data of sync users.
bool is_sync_enabled;
// Whether migration was completed successfully or not.
bool is_successful_migration;
};
class BuiltInBackendToAndroidBackendMigratorTestMetrics
: public BuiltInBackendToAndroidBackendMigratorTest,
public testing::WithParamInterface<MigrationParamForMetrics> {
protected:
BuiltInBackendToAndroidBackendMigratorTestMetrics() {
prefs()->registry()->RegisterIntegerPref(
prefs::kCurrentMigrationVersionToGoogleMobileServices, 0);
prefs()->registry()->RegisterDoublePref(prefs::kTimeOfLastMigrationAttempt,
0.0);
if (GetParam().is_initial_migration) {
feature_list().InitAndEnableFeatureWithParameters(
/*enabled_feature=*/features::kUnifiedPasswordManagerAndroid,
{{"migration_version", "1"}, {"stage", "0"}});
latency_metric_ =
"PasswordManager.UnifiedPasswordManager.InitialMigration.Latency";
success_metric_ =
"PasswordManager.UnifiedPasswordManager.InitialMigration.Success";
} else {
feature_list().InitWithFeaturesAndParameters(
/*enabled_features=*/{{features::kUnifiedPasswordManagerAndroid,
{{"migration_version", "1"}, {"stage", "0"}}}},
/*disabled_features=*/{});
// Setup the pref to indicate that the initial migration has happened
// already.
prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices,
1);
latency_metric_ =
"PasswordManager.UnifiedPasswordManager.RollingMigration.Latency";
success_metric_ =
"PasswordManager.UnifiedPasswordManager.RollingMigration.Success";
}
migrator_ = std::make_unique<BuiltInBackendToAndroidBackendMigrator>(
&built_in_backend_, &android_backend_, prefs(), &sync_delegate());
}
std::string latency_metric_;
std::string success_metric_;
::testing::StrictMock<MockPasswordStoreBackend> built_in_backend_;
::testing::StrictMock<MockPasswordStoreBackend> android_backend_;
std::unique_ptr<BuiltInBackendToAndroidBackendMigrator> migrator_;
};
TEST_P(BuiltInBackendToAndroidBackendMigratorTestMetrics,
MigrationMetricsTest) {
base::HistogramTester histogram_tester;
// Initial migration only happens with sync enabled for now.
EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
.WillRepeatedly(Return(GetParam().is_sync_enabled));
EXPECT_CALL(built_in_backend_, GetAllLoginsAsync)
.WillOnce(WithArg<0>(Invoke([](LoginsOrErrorReply reply) -> void {
LoginsResultOrError result =
GetParam().is_successful_migration
? LoginsResultOrError(LoginsResult())
: LoginsResultOrError(PasswordStoreBackendError::kUnspecified);
base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, base::BindOnce(std::move(reply), std::move(result)),
kLatencyDelta);
})));
// With sync enabled, the android backend should not contain relevant
// differences and the additional call is unnecessary.
if (!GetParam().is_sync_enabled) {
EXPECT_CALL(android_backend_, GetAllLoginsAsync)
.WillOnce(WithArg<0>(Invoke([](LoginsOrErrorReply reply) -> void {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(reply), LoginsResult()));
})));
}
migrator_->StartMigrationIfNecessary();
FastForwardBy(kLatencyDelta);
histogram_tester.ExpectTotalCount(latency_metric_, 1);
histogram_tester.ExpectTimeBucketCount(latency_metric_, kLatencyDelta, 1);
histogram_tester.ExpectTotalCount(success_metric_, 1);
histogram_tester.ExpectBucketCount(success_metric_, true,
GetParam().is_successful_migration);
histogram_tester.ExpectBucketCount(success_metric_, false,
!GetParam().is_successful_migration);
}
// TODO(crbug.com/1306001): Add cases for rolling migration and non-syncing
// users or clean up.
INSTANTIATE_TEST_SUITE_P(
BuiltInBackendToAndroidBackendMigratorTest,
BuiltInBackendToAndroidBackendMigratorTestMetrics,
testing::Values(MigrationParamForMetrics{.is_initial_migration = true,
.is_sync_enabled = true,
.is_successful_migration = true},
MigrationParamForMetrics{
.is_initial_migration = true,
.is_sync_enabled = true,
.is_successful_migration = false}));
class BuiltInBackendToAndroidBackendMigratorWithMockAndroidBackendTest
: public BuiltInBackendToAndroidBackendMigratorTest {
protected:
BuiltInBackendToAndroidBackendMigratorWithMockAndroidBackendTest() {
prefs()->registry()->RegisterIntegerPref(
prefs::kCurrentMigrationVersionToGoogleMobileServices, 0);
prefs()->registry()->RegisterDoublePref(prefs::kTimeOfLastMigrationAttempt,
0.0);
feature_list().InitAndEnableFeatureWithParameters(
/*enabled_feature=*/features::kUnifiedPasswordManagerAndroid,
{{"migration_version", "1"}, {"stage", "0"}});
migrator_ = std::make_unique<BuiltInBackendToAndroidBackendMigrator>(
&built_in_backend_, &android_backend_, prefs(), &sync_delegate_);
}
PasswordStoreBackend& built_in_backend() { return built_in_backend_; }
::testing::NiceMock<MockPasswordStoreBackend> android_backend_;
std::unique_ptr<BuiltInBackendToAndroidBackendMigrator> migrator_;
private:
FakePasswordStoreBackend built_in_backend_;
};
TEST_F(BuiltInBackendToAndroidBackendMigratorWithMockAndroidBackendTest,
DoesNotCompleteMigrationWhenWritingToAndroidBackendFails_SyncOn) {
EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
.WillRepeatedly(Return(true));
// Add two credentials to the built-in backend.
built_in_backend().AddLoginAsync(CreateTestPasswordForm(/*index=*/1),
base::DoNothing());
built_in_backend().AddLoginAsync(CreateTestPasswordForm(/*index=*/2),
base::DoNothing());
// Simulate an Android backend that fails to write by returning an empty
// changelist.
ON_CALL(android_backend_, UpdateLoginAsync)
.WillByDefault(
WithArg<1>(Invoke([](PasswordStoreChangeListReply callback) -> void {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), PasswordStoreChangeList()));
})));
// Once one UpdateLoginAsync() call fails, all consecutive ones will not be
// executed. Check that exactly one UpdateLoginAsync() is called.
EXPECT_CALL(android_backend_, UpdateLoginAsync).Times(1);
migrator_->StartMigrationIfNecessary();
// Migration version is still 0 since migration didn't complete.
EXPECT_EQ(0, prefs()->GetInteger(
prefs::kCurrentMigrationVersionToGoogleMobileServices));
RunUntilIdle();
}
TEST_F(
BuiltInBackendToAndroidBackendMigratorWithMockAndroidBackendTest,
DISABLED_DoesNotCompleteMigrationWhenWritingToAndroidBackendFails_SyncOff) {
// Sync state doesn't affect this test, run it arbitrarily for non-sync'ing
// users.
EXPECT_CALL(sync_delegate(), IsSyncingPasswordsEnabled)
.WillRepeatedly(Return(false));
// Add two credentials to the built-in backend.
built_in_backend().AddLoginAsync(CreateTestPasswordForm(/*index=*/1),
base::DoNothing());
built_in_backend().AddLoginAsync(CreateTestPasswordForm(/*index=*/2),
base::DoNothing());
// Simulate an empty Android backend.
EXPECT_CALL(android_backend_, GetAllLoginsAsync)
.WillOnce(WithArg<0>(Invoke([](LoginsOrErrorReply reply) -> void {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(reply), LoginsResult()));
})));
// Simulate an Android backend that fails to write by returning an empty
// changelist.
ON_CALL(android_backend_, AddLoginAsync)
.WillByDefault(
WithArg<1>(Invoke([](PasswordStoreChangeListReply callback) -> void {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), PasswordStoreChangeList()));
})));
// Once one AddLoginAsync() call fails, all consecutive ones will not be
// executed. Check that exactly one AddLoginAsync() is called.
EXPECT_CALL(android_backend_, AddLoginAsync).Times(1);
migrator_->StartMigrationIfNecessary();
// Migration version is still 0 since migration didn't complete.
EXPECT_EQ(0, prefs()->GetInteger(
prefs::kCurrentMigrationVersionToGoogleMobileServices));
RunUntilIdle();
}
} // namespace password_manager