blob: 47bba6e02c23b11fa70a8835e76bc3d2ea8cfe94 [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_to_signin_migration.h"
#include <memory>
#include <string>
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "components/browser_sync/browser_sync_switches.h"
#include "components/prefs/testing_pref_service.h"
#include "components/signin/public/base/signin_pref_names.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/sync/base/features.h"
#include "components/sync/base/model_type.h"
#include "components/sync/base/pref_names.h"
#include "components/sync/base/user_selectable_type.h"
#include "components/sync/service/sync_feature_status_for_migrations_recorder.h"
#include "components/sync/service/sync_prefs.h"
#include "components/sync/test/test_sync_service.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace browser_sync {
namespace {
class SyncToSigninMigrationTestBase {
public:
explicit SyncToSigninMigrationTestBase(bool migration_feature_enabled) {
if (migration_feature_enabled) {
features_.InitWithFeatures(
/*enabled_features=*/{syncer::kReplaceSyncPromosWithSignInPromos,
switches::kMigrateSyncingUserToSignedIn},
/*disabled_features=*/{});
} else {
features_.InitWithFeatures(
/*enabled_features=*/{syncer::kReplaceSyncPromosWithSignInPromos},
/*disabled_features=*/{switches::kMigrateSyncingUserToSignedIn});
}
signin::IdentityManager::RegisterProfilePrefs(pref_service_.registry());
syncer::SyncPrefs::RegisterProfilePrefs(pref_service_.registry());
sync_prefs_ = std::make_unique<syncer::SyncPrefs>(&pref_service_);
CHECK(fake_profile_dir_.CreateUniqueTempDir());
}
virtual ~SyncToSigninMigrationTestBase() = default;
void RecordStateToPrefs(bool include_status_recorder = true) {
// Populate signin prefs based on the state of the TestSyncService.
pref_service_.SetString(prefs::kGoogleServicesAccountId,
sync_service_.GetAccountInfo().gaia);
pref_service_.SetBoolean(prefs::kGoogleServicesConsentedToSync,
sync_service_.HasSyncConsent());
pref_service_.SetString(prefs::kGoogleServicesLastSyncingGaiaId,
sync_service_.GetAccountInfo().gaia);
pref_service_.SetString(prefs::kGoogleServicesLastSyncingUsername,
sync_service_.GetAccountInfo().email);
// Populate sync prefs. The TestSyncService doesn't write these, so they
// have to be set manually here.
syncer::SyncUserSettings* settings = sync_service_.GetUserSettings();
sync_prefs_->SetSelectedTypesForSyncingUser(
settings->IsSyncEverythingEnabled(),
settings->GetRegisteredSelectableTypes(), settings->GetSelectedTypes());
#if !BUILDFLAG(IS_CHROMEOS_ASH)
sync_prefs_->SetInitialSyncFeatureSetupComplete();
#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
if (include_status_recorder) {
// Populate migration-specific Sync status prefs.
syncer::SyncFeatureStatusForMigrationsRecorder recorder(&pref_service_,
&sync_service_);
// Before destroying the recorder again, tell it that sync is shutting
// down to avoid a dangling observer.
recorder.OnSyncShutdown(&sync_service_);
}
}
private:
base::test::ScopedFeatureList features_;
base::test::SingleThreadTaskEnvironment task_environment_;
protected:
TestingPrefServiceSimple pref_service_;
std::unique_ptr<syncer::SyncPrefs> sync_prefs_;
syncer::TestSyncService sync_service_;
base::ScopedTempDir fake_profile_dir_;
};
class SyncToSigninMigrationTest : public SyncToSigninMigrationTestBase,
public testing::Test {
public:
SyncToSigninMigrationTest()
: SyncToSigninMigrationTestBase(
/*migration_feature_enabled=*/true) {}
};
TEST_F(SyncToSigninMigrationTest, SyncActive) {
// Sync is active.
ASSERT_EQ(sync_service_.GetTransportState(),
syncer::SyncService::TransportState::ACTIVE);
ASSERT_TRUE(sync_service_.HasSyncConsent());
const std::string gaia_id = sync_service_.GetAccountInfo().gaia;
const std::string email = sync_service_.GetAccountInfo().email;
// Save the above state to prefs.
RecordStateToPrefs();
ASSERT_TRUE(sync_prefs_->IsInitialSyncFeatureSetupComplete());
// Before the migration, there are no per-account selected types.
ASSERT_TRUE(
pref_service_.GetDict(syncer::prefs::internal::kSelectedTypesPerAccount)
.empty());
// Run the migration. This should change the user to be non-syncing.
MaybeMigrateSyncingUserToSignedIn(fake_profile_dir_.GetPath(),
&pref_service_);
// Note that TestSyncService doesn't consume the prefs, so verify the prefs
// directly here.
// The user should still be signed in.
EXPECT_EQ(pref_service_.GetString(prefs::kGoogleServicesAccountId), gaia_id);
// But not syncing anymore.
EXPECT_FALSE(pref_service_.GetBoolean(prefs::kGoogleServicesConsentedToSync));
#if !BUILDFLAG(IS_CHROMEOS_ASH)
EXPECT_FALSE(sync_prefs_->IsInitialSyncFeatureSetupComplete());
#endif
// The fact that the user was migrated should be recorded in prefs.
EXPECT_EQ(pref_service_.GetString(
prefs::kGoogleServicesSyncingGaiaIdMigratedToSignedIn),
gaia_id);
EXPECT_EQ(pref_service_.GetString(
prefs::kGoogleServicesSyncingUsernameMigratedToSignedIn),
email);
// There should be per-account selected types now. The details of this are
// covered in SyncPrefs unit tests.
EXPECT_FALSE(
pref_service_.GetDict(syncer::prefs::internal::kSelectedTypesPerAccount)
.empty());
}
TEST_F(SyncToSigninMigrationTest, SyncStatusPrefsUnset) {
// Everything is active.
ASSERT_EQ(sync_service_.GetTransportState(),
syncer::SyncService::TransportState::ACTIVE);
ASSERT_TRUE(sync_service_.HasSyncConsent());
// Save the Sync configuration (enabled data types etc) to prefs, but not the
// migration-specific status prefs. This simulates the case of an old client
// which has never written those prefs.
RecordStateToPrefs(/*include_status_recorder=*/false);
// Take a copy of all current pref values, to verify that the migration
// doesn't modify any of them.
const base::Value::Dict all_prefs =
pref_service_.user_prefs_store()->GetValues();
// Trigger the migration - it should NOT actually run in this state.
MaybeMigrateSyncingUserToSignedIn(fake_profile_dir_.GetPath(),
&pref_service_);
// Note that TestSyncService doesn't consume the prefs, so verify the prefs
// directly here.
// Since the migration didn't actually run, the prefs should be unmodified.
EXPECT_EQ(pref_service_.user_prefs_store()->GetValues(), all_prefs);
}
TEST_F(SyncToSigninMigrationTest, SyncTransport) {
// There's no Sync consent, but otherwise everything is active (running in
// transport mode).
sync_service_.SetHasSyncConsent(false);
ASSERT_EQ(sync_service_.GetTransportState(),
syncer::SyncService::TransportState::ACTIVE);
// Save the above state to prefs.
RecordStateToPrefs();
// Take a copy of all current pref values, to verify that the migration
// doesn't modify any of them.
const base::Value::Dict all_prefs =
pref_service_.user_prefs_store()->GetValues();
// Trigger the migration - it should NOT actually run in this state.
MaybeMigrateSyncingUserToSignedIn(fake_profile_dir_.GetPath(),
&pref_service_);
// Note that TestSyncService doesn't consume the prefs, so verify the prefs
// directly here.
// Since the migration didn't actually run, the prefs should be unmodified.
EXPECT_EQ(pref_service_.user_prefs_store()->GetValues(), all_prefs);
}
TEST_F(SyncToSigninMigrationTest, SyncDisabledByPolicy) {
// The user is signed in and opted in to Sync, but Sync is disabled via
// enterprise policy.
sync_service_.SetDisableReasons(
{syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY});
ASSERT_EQ(sync_service_.GetTransportState(),
syncer::SyncService::TransportState::DISABLED);
ASSERT_TRUE(sync_service_.HasSyncConsent());
const std::string gaia_id = sync_service_.GetAccountInfo().gaia;
const std::string email = sync_service_.GetAccountInfo().email;
// Save the above state to prefs.
RecordStateToPrefs();
// Before the migration, there are no per-account selected types.
ASSERT_TRUE(
pref_service_.GetDict(syncer::prefs::internal::kSelectedTypesPerAccount)
.empty());
// Run the migration. This should change the user to be non-syncing (even
// though Sync wasn't actually active).
MaybeMigrateSyncingUserToSignedIn(fake_profile_dir_.GetPath(),
&pref_service_);
// Note that TestSyncService doesn't consume the prefs, so verify the prefs
// directly here.
// The user should still be signed in.
EXPECT_EQ(pref_service_.GetString(prefs::kGoogleServicesAccountId), gaia_id);
// But not syncing anymore.
EXPECT_FALSE(pref_service_.GetBoolean(prefs::kGoogleServicesConsentedToSync));
// The fact that the user was migrated should be recorded in prefs.
EXPECT_EQ(pref_service_.GetString(
prefs::kGoogleServicesSyncingGaiaIdMigratedToSignedIn),
gaia_id);
EXPECT_EQ(pref_service_.GetString(
prefs::kGoogleServicesSyncingUsernameMigratedToSignedIn),
email);
// There should be per-account selected types now. The details of this are
// covered in SyncPrefs unit tests.
EXPECT_FALSE(
pref_service_.GetDict(syncer::prefs::internal::kSelectedTypesPerAccount)
.empty());
}
TEST_F(SyncToSigninMigrationTest, SyncPaused) {
// Sync-the-feature is enabled, but in the "paused" state due to a persistent
// auth error.
sync_service_.SetPersistentAuthError();
ASSERT_EQ(sync_service_.GetTransportState(),
syncer::SyncService::TransportState::PAUSED);
ASSERT_TRUE(sync_service_.HasSyncConsent());
ASSERT_TRUE(sync_service_.GetActiveDataTypes().empty());
const std::string gaia_id = sync_service_.GetAccountInfo().gaia;
const std::string email = sync_service_.GetAccountInfo().email;
// Save the above state to prefs.
RecordStateToPrefs();
// Before the migration, there are no per-account selected types.
ASSERT_TRUE(
pref_service_.GetDict(syncer::prefs::internal::kSelectedTypesPerAccount)
.empty());
// Run the migration. This should change the user to be non-syncing (even
// though Sync wasn't actually active).
MaybeMigrateSyncingUserToSignedIn(fake_profile_dir_.GetPath(),
&pref_service_);
// Note that TestSyncService doesn't consume the prefs, so verify the prefs
// directly here.
// The user should still be signed in.
EXPECT_EQ(pref_service_.GetString(prefs::kGoogleServicesAccountId), gaia_id);
// But not syncing anymore.
EXPECT_FALSE(pref_service_.GetBoolean(prefs::kGoogleServicesConsentedToSync));
// The fact that the user was migrated should be recorded in prefs.
EXPECT_EQ(pref_service_.GetString(
prefs::kGoogleServicesSyncingGaiaIdMigratedToSignedIn),
gaia_id);
EXPECT_EQ(pref_service_.GetString(
prefs::kGoogleServicesSyncingUsernameMigratedToSignedIn),
email);
// There should be per-account selected types now. The details of this are
// covered in SyncPrefs unit tests.
EXPECT_FALSE(
pref_service_.GetDict(syncer::prefs::internal::kSelectedTypesPerAccount)
.empty());
}
TEST_F(SyncToSigninMigrationTest, SyncInitializing) {
// The user is signed in and opted in to Sync, but Sync is still initializing.
sync_service_.SetTransportState(
syncer::SyncService::TransportState::INITIALIZING);
ASSERT_TRUE(sync_service_.HasSyncConsent());
// Save the above state to prefs.
RecordStateToPrefs();
// Take a copy of all current pref values, to verify that the migration
// doesn't modify any of them.
const base::Value::Dict all_prefs =
pref_service_.user_prefs_store()->GetValues();
// Trigger the migration - it should NOT actually run in this state.
MaybeMigrateSyncingUserToSignedIn(fake_profile_dir_.GetPath(),
&pref_service_);
// Note that TestSyncService doesn't consume the prefs, so verify the prefs
// directly here.
// Since the migration didn't actually run, the prefs should be unmodified.
EXPECT_EQ(pref_service_.user_prefs_store()->GetValues(), all_prefs);
}
TEST_F(SyncToSigninMigrationTest, UndoFeaturePreventsMigration) {
base::test::ScopedFeatureList undo_feature;
undo_feature.InitAndEnableFeature(
switches::kUndoMigrationOfSyncingUserToSignedIn);
// Everything is active.
ASSERT_EQ(sync_service_.GetTransportState(),
syncer::SyncService::TransportState::ACTIVE);
ASSERT_TRUE(sync_service_.HasSyncConsent());
ASSERT_TRUE(sync_service_.GetActiveDataTypes().HasAll(
{syncer::BOOKMARKS, syncer::PASSWORDS, syncer::READING_LIST}));
// Save the above state to prefs.
RecordStateToPrefs();
// Take a copy of all current pref values, to verify that the migration
// doesn't modify any of them.
const base::Value::Dict all_prefs =
pref_service_.user_prefs_store()->GetValues();
base::HistogramTester histograms;
// Trigger the migration.
MaybeMigrateSyncingUserToSignedIn(fake_profile_dir_.GetPath(),
&pref_service_);
// Even though the user would be eligible, the "undo" feature should have
// prevented the migration from happening. (And since there was nothing to
// undo, it shouldn't have had any effect either.)
EXPECT_EQ(pref_service_.user_prefs_store()->GetValues(), all_prefs);
histograms.ExpectUniqueSample(
"Sync.SyncToSigninMigrationDecision",
/*SyncToSigninMigrationDecision::kUndoNotNecessary*/ 7, 1);
}
// Fixture for tests covering migration metrics. The test param determines
// whether the feature flag is enabled or not.
class SyncToSigninMigrationMetricsTest : public SyncToSigninMigrationTestBase,
public testing::TestWithParam<bool> {
public:
SyncToSigninMigrationMetricsTest()
: SyncToSigninMigrationTestBase(
/*migration_feature_enabled=*/GetParam()) {}
bool IsMigrationEnabled() const { return GetParam(); }
std::string GetTypeDecisionHistogramInfix() const {
return IsMigrationEnabled() ? "Migration" : "DryRun";
}
};
TEST_P(SyncToSigninMigrationMetricsTest, SyncAndAllDataTypesActive) {
// Everything is active.
ASSERT_EQ(sync_service_.GetTransportState(),
syncer::SyncService::TransportState::ACTIVE);
ASSERT_TRUE(sync_service_.HasSyncConsent());
ASSERT_TRUE(sync_service_.GetActiveDataTypes().HasAll(
{syncer::BOOKMARKS, syncer::PASSWORDS, syncer::READING_LIST}));
// Save the above state to prefs.
RecordStateToPrefs();
base::HistogramTester histograms;
MaybeMigrateSyncingUserToSignedIn(fake_profile_dir_.GetPath(),
&pref_service_);
// The overall migration should run, except if the feature flag is disabled.
int expected_decision =
IsMigrationEnabled()
? /*SyncToSigninMigrationDecision::kMigrate*/ 0
: /*SyncToSigninMigrationDecision::kDontMigrateFlagDisabled*/ 5;
histograms.ExpectUniqueSample("Sync.SyncToSigninMigrationDecision",
expected_decision, 1);
if (IsMigrationEnabled()) {
histograms.ExpectTotalCount("Sync.SyncToSigninMigrationOutcome", 1);
histograms.ExpectTotalCount("Sync.SyncToSigninMigrationTime", 1);
} else {
histograms.ExpectTotalCount("Sync.SyncToSigninMigrationOutcome", 0);
histograms.ExpectTotalCount("Sync.SyncToSigninMigrationTime", 0);
}
// All the data type migrations should run - in "DryRun" mode if the feature
// flag is disabled.
std::string infix = GetTypeDecisionHistogramInfix();
histograms.ExpectUniqueSample(
"Sync.SyncToSigninMigrationDecision." + infix + ".BOOKMARK",
/*SyncToSigninMigrationDataTypeDecision::kMigrate*/ 0, 1);
histograms.ExpectUniqueSample(
"Sync.SyncToSigninMigrationDecision." + infix + ".PASSWORD",
/*SyncToSigninMigrationDataTypeDecision::kMigrate*/ 0, 1);
histograms.ExpectUniqueSample(
"Sync.SyncToSigninMigrationDecision." + infix + ".READING_LIST",
/*SyncToSigninMigrationDataTypeDecision::kMigrate*/ 0, 1);
}
TEST_P(SyncToSigninMigrationMetricsTest, SyncActiveButNotDataTypes) {
// Sync-the-feature is active.
ASSERT_EQ(sync_service_.GetTransportState(),
syncer::SyncService::TransportState::ACTIVE);
ASSERT_TRUE(sync_service_.HasSyncConsent());
// ReadingList is not selected.
sync_service_.GetUserSettings()->SetSelectedTypes(
/*sync_everything=*/false, {syncer::UserSelectableType::kBookmarks,
syncer::UserSelectableType::kPasswords});
// Passwords is selected, but failed to actually start up (e.g. disabled by
// policy).
sync_service_.SetFailedDataTypes({syncer::PASSWORDS});
ASSERT_TRUE(sync_service_.GetActiveDataTypes().Has(syncer::BOOKMARKS));
ASSERT_FALSE(sync_service_.GetActiveDataTypes().Has(syncer::PASSWORDS));
ASSERT_FALSE(sync_service_.GetActiveDataTypes().Has(syncer::READING_LIST));
// Save the above state to prefs.
RecordStateToPrefs();
base::HistogramTester histograms;
MaybeMigrateSyncingUserToSignedIn(fake_profile_dir_.GetPath(),
&pref_service_);
// The overall migration should run, except if the feature flag is disabled.
int expected_decision =
IsMigrationEnabled()
? /*SyncToSigninMigrationDecision::kMigrate*/ 0
: /*SyncToSigninMigrationDecision::kDontMigrateFlagDisabled*/ 5;
histograms.ExpectUniqueSample("Sync.SyncToSigninMigrationDecision",
expected_decision, 1);
if (IsMigrationEnabled()) {
histograms.ExpectTotalCount("Sync.SyncToSigninMigrationOutcome", 1);
histograms.ExpectTotalCount("Sync.SyncToSigninMigrationTime", 1);
} else {
histograms.ExpectTotalCount("Sync.SyncToSigninMigrationOutcome", 0);
histograms.ExpectTotalCount("Sync.SyncToSigninMigrationTime", 0);
}
std::string infix = GetTypeDecisionHistogramInfix();
// Bookmarks was active, so its migration should run.
histograms.ExpectUniqueSample(
"Sync.SyncToSigninMigrationDecision." + infix + ".BOOKMARK",
/*SyncToSigninMigrationDataTypeDecision::kMigrate*/ 0, 1);
// Passwords was not active, even though it was enabled.
histograms.ExpectUniqueSample(
"Sync.SyncToSigninMigrationDecision." + infix + ".PASSWORD",
/*SyncToSigninMigrationDataTypeDecision::kDontMigrateTypeNotActive*/ 2,
1);
// ReadingList was disabled by the user.
histograms.ExpectUniqueSample(
"Sync.SyncToSigninMigrationDecision." + infix + ".READING_LIST",
/*SyncToSigninMigrationDataTypeDecision::kDontMigrateTypeDisabled*/ 1, 1);
}
TEST_P(SyncToSigninMigrationMetricsTest, SyncStatusPrefsUnset) {
// Everything is active.
ASSERT_EQ(sync_service_.GetTransportState(),
syncer::SyncService::TransportState::ACTIVE);
ASSERT_TRUE(sync_service_.HasSyncConsent());
ASSERT_TRUE(sync_service_.GetActiveDataTypes().HasAll(
{syncer::BOOKMARKS, syncer::PASSWORDS, syncer::READING_LIST}));
// Save the Sync configuration (enabled data types etc) to prefs, but not the
// migration-specific status prefs. This simulates the case of an old client
// which has never written those prefs.
RecordStateToPrefs(/*include_status_recorder=*/false);
base::HistogramTester histograms;
MaybeMigrateSyncingUserToSignedIn(fake_profile_dir_.GetPath(),
&pref_service_);
// The migration should not run due to the missing/undefined status.
histograms.ExpectUniqueSample(
"Sync.SyncToSigninMigrationDecision",
/*SyncToSigninMigrationDecision::kDontMigrateSyncStatusUndefined*/ 3, 1);
histograms.ExpectTotalCount("Sync.SyncToSigninMigrationOutcome", 0);
histograms.ExpectTotalCount("Sync.SyncToSigninMigrationTime", 0);
histograms.ExpectTotalCount(
"Sync.SyncToSigninMigrationDecision.DryRun.BOOKMARK", 0);
histograms.ExpectTotalCount(
"Sync.SyncToSigninMigrationDecision.DryRun.PASSWORD", 0);
histograms.ExpectTotalCount(
"Sync.SyncToSigninMigrationDecision.DryRun.READING_LIST", 0);
histograms.ExpectTotalCount(
"Sync.SyncToSigninMigrationDecision.Migration.BOOKMARK", 0);
histograms.ExpectTotalCount(
"Sync.SyncToSigninMigrationDecision.Migration.PASSWORD", 0);
histograms.ExpectTotalCount(
"Sync.SyncToSigninMigrationDecision.Migration.READING_LIST", 0);
}
TEST_P(SyncToSigninMigrationMetricsTest, NotSignedIn) {
// There's no signed-in user.
sync_service_.SetAccountInfo(CoreAccountInfo());
sync_service_.SetHasSyncConsent(false);
sync_service_.SetTransportState(
syncer::SyncService::TransportState::DISABLED);
ASSERT_TRUE(sync_service_.GetActiveDataTypes().empty());
// Save the above state to prefs.
RecordStateToPrefs();
base::HistogramTester histograms;
MaybeMigrateSyncingUserToSignedIn(fake_profile_dir_.GetPath(),
&pref_service_);
// The migration should not run since there's no signed-in user.
histograms.ExpectUniqueSample(
"Sync.SyncToSigninMigrationDecision",
/*SyncToSigninMigrationDecision::kDontMigrateNotSignedIn*/ 1, 1);
histograms.ExpectTotalCount("Sync.SyncToSigninMigrationOutcome", 0);
histograms.ExpectTotalCount("Sync.SyncToSigninMigrationTime", 0);
histograms.ExpectTotalCount(
"Sync.SyncToSigninMigrationDecision.DryRun.BOOKMARK", 0);
histograms.ExpectTotalCount(
"Sync.SyncToSigninMigrationDecision.DryRun.PASSWORD", 0);
histograms.ExpectTotalCount(
"Sync.SyncToSigninMigrationDecision.DryRun.READING_LIST", 0);
histograms.ExpectTotalCount(
"Sync.SyncToSigninMigrationDecision.Migration.BOOKMARK", 0);
histograms.ExpectTotalCount(
"Sync.SyncToSigninMigrationDecision.Migration.PASSWORD", 0);
histograms.ExpectTotalCount(
"Sync.SyncToSigninMigrationDecision.Migration.READING_LIST", 0);
}
TEST_P(SyncToSigninMigrationMetricsTest, SyncTransport) {
// There's no Sync consent, but otherwise everything is active (running in
// transport mode).
sync_service_.SetHasSyncConsent(false);
ASSERT_EQ(sync_service_.GetTransportState(),
syncer::SyncService::TransportState::ACTIVE);
ASSERT_TRUE(sync_service_.GetActiveDataTypes().HasAll(
{syncer::BOOKMARKS, syncer::PASSWORDS, syncer::READING_LIST}));
// Save the above state to prefs.
RecordStateToPrefs();
base::HistogramTester histograms;
MaybeMigrateSyncingUserToSignedIn(fake_profile_dir_.GetPath(),
&pref_service_);
// The migration should not run since this is not a Sync-the-feature user.
histograms.ExpectUniqueSample(
"Sync.SyncToSigninMigrationDecision",
/*SyncToSigninMigrationDecision::kDontMigrateNotSyncing*/ 2, 1);
histograms.ExpectTotalCount("Sync.SyncToSigninMigrationOutcome", 0);
histograms.ExpectTotalCount("Sync.SyncToSigninMigrationTime", 0);
histograms.ExpectTotalCount(
"Sync.SyncToSigninMigrationDecision.DryRun.BOOKMARK", 0);
histograms.ExpectTotalCount(
"Sync.SyncToSigninMigrationDecision.DryRun.PASSWORD", 0);
histograms.ExpectTotalCount(
"Sync.SyncToSigninMigrationDecision.DryRun.READING_LIST", 0);
histograms.ExpectTotalCount(
"Sync.SyncToSigninMigrationDecision.Migration.BOOKMARK", 0);
histograms.ExpectTotalCount(
"Sync.SyncToSigninMigrationDecision.Migration.PASSWORD", 0);
histograms.ExpectTotalCount(
"Sync.SyncToSigninMigrationDecision.Migration.READING_LIST", 0);
}
TEST_P(SyncToSigninMigrationMetricsTest, SyncPaused) {
sync_service_.SetPersistentAuthError();
ASSERT_EQ(sync_service_.GetTransportState(),
syncer::SyncService::TransportState::PAUSED);
ASSERT_TRUE(sync_service_.HasSyncConsent());
ASSERT_TRUE(sync_service_.GetActiveDataTypes().empty());
// Save the above state to prefs.
RecordStateToPrefs();
base::HistogramTester histograms;
MaybeMigrateSyncingUserToSignedIn(fake_profile_dir_.GetPath(),
&pref_service_);
// In the Sync-paused state, the overall migration should run, except if the
// feature flag is disabled.
int expected_decision =
IsMigrationEnabled()
? /*SyncToSigninMigrationDecision::kMigrate*/ 0
: /*SyncToSigninMigrationDecision::kDontMigrateFlagDisabled*/ 5;
histograms.ExpectUniqueSample("Sync.SyncToSigninMigrationDecision",
expected_decision, 1);
if (IsMigrationEnabled()) {
histograms.ExpectTotalCount("Sync.SyncToSigninMigrationOutcome", 1);
histograms.ExpectTotalCount("Sync.SyncToSigninMigrationTime", 1);
} else {
histograms.ExpectTotalCount("Sync.SyncToSigninMigrationOutcome", 0);
histograms.ExpectTotalCount("Sync.SyncToSigninMigrationTime", 0);
}
// However, the individual data types were by definition not active and so
// should not be migrated.
std::string infix = GetTypeDecisionHistogramInfix();
histograms.ExpectUniqueSample(
"Sync.SyncToSigninMigrationDecision." + infix + ".BOOKMARK",
/*SyncToSigninMigrationDataTypeDecision::kDontMigrateTypeNotActive*/ 2,
1);
histograms.ExpectUniqueSample(
"Sync.SyncToSigninMigrationDecision." + infix + ".PASSWORD",
/*SyncToSigninMigrationDataTypeDecision::kDontMigrateTypeNotActive*/ 2,
1);
histograms.ExpectUniqueSample(
"Sync.SyncToSigninMigrationDecision." + infix + ".READING_LIST",
/*SyncToSigninMigrationDataTypeDecision::kDontMigrateTypeNotActive*/ 2,
1);
}
TEST_P(SyncToSigninMigrationMetricsTest, SyncInitializing) {
sync_service_.SetTransportState(
syncer::SyncService::TransportState::INITIALIZING);
ASSERT_TRUE(sync_service_.HasSyncConsent());
ASSERT_TRUE(sync_service_.GetActiveDataTypes().empty());
// Save the above state to prefs.
RecordStateToPrefs();
base::HistogramTester histograms;
MaybeMigrateSyncingUserToSignedIn(fake_profile_dir_.GetPath(),
&pref_service_);
// The migration should not run, because Sync was still initializing.
histograms.ExpectUniqueSample(
"Sync.SyncToSigninMigrationDecision",
/*SyncToSigninMigrationDecision::kDontMigrateSyncStatusInitializing*/ 4,
1);
histograms.ExpectTotalCount("Sync.SyncToSigninMigrationOutcome", 0);
histograms.ExpectTotalCount("Sync.SyncToSigninMigrationTime", 0);
histograms.ExpectTotalCount(
"Sync.SyncToSigninMigrationDecision.DryRun.BOOKMARK", 0);
histograms.ExpectTotalCount(
"Sync.SyncToSigninMigrationDecision.DryRun.PASSWORD", 0);
histograms.ExpectTotalCount(
"Sync.SyncToSigninMigrationDecision.DryRun.READING_LIST", 0);
histograms.ExpectTotalCount(
"Sync.SyncToSigninMigrationDecision.Migration.BOOKMARK", 0);
histograms.ExpectTotalCount(
"Sync.SyncToSigninMigrationDecision.Migration.PASSWORD", 0);
histograms.ExpectTotalCount(
"Sync.SyncToSigninMigrationDecision.Migration.READING_LIST", 0);
}
INSTANTIATE_TEST_SUITE_P(,
SyncToSigninMigrationMetricsTest,
testing::Bool(),
[](const testing::TestParamInfo<bool>& info) {
return info.param ? "MigrationEnabled"
: "MigrationDisabled";
});
class SyncToSigninMigrationDataTypesTest : public SyncToSigninMigrationTestBase,
public testing::Test {
public:
SyncToSigninMigrationDataTypesTest()
: SyncToSigninMigrationTestBase(
/*migration_feature_enabled=*/true) {}
void SetUp() override {
// Everything is active.
ASSERT_EQ(sync_service_.GetTransportState(),
syncer::SyncService::TransportState::ACTIVE);
ASSERT_TRUE(sync_service_.HasSyncConsent());
ASSERT_TRUE(sync_service_.GetActiveDataTypes().HasAll(
{syncer::BOOKMARKS, syncer::PASSWORDS, syncer::READING_LIST}));
// Save the above state to prefs.
RecordStateToPrefs();
}
base::FilePath GetBookmarksLocalStorePath() const {
return fake_profile_dir_.GetPath().AppendASCII("Bookmarks");
}
base::FilePath GetBookmarksAccountStorePath() const {
return fake_profile_dir_.GetPath().AppendASCII("AccountBookmarks");
}
base::FilePath GetPasswordsLocalStorePath() const {
return fake_profile_dir_.GetPath().AppendASCII("Login Data");
}
base::FilePath GetPasswordsAccountStorePath() const {
return fake_profile_dir_.GetPath().AppendASCII("Login Data For Account");
}
};
TEST_F(SyncToSigninMigrationDataTypesTest, MoveBookmarks_BothExist) {
// Both bookmark stores exist on disk. The account store is empty, since it
// was unused pre-migration. This is the typical pre-migration state.
base::WriteFile(GetBookmarksLocalStorePath(), "local bookmarks");
base::WriteFile(GetBookmarksAccountStorePath(), "");
base::HistogramTester histograms;
MaybeMigrateSyncingUserToSignedIn(fake_profile_dir_.GetPath(),
&pref_service_);
// The local file should have been moved over the account one.
EXPECT_FALSE(base::PathExists(GetBookmarksLocalStorePath()));
EXPECT_TRUE(base::PathExists(GetBookmarksAccountStorePath()));
std::string account_contents;
ASSERT_TRUE(base::ReadFileToString(GetBookmarksAccountStorePath(),
&account_contents));
EXPECT_EQ(account_contents, "local bookmarks");
histograms.ExpectUniqueSample(
"Sync.SyncToSigninMigrationOutcome.BookmarksFileMove",
-base::File::FILE_OK, 1);
}
TEST_F(SyncToSigninMigrationDataTypesTest, MoveBookmarks_OnlyLocalExists) {
// Only the local store exists on disk; the account store doesn't. This is
// uncommon, but could happen upgrades directly from an old Chrome version
// that didn't have an account store yet.
base::WriteFile(GetBookmarksLocalStorePath(), "local bookmarks");
base::HistogramTester histograms;
MaybeMigrateSyncingUserToSignedIn(fake_profile_dir_.GetPath(),
&pref_service_);
// The local file should have been renamed to the account one.
EXPECT_FALSE(base::PathExists(GetBookmarksLocalStorePath()));
EXPECT_TRUE(base::PathExists(GetBookmarksAccountStorePath()));
std::string account_contents;
ASSERT_TRUE(base::ReadFileToString(GetBookmarksAccountStorePath(),
&account_contents));
EXPECT_EQ(account_contents, "local bookmarks");
histograms.ExpectUniqueSample(
"Sync.SyncToSigninMigrationOutcome.BookmarksFileMove",
-base::File::FILE_OK, 1);
}
TEST_F(SyncToSigninMigrationDataTypesTest, MoveBookmarks_OnlyAccountExists) {
// Only the account store exists on disk; the local store doesn't. This
// should be impossible in practice, except maybe in rare error cases.
base::WriteFile(GetBookmarksAccountStorePath(), "account bookmarks");
base::HistogramTester histograms;
MaybeMigrateSyncingUserToSignedIn(fake_profile_dir_.GetPath(),
&pref_service_);
// The migration shouldn't have done anything; the account store should still
// exist with the same contents.
EXPECT_FALSE(base::PathExists(GetBookmarksLocalStorePath()));
EXPECT_TRUE(base::PathExists(GetBookmarksAccountStorePath()));
std::string account_contents;
ASSERT_TRUE(base::ReadFileToString(GetBookmarksAccountStorePath(),
&account_contents));
EXPECT_EQ(account_contents, "account bookmarks");
histograms.ExpectUniqueSample(
"Sync.SyncToSigninMigrationOutcome.BookmarksFileMove",
-base::File::FILE_ERROR_NOT_FOUND, 1);
}
TEST_F(SyncToSigninMigrationDataTypesTest, MoveBookmarks_NoneExists) {
// Neither of the two stores exist on disk. This should be impossible in
// practice, except maybe in rare error cases.
base::HistogramTester histograms;
MaybeMigrateSyncingUserToSignedIn(fake_profile_dir_.GetPath(),
&pref_service_);
// The migration shouldn't have done anything; still neither of the stores
// should exist.
EXPECT_FALSE(base::PathExists(GetBookmarksLocalStorePath()));
EXPECT_FALSE(base::PathExists(GetBookmarksAccountStorePath()));
histograms.ExpectUniqueSample(
"Sync.SyncToSigninMigrationOutcome.BookmarksFileMove",
-base::File::FILE_ERROR_NOT_FOUND, 1);
}
#if BUILDFLAG(IS_POSIX)
TEST_F(SyncToSigninMigrationDataTypesTest, MoveBookmarks_FolderNotWritable) {
// Both bookmark stores exist on disk. The account store is empty, since it
// was unused pre-migration. This is the typical pre-migration state.
base::WriteFile(GetBookmarksLocalStorePath(), "local bookmarks");
base::WriteFile(GetBookmarksAccountStorePath(), "");
// However, the folder containing the files is (for some reason) not writable,
// so the move/rename can't actually happen. This should not happen in
// practice (if it does, Chrome will likely be very broken). This test mostly
// verifies that nothing catastrophic happens, e.g. no crash.
int mode = 0;
ASSERT_TRUE(
base::GetPosixFilePermissions(fake_profile_dir_.GetPath(), &mode));
mode &= ~base::FILE_PERMISSION_WRITE_BY_USER;
mode &= ~base::FILE_PERMISSION_WRITE_BY_GROUP;
mode &= ~base::FILE_PERMISSION_WRITE_BY_OTHERS;
ASSERT_TRUE(base::SetPosixFilePermissions(fake_profile_dir_.GetPath(), mode));
base::HistogramTester histograms;
MaybeMigrateSyncingUserToSignedIn(fake_profile_dir_.GetPath(),
&pref_service_);
// Nothing should have changed.
EXPECT_TRUE(base::PathExists(GetBookmarksLocalStorePath()));
EXPECT_TRUE(base::PathExists(GetBookmarksAccountStorePath()));
std::string local_contents;
ASSERT_TRUE(
base::ReadFileToString(GetBookmarksLocalStorePath(), &local_contents));
EXPECT_EQ(local_contents, "local bookmarks");
std::string account_contents;
ASSERT_TRUE(base::ReadFileToString(GetBookmarksAccountStorePath(),
&account_contents));
EXPECT_EQ(account_contents, "");
histograms.ExpectUniqueSample(
"Sync.SyncToSigninMigrationOutcome.BookmarksFileMove",
-base::File::FILE_ERROR_ACCESS_DENIED, 1);
}
#endif // BUILDFLAG(IS_POSIX)
TEST_F(SyncToSigninMigrationDataTypesTest, MovePasswords_BothExist) {
// Both password stores exist on disk. The account store is empty, since it
// was unused pre-migration. This is the typical pre-migration state.
base::WriteFile(GetPasswordsLocalStorePath(), "local passwords");
base::WriteFile(GetPasswordsAccountStorePath(), "");
base::HistogramTester histograms;
MaybeMigrateSyncingUserToSignedIn(fake_profile_dir_.GetPath(),
&pref_service_);
// The local file should have been moved over the account one.
EXPECT_FALSE(base::PathExists(GetPasswordsLocalStorePath()));
EXPECT_TRUE(base::PathExists(GetPasswordsAccountStorePath()));
std::string account_contents;
ASSERT_TRUE(base::ReadFileToString(GetPasswordsAccountStorePath(),
&account_contents));
EXPECT_EQ(account_contents, "local passwords");
histograms.ExpectUniqueSample(
"Sync.SyncToSigninMigrationOutcome.PasswordsFileMove",
-base::File::FILE_OK, 1);
}
TEST_F(SyncToSigninMigrationDataTypesTest, MovePasswords_OnlyLocalExists) {
// Only the local store exists on disk; the account store doesn't. This is
// uncommon, but could happen upgrades directly from an old Chrome version
// that didn't have an account store yet.
base::WriteFile(GetPasswordsLocalStorePath(), "local passwords");
base::HistogramTester histograms;
MaybeMigrateSyncingUserToSignedIn(fake_profile_dir_.GetPath(),
&pref_service_);
// The local file should have been renamed to the account one.
EXPECT_FALSE(base::PathExists(GetPasswordsLocalStorePath()));
EXPECT_TRUE(base::PathExists(GetPasswordsAccountStorePath()));
std::string account_contents;
ASSERT_TRUE(base::ReadFileToString(GetPasswordsAccountStorePath(),
&account_contents));
EXPECT_EQ(account_contents, "local passwords");
histograms.ExpectUniqueSample(
"Sync.SyncToSigninMigrationOutcome.PasswordsFileMove",
-base::File::FILE_OK, 1);
}
TEST_F(SyncToSigninMigrationDataTypesTest, MovePasswords_OnlyAccountExists) {
// Only the account store exists on disk; the local store doesn't. This
// should be impossible in practice, except maybe in rare error cases.
base::WriteFile(GetPasswordsAccountStorePath(), "account passwords");
base::HistogramTester histograms;
MaybeMigrateSyncingUserToSignedIn(fake_profile_dir_.GetPath(),
&pref_service_);
// The migration shouldn't have done anything; the account store should still
// exist with the same contents.
EXPECT_FALSE(base::PathExists(GetPasswordsLocalStorePath()));
EXPECT_TRUE(base::PathExists(GetPasswordsAccountStorePath()));
std::string account_contents;
ASSERT_TRUE(base::ReadFileToString(GetPasswordsAccountStorePath(),
&account_contents));
EXPECT_EQ(account_contents, "account passwords");
histograms.ExpectUniqueSample(
"Sync.SyncToSigninMigrationOutcome.PasswordsFileMove",
-base::File::FILE_ERROR_NOT_FOUND, 1);
}
TEST_F(SyncToSigninMigrationDataTypesTest, MovePasswords_NoneExists) {
// Neither of the two stores exist on disk. This should be impossible in
// practice, except maybe in rare error cases.
base::HistogramTester histograms;
MaybeMigrateSyncingUserToSignedIn(fake_profile_dir_.GetPath(),
&pref_service_);
// The migration shouldn't have done anything; still neither of the stores
// should exist.
EXPECT_FALSE(base::PathExists(GetPasswordsLocalStorePath()));
EXPECT_FALSE(base::PathExists(GetPasswordsAccountStorePath()));
histograms.ExpectUniqueSample(
"Sync.SyncToSigninMigrationOutcome.PasswordsFileMove",
-base::File::FILE_ERROR_NOT_FOUND, 1);
}
#if BUILDFLAG(IS_POSIX)
TEST_F(SyncToSigninMigrationDataTypesTest, MovePasswords_FolderNotWritable) {
// Both password stores exist on disk. The account store is empty, since it
// was unused pre-migration. This is the typical pre-migration state.
base::WriteFile(GetPasswordsLocalStorePath(), "local passwords");
base::WriteFile(GetPasswordsAccountStorePath(), "");
// However, the folder containing the files is (for some reason) not writable,
// so the move/rename can't actually happen. This should not happen in
// practice (if it does, Chrome will likely be very broken). This test mostly
// verifies that nothing catastrophic happens, e.g. no crash.
int mode = 0;
ASSERT_TRUE(
base::GetPosixFilePermissions(fake_profile_dir_.GetPath(), &mode));
mode &= ~base::FILE_PERMISSION_WRITE_BY_USER;
mode &= ~base::FILE_PERMISSION_WRITE_BY_GROUP;
mode &= ~base::FILE_PERMISSION_WRITE_BY_OTHERS;
ASSERT_TRUE(base::SetPosixFilePermissions(fake_profile_dir_.GetPath(), mode));
base::HistogramTester histograms;
MaybeMigrateSyncingUserToSignedIn(fake_profile_dir_.GetPath(),
&pref_service_);
// Nothing should have changed.
EXPECT_TRUE(base::PathExists(GetPasswordsLocalStorePath()));
EXPECT_TRUE(base::PathExists(GetPasswordsAccountStorePath()));
std::string local_contents;
ASSERT_TRUE(
base::ReadFileToString(GetPasswordsLocalStorePath(), &local_contents));
EXPECT_EQ(local_contents, "local passwords");
std::string account_contents;
ASSERT_TRUE(base::ReadFileToString(GetPasswordsAccountStorePath(),
&account_contents));
EXPECT_EQ(account_contents, "");
histograms.ExpectUniqueSample(
"Sync.SyncToSigninMigrationOutcome.PasswordsFileMove",
-base::File::FILE_ERROR_ACCESS_DENIED, 1);
}
#endif // BUILDFLAG(IS_POSIX)
// A test fixture that performs the SyncToSignin migration, then enables the
// "undo migration" feature.
class SyncToSigninMigrationUndoTest : public SyncToSigninMigrationTestBase,
public testing::Test {
public:
SyncToSigninMigrationUndoTest()
: SyncToSigninMigrationTestBase(
/*migration_feature_enabled=*/true) {}
void SetUp() override {
// Everything is active.
ASSERT_EQ(sync_service_.GetTransportState(),
syncer::SyncService::TransportState::ACTIVE);
ASSERT_TRUE(sync_service_.HasSyncConsent());
ASSERT_TRUE(sync_service_.GetActiveDataTypes().HasAll(
{syncer::BOOKMARKS, syncer::PASSWORDS, syncer::READING_LIST}));
// Save the above state to prefs.
RecordStateToPrefs();
// Run the migration, so that there is something to undo.
MaybeMigrateSyncingUserToSignedIn(fake_profile_dir_.GetPath(),
&pref_service_);
undo_feature_.InitAndEnableFeature(
switches::kUndoMigrationOfSyncingUserToSignedIn);
}
private:
base::test::ScopedFeatureList undo_feature_;
};
TEST_F(SyncToSigninMigrationUndoTest, UndoesMigration) {
// The user is in the migrated state - signed-in:
ASSERT_FALSE(
pref_service_.GetString(prefs::kGoogleServicesAccountId).empty());
ASSERT_EQ(pref_service_.GetString(prefs::kGoogleServicesAccountId),
sync_service_.GetAccountInfo().gaia);
// Not syncing:
ASSERT_FALSE(pref_service_.GetBoolean(prefs::kGoogleServicesConsentedToSync));
ASSERT_TRUE(
pref_service_.GetString(prefs::kGoogleServicesLastSyncingGaiaId).empty());
ASSERT_TRUE(pref_service_.GetString(prefs::kGoogleServicesLastSyncingUsername)
.empty());
#if !BUILDFLAG(IS_CHROMEOS_ASH)
ASSERT_FALSE(sync_prefs_->IsInitialSyncFeatureSetupComplete());
#endif
// Marked as "migrated":
ASSERT_EQ(pref_service_.GetString(
prefs::kGoogleServicesSyncingGaiaIdMigratedToSignedIn),
sync_service_.GetAccountInfo().gaia);
ASSERT_EQ(pref_service_.GetString(
prefs::kGoogleServicesSyncingUsernameMigratedToSignedIn),
sync_service_.GetAccountInfo().email);
// Trigger the "undo" migration.
MaybeMigrateSyncingUserToSignedIn(fake_profile_dir_.GetPath(),
&pref_service_);
// The migration should've been undone, and the user should be back in the
// "syncing" state.
ASSERT_FALSE(
pref_service_.GetString(prefs::kGoogleServicesAccountId).empty());
EXPECT_TRUE(pref_service_.GetBoolean(prefs::kGoogleServicesConsentedToSync));
EXPECT_TRUE(sync_prefs_->IsInitialSyncFeatureSetupComplete());
// The "last syncing user" prefs should also have been restored.
EXPECT_EQ(pref_service_.GetString(prefs::kGoogleServicesLastSyncingGaiaId),
sync_service_.GetAccountInfo().gaia);
EXPECT_EQ(pref_service_.GetString(prefs::kGoogleServicesLastSyncingUsername),
sync_service_.GetAccountInfo().email);
// And the "was migrated" prefs should've been cleared.
EXPECT_TRUE(
pref_service_
.GetString(prefs::kGoogleServicesSyncingGaiaIdMigratedToSignedIn)
.empty());
EXPECT_TRUE(
pref_service_
.GetString(prefs::kGoogleServicesSyncingUsernameMigratedToSignedIn)
.empty());
}
TEST_F(SyncToSigninMigrationUndoTest, Idempotent) {
// Trigger the "undo" migration.
MaybeMigrateSyncingUserToSignedIn(fake_profile_dir_.GetPath(),
&pref_service_);
// The user is now back in the "syncing" state.
ASSERT_FALSE(
pref_service_.GetString(prefs::kGoogleServicesAccountId).empty());
ASSERT_TRUE(pref_service_.GetBoolean(prefs::kGoogleServicesConsentedToSync));
ASSERT_TRUE(
pref_service_
.GetString(prefs::kGoogleServicesSyncingGaiaIdMigratedToSignedIn)
.empty());
// Take a copy of all current pref values, to verify that the second undo
// attempt doesn't modify any of them.
const base::Value::Dict all_prefs =
pref_service_.user_prefs_store()->GetValues();
// Trigger the (undo) migration again - it should have no further effect.
MaybeMigrateSyncingUserToSignedIn(fake_profile_dir_.GetPath(),
&pref_service_);
// The prefs should be unmodified.
EXPECT_EQ(pref_service_.user_prefs_store()->GetValues(), all_prefs);
}
TEST_F(SyncToSigninMigrationUndoTest, DoesNotUndoMigrationIfSignedOut) {
// The user is in the "migrated" state - signed-in, not syncing, marked as
// migrated.
ASSERT_FALSE(
pref_service_.GetString(prefs::kGoogleServicesAccountId).empty());
ASSERT_FALSE(pref_service_.GetBoolean(prefs::kGoogleServicesConsentedToSync));
ASSERT_FALSE(
pref_service_
.GetString(prefs::kGoogleServicesSyncingGaiaIdMigratedToSignedIn)
.empty());
// The account gets signed out.
pref_service_.ClearPref(prefs::kGoogleServicesAccountId);
// Trigger the "undo" migration.
MaybeMigrateSyncingUserToSignedIn(fake_profile_dir_.GetPath(),
&pref_service_);
// The migration should NOT have been undone, since the account isn't signed
// in anymore.
ASSERT_TRUE(pref_service_.GetString(prefs::kGoogleServicesAccountId).empty());
EXPECT_FALSE(pref_service_.GetBoolean(prefs::kGoogleServicesConsentedToSync));
}
TEST_F(SyncToSigninMigrationUndoTest, DoesNotUndoMigrationIfDiffentAccount) {
// The user is in the "migrated" state - signed-in, not syncing, marked as
// migrated.
ASSERT_FALSE(
pref_service_.GetString(prefs::kGoogleServicesAccountId).empty());
ASSERT_FALSE(pref_service_.GetBoolean(prefs::kGoogleServicesConsentedToSync));
ASSERT_FALSE(
pref_service_
.GetString(prefs::kGoogleServicesSyncingGaiaIdMigratedToSignedIn)
.empty());
// The account gets signed out, and a different account signed in.
pref_service_.SetString(prefs::kGoogleServicesAccountId, "different_gaia");
ASSERT_NE(pref_service_.GetString(prefs::kGoogleServicesAccountId),
pref_service_.GetString(
prefs::kGoogleServicesSyncingGaiaIdMigratedToSignedIn));
// Trigger the "undo" migration.
MaybeMigrateSyncingUserToSignedIn(fake_profile_dir_.GetPath(),
&pref_service_);
// The migration should NOT have been undone, since a different account is
// signed in now.
ASSERT_EQ(pref_service_.GetString(prefs::kGoogleServicesAccountId),
"different_gaia");
EXPECT_FALSE(pref_service_.GetBoolean(prefs::kGoogleServicesConsentedToSync));
}
} // namespace
} // namespace browser_sync