chromium/chrome/browser/password_manager/android/password_manager_android_util_unittest.cc

// 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 "chrome/browser/password_manager/android/password_manager_android_util.h"

#include <limits>
#include <memory>

#include "base/android/build_info.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/memory/scoped_refptr.h"
#include "base/run_loop.h"
#include "base/scoped_observation.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/to_string.h"
#include "base/task/bind_post_task.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_file_util.h"
#include "build/build_config.h"
#include "chrome/browser/password_manager/account_password_store_factory.h"
#include "chrome/browser/password_manager/profile_password_store_factory.h"
#include "chrome/browser/prefs/browser_prefs.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
#include "chrome/browser/sync/sync_service_factory.h"
#include "chrome/browser/trusted_vault/trusted_vault_service_factory.h"
#include "chrome/test/base/scoped_testing_local_state.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "components/browser_sync/sync_to_signin_migration.h"
#include "components/keyed_service/core/service_access_type.h"
#include "components/password_manager/core/browser/features/password_features.h"
#include "components/password_manager/core/browser/password_form.h"
#include "components/password_manager/core/browser/password_manager_constants.h"
#include "components/password_manager/core/browser/password_manager_test_utils.h"
#include "components/password_manager/core/browser/password_store/login_database.h"
#include "components/password_manager/core/browser/password_store/password_store_backend.h"
#include "components/password_manager/core/browser/password_store/password_store_built_in_backend.h"
#include "components/password_manager/core/browser/password_store/password_store_interface.h"
#include "components/password_manager/core/browser/password_store/password_store_results_observer.h"
#include "components/password_manager/core/browser/password_store_factory_util.h"
#include "components/password_manager/core/browser/split_stores_and_local_upm.h"
#include "components/password_manager/core/common/password_manager_pref_names.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_notifier_impl.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/testing_pref_service.h"
#include "components/prefs/testing_pref_store.h"
#include "components/signin/public/base/consent_level.h"
#include "components/sync/base/command_line_switches.h"
#include "components/sync/base/data_type.h"
#include "components/sync/base/pref_names.h"
#include "components/sync/service/sync_prefs.h"
#include "components/sync/service/sync_service.h"
#include "components/sync/service/sync_service_impl.h"
#include "components/sync/service/sync_user_settings.h"
#include "components/sync/test/fake_server.h"
#include "components/sync/test/fake_server_http_post_provider.h"
#include "components/sync/test/fake_server_network_resources.h"
#include "components/sync/test/test_sync_service.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"

using password_manager::GetLocalUpmMinGmsVersion;
using password_manager::UsesSplitStoresAndUPMForLocal;
using password_manager::prefs::kPasswordsUseUPMLocalAndSeparateStores;
using password_manager::prefs::UseUpmLocalAndSeparateStoresState;
using password_manager::prefs::UseUpmLocalAndSeparateStoresState::kOff;
using password_manager::prefs::UseUpmLocalAndSeparateStoresState::
    kOffAndMigrationPending;
using password_manager::prefs::UseUpmLocalAndSeparateStoresState::kOn;
using password_manager_android_util::PasswordAccessLossWarningType;

namespace password_manager_android_util {
namespace {

// Duplicated from password_manager_android_util.cc, which is fine since the
// enum values should never change.
enum class ActivationError {
  // The user was activated or the local passwords/settings migration was
  // scheduled if one is needed.
  kNone = 0,
  kUnenrolled = 1,
  kInitialUpmMigrationMissing = 2,
  kLoginDbFileMoveFailed = 3,
  kOutdatedGmsCore = 4,
  kMigrationWarningUnacknowledged = 6,
  kMax = kMigrationWarningUnacknowledged,
};

password_manager::PasswordForm MakeExampleForm() {
  password_manager::PasswordForm form;
  form.signon_realm = "https://g.com";
  form.url = GURL(form.signon_realm);
  form.username_value = u"username";
  form.password_value = u"password";
  return form;
}

class SyncDataTypeActiveWaiter : public syncer::SyncServiceObserver {
 public:
  SyncDataTypeActiveWaiter(syncer::SyncService* sync_service,
                           syncer::DataType data_type)
      : sync_service_(sync_service), data_type_(data_type) {}
  SyncDataTypeActiveWaiter(const SyncDataTypeActiveWaiter&) = delete;
  SyncDataTypeActiveWaiter& operator=(const SyncDataTypeActiveWaiter&) = delete;
  ~SyncDataTypeActiveWaiter() override = default;

  [[nodiscard]] bool Wait() {
    observation_.Observe(sync_service_);
    run_loop_.Run();
    // OnStateChanged() resets `observation_` if successful.
    return !observation_.IsObserving();
  }

 private:
  // syncer::SyncServiceObserver overrides.
  void OnStateChanged(syncer::SyncService* service) override {
    if (service->GetActiveDataTypes().Has(data_type_)) {
      observation_.Reset();
      run_loop_.Quit();
    }
  }

  const raw_ptr<syncer::SyncService> sync_service_;
  const syncer::DataType data_type_;
  base::ScopedObservation<syncer::SyncService, syncer::SyncServiceObserver>
      observation_{this};
  base::RunLoop run_loop_;
};

class PasswordManagerAndroidUtilTest : public testing::Test {
 public:
  PasswordManagerAndroidUtilTest() {
    pref_service_.registry()->RegisterBooleanPref(
        password_manager::prefs::kUnenrolledFromGoogleMobileServicesDueToErrors,
        false);
    pref_service_.registry()->RegisterIntegerPref(
        password_manager::prefs::kCurrentMigrationVersionToGoogleMobileServices,
        0);
    pref_service_.registry()->RegisterIntegerPref(
        password_manager::prefs::kPasswordsUseUPMLocalAndSeparateStores,
        static_cast<int>(kOff));
    pref_service_.registry()->RegisterBooleanPref(
        password_manager::prefs::kCredentialsEnableService, false);
    pref_service_.registry()->RegisterBooleanPref(
        password_manager::prefs::kCredentialsEnableAutosignin, false);
    pref_service_.registry()->RegisterBooleanPref(
        password_manager::prefs::kEmptyProfileStoreLoginDatabase, false);
    pref_service_.registry()->RegisterBooleanPref(
        syncer::prefs::internal::kSyncInitialSyncFeatureSetupComplete, false);
    pref_service_.registry()->RegisterBooleanPref(
        syncer::prefs::internal::kSyncKeepEverythingSynced, false);
    pref_service_.registry()->RegisterBooleanPref(
        base::StrCat({syncer::prefs::internal::
                          kSyncDataTypeStatusForSyncToSigninMigrationPrefix,
                      ".",
                      syncer::GetDataTypeLowerCaseRootTag(syncer::PASSWORDS)}),
        false);
    pref_service_.registry()->RegisterBooleanPref(
        password_manager::prefs::
            kUserAcknowledgedLocalPasswordsMigrationWarning,
        false);
    pref_service_.registry()->RegisterBooleanPref(
        password_manager::prefs::kSettingsMigratedToUPMLocal, false);

    SetPasswordSyncEnabledPref(false);
    base::WriteFile(login_db_directory_.Append(
                        password_manager::kLoginDataForProfileFileName),
                    "");

    // Most tests check the modern GmsCore case.
    base::android::BuildInfo::GetInstance()->set_gms_version_code_for_test(
        base::NumberToString(GetLocalUpmMinGmsVersion()));
  }

  // SetUsesSplitStoresAndUPMForLocal() reads whether password sync is enabled
  // from a pref rather than the SyncService. This helper sets such pref.
  void SetPasswordSyncEnabledPref(bool enabled) {
    if (enabled) {
      pref_service_.SetBoolean(
          syncer::prefs::internal::kSyncInitialSyncFeatureSetupComplete, true);
      pref_service_.SetBoolean(
          syncer::prefs::internal::kSyncKeepEverythingSynced, true);
      pref_service_.SetBoolean(
          base::StrCat(
              {syncer::prefs::internal::
                   kSyncDataTypeStatusForSyncToSigninMigrationPrefix,
               ".", syncer::GetDataTypeLowerCaseRootTag(syncer::PASSWORDS)}),
          true);
      ASSERT_EQ(browser_sync::GetSyncToSigninMigrationDataTypeDecision(
                    &pref_service_, syncer::PASSWORDS,
                    syncer::prefs::internal::kSyncPasswords),
                browser_sync::SyncToSigninMigrationDataTypeDecision::kMigrate);
    } else {
      pref_service_.SetBoolean(
          syncer::prefs::internal::kSyncInitialSyncFeatureSetupComplete, false);
      ASSERT_EQ(browser_sync::GetSyncToSigninMigrationDataTypeDecision(
                    &pref_service_, syncer::PASSWORDS,
                    syncer::prefs::internal::kSyncPasswords),
                browser_sync::SyncToSigninMigrationDataTypeDecision::
                    kDontMigrateTypeDisabled);
    }
  }

  TestingPrefServiceSimple* pref_service() { return &pref_service_; }

  const base::FilePath& login_db_directory() { return login_db_directory_; }

 private:
  TestingPrefServiceSimple pref_service_;
  const base::FilePath login_db_directory_ =
      base::CreateUniqueTempDirectoryScopedToTest();
};

TEST_F(PasswordManagerAndroidUtilTest,
       ShouldUseUpmWiringFalseWhenNotSyncingAndSplitStoresOff) {
  syncer::TestSyncService sync_service;
  sync_service.SetSignedOut();

  EXPECT_FALSE(ShouldUseUpmWiring(&sync_service, pref_service()));
}

TEST_F(PasswordManagerAndroidUtilTest,
       ShouldUseUpmWiringFalseWhenNotSyncingAndSplitStoresMigrationPending) {
  syncer::TestSyncService sync_service;
  sync_service.SetSignedOut();
  pref_service()->SetInteger(
      password_manager::prefs::kPasswordsUseUPMLocalAndSeparateStores,
      static_cast<int>(
          password_manager::prefs::UseUpmLocalAndSeparateStoresState::
              kOffAndMigrationPending));

  EXPECT_FALSE(ShouldUseUpmWiring(&sync_service, pref_service()));
}

TEST_F(PasswordManagerAndroidUtilTest,
       ShouldUseUpmWiringTrueWhenNotSyncingAndSplitStoresOn) {
  syncer::TestSyncService sync_service;
  sync_service.SetSignedOut();
  pref_service()->SetInteger(
      password_manager::prefs::kPasswordsUseUPMLocalAndSeparateStores,
      static_cast<int>(kOn));

  EXPECT_TRUE(ShouldUseUpmWiring(&sync_service, pref_service()));
}

TEST_F(PasswordManagerAndroidUtilTest,
       ShouldUseUpmWiringTrueWhenNotSyncingAndSplitStoresEnabledAndUnenrolled) {
  syncer::TestSyncService sync_service;
  sync_service.SetSignedOut();
  pref_service()->SetInteger(
      password_manager::prefs::kPasswordsUseUPMLocalAndSeparateStores,
      static_cast<int>(kOn));

  pref_service()->SetBoolean(
      password_manager::prefs::kUnenrolledFromGoogleMobileServicesDueToErrors,
      true);

  EXPECT_TRUE(ShouldUseUpmWiring(&sync_service, pref_service()));
}

TEST_F(PasswordManagerAndroidUtilTest,
       ShouldUseUpmWiringFalseWhenSyncingAndUnenrolled) {
  syncer::TestSyncService sync_service;
  sync_service.SetSignedIn(signin::ConsentLevel::kSync);
  pref_service()->SetBoolean(
      password_manager::prefs::kUnenrolledFromGoogleMobileServicesDueToErrors,
      true);

  EXPECT_FALSE(ShouldUseUpmWiring(&sync_service, pref_service()));
}

TEST_F(PasswordManagerAndroidUtilTest,
       ShouldUseUpmWiringTrueWhenSyncingAndSplitStoresDisabled) {
  syncer::TestSyncService sync_service;
  sync_service.SetSignedIn(signin::ConsentLevel::kSync);

  EXPECT_TRUE(ShouldUseUpmWiring(&sync_service, pref_service()));
}

TEST_F(PasswordManagerAndroidUtilTest,
       ShouldUseUpmWiringTrueWhenSyncingAndSplitStoresEnabled) {
  syncer::TestSyncService sync_service;
  sync_service.SetSignedIn(signin::ConsentLevel::kSync);
  pref_service()->SetInteger(
      password_manager::prefs::kPasswordsUseUPMLocalAndSeparateStores,
      static_cast<int>(kOn));

  EXPECT_TRUE(ShouldUseUpmWiring(&sync_service, pref_service()));
}

TEST_F(PasswordManagerAndroidUtilTest,
       ShouldUseUpmWiringTrueWhenSignedInWithoutSyncAndSplitStoresDisabled) {
  syncer::TestSyncService sync_service;
  sync_service.SetSignedIn(signin::ConsentLevel::kSignin);
  pref_service()->SetInteger(
      password_manager::prefs::kPasswordsUseUPMLocalAndSeparateStores,
      static_cast<int>(kOff));

  EXPECT_TRUE(ShouldUseUpmWiring(&sync_service, pref_service()));
}

TEST_F(PasswordManagerAndroidUtilTest,
       ShouldUseUpmWiringTrueWhenSignedInWithoutSyncAndSplitStoresEnabled) {
  syncer::TestSyncService sync_service;
  sync_service.SetSignedIn(signin::ConsentLevel::kSignin);
  pref_service()->SetInteger(
      password_manager::prefs::kPasswordsUseUPMLocalAndSeparateStores,
      static_cast<int>(kOn));

  EXPECT_TRUE(ShouldUseUpmWiring(&sync_service, pref_service()));
}

TEST_F(
    PasswordManagerAndroidUtilTest,
    SetUsesSplitStoresAndUPMForLocal_SignedOutWithNoPasswordsAndDefaultSettings) {
  auto histogram_tester = std::make_unique<base::HistogramTester>();
  pref_service()->SetBoolean(
      password_manager::prefs::kEmptyProfileStoreLoginDatabase, true);
  ASSERT_EQ(pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
            static_cast<int>(kOff));

  SetUsesSplitStoresAndUPMForLocal(pref_service(), login_db_directory());

  // The user got activated.
  EXPECT_EQ(pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
            static_cast<int>(kOn));
  histogram_tester->ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationError.NonSyncingNoMigration",
      ActivationError::kNone, 1);
  histogram_tester->ExpectUniqueSample("PasswordManager.LocalUpmActivated",
                                       true, 1);
  histogram_tester->ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationStatus", kOn, 1);
  histogram_tester = std::make_unique<base::HistogramTester>();

  pref_service()->SetBoolean(
      password_manager::prefs::kCredentialsEnableAutosignin, false);
  SetUsesSplitStoresAndUPMForLocal(pref_service(), login_db_directory());

  // After activation, next calls are no-ops, even if settings are customized.
  // The histogram is now recorded for NonSyncingWithMigration though, which is
  // a bit misleading.
  EXPECT_EQ(pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
            static_cast<int>(kOn));
  histogram_tester->ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationError.NonSyncingWithMigration",
      ActivationError::kNone, 1);
  histogram_tester->ExpectUniqueSample("PasswordManager.LocalUpmActivated",
                                       true, 1);
  histogram_tester->ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationStatus", kOn, 1);
}

TEST_F(PasswordManagerAndroidUtilTest,
       SetUsesSplitStoresAndUPMForLocal_SignedOutNoPasswordsAndCustomSettings) {
  auto histogram_tester = std::make_unique<base::HistogramTester>();
  pref_service()->SetBoolean(
      password_manager::prefs::kEmptyProfileStoreLoginDatabase, true);
  pref_service()->SetBoolean(password_manager::prefs::kCredentialsEnableService,
                             false);
  pref_service()->SetBoolean(
      password_manager::prefs::kUserAcknowledgedLocalPasswordsMigrationWarning,
      false);
  ASSERT_EQ(pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
            static_cast<int>(kOff));

  SetUsesSplitStoresAndUPMForLocal(pref_service(), login_db_directory());

  // The migration is pending.
  // Even though the migration warning was not acknowledged, the migration
  // should happen because there are no local passwords. Only settings should be
  // migrated.
  EXPECT_EQ(pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
            static_cast<int>(kOffAndMigrationPending));
  histogram_tester->ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationError.NonSyncingWithMigration",
      ActivationError::kNone, 1);
  histogram_tester->ExpectUniqueSample("PasswordManager.LocalUpmActivated",
                                       false, 1);
  histogram_tester->ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationStatus", kOffAndMigrationPending, 1);
}

TEST_F(PasswordManagerAndroidUtilTest,
       SetUsesSplitStoresAndUPMForLocal_SignedOutWithFreshProfile) {
  base::HistogramTester histogram_tester;
  // kEmptyProfileStoreLoginDatabase is false, so in principle there would be
  // local passwords to migrate. But actually the pref is just in its default
  // state. This is a fresh profile without a DB file.
  base::DeleteFile(login_db_directory().Append(
      password_manager::kLoginDataForProfileFileName));
  const PrefService::Preference* no_passwords_pref =
      pref_service()->FindPreference(
          password_manager::prefs::kEmptyProfileStoreLoginDatabase);
  ASSERT_TRUE(no_passwords_pref->IsDefaultValue());
  ASSERT_EQ(no_passwords_pref->GetValue()->GetBool(), false);
  ASSERT_EQ(pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
            static_cast<int>(kOff));

  SetUsesSplitStoresAndUPMForLocal(pref_service(), login_db_directory());

  // User got activated.
  EXPECT_EQ(pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
            static_cast<int>(kOn));
  histogram_tester.ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationError.NonSyncingNoMigration",
      ActivationError::kNone, 1);
  histogram_tester.ExpectUniqueSample("PasswordManager.LocalUpmActivated", true,
                                      1);
  histogram_tester.ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationStatus", kOn, 1);
}

TEST_F(PasswordManagerAndroidUtilTest,
       SetUsesSplitStoresAndUPMForLocal_SignedOutWithPasswords) {
  auto histogram_tester = std::make_unique<base::HistogramTester>();
  pref_service()->SetBoolean(
      password_manager::prefs::kEmptyProfileStoreLoginDatabase, false);
  ASSERT_EQ(pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
            static_cast<int>(kOff));

  SetUsesSplitStoresAndUPMForLocal(pref_service(), login_db_directory());

  if (!base::android::BuildInfo::GetInstance()->is_automotive()) {
    // The migration warning was not acknowledged so the migration attempt
    // fails.
    EXPECT_EQ(
        pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
        static_cast<int>(kOff));
    histogram_tester->ExpectUniqueSample(
        "PasswordManager.LocalUpmActivationError.NonSyncingWithMigration",
        ActivationError::kMigrationWarningUnacknowledged, 1);
    histogram_tester->ExpectUniqueSample("PasswordManager.LocalUpmActivated",
                                         false, 1);
    histogram_tester->ExpectUniqueSample(
        "PasswordManager.LocalUpmActivationStatus", kOff, 1);
    histogram_tester = std::make_unique<base::HistogramTester>();
    pref_service()->SetBoolean(
        password_manager::prefs::
            kUserAcknowledgedLocalPasswordsMigrationWarning,
        true);

    // Try again.
    SetUsesSplitStoresAndUPMForLocal(pref_service(), login_db_directory());
  } else {
    // On Android Auto, the migration warning is not shown, so acknowledging is
    // not required.
  }

  // The migration got marked as pending (but the user is not considered
  // activated).
  EXPECT_EQ(pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
            static_cast<int>(kOffAndMigrationPending));
  histogram_tester->ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationError.NonSyncingWithMigration",
      ActivationError::kNone, 1);
  histogram_tester->ExpectUniqueSample("PasswordManager.LocalUpmActivated",
                                       false, 1);
  histogram_tester->ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationStatus", kOffAndMigrationPending, 1);
  histogram_tester = std::make_unique<base::HistogramTester>();

  SetUsesSplitStoresAndUPMForLocal(pref_service(), login_db_directory());

  // The first migration didn't finish/succeed, so a new migration is scheduled.
  EXPECT_EQ(pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
            static_cast<int>(kOffAndMigrationPending));
  histogram_tester->ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationError.NonSyncingWithMigration",
      ActivationError::kNone, 1);
  histogram_tester->ExpectUniqueSample("PasswordManager.LocalUpmActivated",
                                       false, 1);
  histogram_tester->ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationStatus", kOffAndMigrationPending, 1);
  histogram_tester = std::make_unique<base::HistogramTester>();

  pref_service()->SetInteger(kPasswordsUseUPMLocalAndSeparateStores,
                             static_cast<int>(kOn));
  SetUsesSplitStoresAndUPMForLocal(pref_service(), login_db_directory());

  // The migration finished successfully, the user is activated, so next calls
  // are no-ops.
  EXPECT_EQ(pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
            static_cast<int>(kOn));
  histogram_tester->ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationError.NonSyncingWithMigration",
      ActivationError::kNone, 1);
  histogram_tester->ExpectUniqueSample("PasswordManager.LocalUpmActivated",
                                       true, 1);
  histogram_tester->ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationStatus", kOn, 1);
}

// Tests that acknowledging the migration warning is no longer required for
// migration.
TEST_F(PasswordManagerAndroidUtilTest,
       SetUsesSplitStoresAndUPMForLocal_SkipMigrationWarningAcknowledgement) {
  base::test::ScopedFeatureList scoped_feature_list;
  scoped_feature_list.InitAndDisableFeature(
      password_manager::features::
          kUnifiedPasswordManagerLocalPasswordsMigrationWarning);
  auto histogram_tester = std::make_unique<base::HistogramTester>();
  pref_service()->SetBoolean(
      password_manager::prefs::kEmptyProfileStoreLoginDatabase, false);
  ASSERT_EQ(pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
            static_cast<int>(kOff));

  SetUsesSplitStoresAndUPMForLocal(pref_service(), login_db_directory());

  // The migration got marked as pending (but the user is not considered
  // activated).
  EXPECT_EQ(pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
            static_cast<int>(kOffAndMigrationPending));
  histogram_tester->ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationError.NonSyncingWithMigration",
      ActivationError::kNone, 1);
  histogram_tester->ExpectUniqueSample("PasswordManager.LocalUpmActivated",
                                       false, 1);
  histogram_tester->ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationStatus", kOffAndMigrationPending, 1);
  histogram_tester = std::make_unique<base::HistogramTester>();
}

TEST_F(
    PasswordManagerAndroidUtilTest,
    SetUsesSplitStoresAndUPMForLocal_SignedOutWithCustomEnableServiceSetting) {
  auto histogram_tester = std::make_unique<base::HistogramTester>();
  pref_service()->SetBoolean(password_manager::prefs::kCredentialsEnableService,
                             false);
  pref_service()->SetBoolean(
      password_manager::prefs::kUserAcknowledgedLocalPasswordsMigrationWarning,
      true);
  ASSERT_EQ(pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
            static_cast<int>(kOff));

  SetUsesSplitStoresAndUPMForLocal(pref_service(), login_db_directory());

  // The migration got marked as pending (but the user is not considered
  // activated yet).
  EXPECT_EQ(pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
            static_cast<int>(kOffAndMigrationPending));
  histogram_tester->ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationError.NonSyncingWithMigration",
      ActivationError::kNone, 1);
  histogram_tester->ExpectUniqueSample("PasswordManager.LocalUpmActivated",
                                       false, 1);
  histogram_tester->ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationStatus", kOffAndMigrationPending, 1);
}

TEST_F(PasswordManagerAndroidUtilTest,
       SetUsesSplitStoresAndUPMForLocal_SignedOutWithCustomAutoSigninSetting) {
  auto histogram_tester = std::make_unique<base::HistogramTester>();
  pref_service()->SetBoolean(
      password_manager::prefs::kCredentialsEnableAutosignin, false);
  pref_service()->SetBoolean(
      password_manager::prefs::kUserAcknowledgedLocalPasswordsMigrationWarning,
      true);
  ASSERT_EQ(pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
            static_cast<int>(kOff));

  SetUsesSplitStoresAndUPMForLocal(pref_service(), login_db_directory());

  // The migration got marked as pending (but the user is not considered
  // activated yet).
  EXPECT_EQ(pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
            static_cast<int>(kOffAndMigrationPending));
  histogram_tester->ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationError.NonSyncingWithMigration",
      ActivationError::kNone, 1);
  histogram_tester->ExpectUniqueSample("PasswordManager.LocalUpmActivated",
                                       false, 1);
  histogram_tester->ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationStatus", kOffAndMigrationPending, 1);
}

TEST_F(PasswordManagerAndroidUtilTest,
       SetUsesSplitStoresAndUPMForLocal_KeepMigrationPendingIfSyncEnabled) {
  // Set up a user who was signed out with saved passwords (thus got into
  // kOffAndMigrationPending), failed to migrate (thus stayed in
  // kOffAndMigrationPending) and later enabled sync.
  // kLoginDataForAccountFileName exists because the account store was created
  // when the migration got scheduled, even if it was never used.
  base::HistogramTester histogram_tester;
  pref_service()->SetInteger(kPasswordsUseUPMLocalAndSeparateStores,
                             static_cast<int>(kOffAndMigrationPending));
  pref_service()->SetBoolean(
      password_manager::prefs::kEmptyProfileStoreLoginDatabase, false);
  SetPasswordSyncEnabledPref(true);
  base::WriteFile(login_db_directory().Append(
                      password_manager::kLoginDataForAccountFileName),
                  "");
  ASSERT_TRUE(base::PathExists(login_db_directory().Append(
      password_manager::kLoginDataForProfileFileName)));
  ASSERT_TRUE(base::PathExists(login_db_directory().Append(
      password_manager::kLoginDataForAccountFileName)));

  SetUsesSplitStoresAndUPMForLocal(pref_service(), login_db_directory());

  // The browser should keep trying to migrate existing passwords to the *local*
  // Android backend. The login database files should be untouched.
  EXPECT_EQ(pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
            static_cast<int>(kOffAndMigrationPending));
  EXPECT_TRUE(base::PathExists(login_db_directory().Append(
      password_manager::kLoginDataForProfileFileName)));
  EXPECT_TRUE(base::PathExists(login_db_directory().Append(
      password_manager::kLoginDataForAccountFileName)));
  histogram_tester.ExpectUniqueSample("PasswordManager.LocalUpmActivated",
                                      false, 1);
  histogram_tester.ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationStatus", kOffAndMigrationPending, 1);

  // Advanced case: deactivate too, by downgrading Gmscore.
  base::android::BuildInfo::GetInstance()->set_gms_version_code_for_test(
      base::NumberToString(GetLocalUpmMinGmsVersion() - 1));
  SetUsesSplitStoresAndUPMForLocal(pref_service(), login_db_directory());

  // kOn syncing users that get rolled back will "undo" the login DB file move,
  // i.e. they replace the "profile" loginDB with the "account" one. This isn't
  // always perfect, see comment MaybeDeactivateSplitStoresAndLocalUpm(). The
  // "account" DB might even be empty and overwrite a non-empty "profile" one.
  // However: for kOffAndMigrationPending users, the "account" DB is *surely*
  // empty (password sync is suppressed). So replacing the file can only be
  // worse. Instead, the DB files should just be untouched. The account one is
  // empty anyway, so no data is leftover.
  EXPECT_EQ(pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
            static_cast<int>(kOff));
  EXPECT_TRUE(base::PathExists(login_db_directory().Append(
      password_manager::kLoginDataForProfileFileName)));
  EXPECT_TRUE(base::PathExists(login_db_directory().Append(
      password_manager::kLoginDataForAccountFileName)));
  histogram_tester.ExpectUniqueSample("PasswordManager.LocalUpmActivated",
                                      false, 2);
  histogram_tester.ExpectBucketCount("PasswordManager.LocalUpmActivationStatus",
                                     kOff, 1);
}

TEST_F(PasswordManagerAndroidUtilTest,
       SetUsesSplitStoresAndUPMForLocal_SyncingHealthy) {
  auto histogram_tester = std::make_unique<base::HistogramTester>();
  SetPasswordSyncEnabledPref(true);
  pref_service()->SetInteger(
      password_manager::prefs::kCurrentMigrationVersionToGoogleMobileServices,
      1);
  // Custom password manager settings should not matter for syncing users.
  pref_service()->SetBoolean(
      password_manager::prefs::kCredentialsEnableAutosignin, false);
  ASSERT_EQ(pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
            static_cast<int>(kOff));
  ASSERT_TRUE(base::PathExists(login_db_directory().Append(
      password_manager::kLoginDataForProfileFileName)));
  ASSERT_FALSE(base::PathExists(login_db_directory().Append(
      password_manager::kLoginDataForAccountFileName)));

  SetUsesSplitStoresAndUPMForLocal(pref_service(), login_db_directory());

  // The user should've been activated and the profile DB file should've become
  // the account DB file.
  EXPECT_EQ(pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
            static_cast<int>(kOn));
  EXPECT_FALSE(base::PathExists(login_db_directory().Append(
      password_manager::kLoginDataForProfileFileName)));
  EXPECT_TRUE(base::PathExists(login_db_directory().Append(
      password_manager::kLoginDataForAccountFileName)));
  histogram_tester->ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationError.Syncing", ActivationError::kNone,
      1);
  histogram_tester->ExpectUniqueSample("PasswordManager.LocalUpmActivated",
                                       true, 1);
  histogram_tester->ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationStatus", kOn, 1);
}

TEST_F(PasswordManagerAndroidUtilTest,
       SetUsesSplitStoresAndUPMForLocal_StaysActivatedIfEnabledSyncLater) {
  // Set up a user that got activated while being signed out and later enabled
  // sync, but didn't have kCurrentMigrationVersionToGoogleMobileServices set
  // (that pref is part of a migration logic that's no longer triggered when
  // the local UPM is activated).
  base::HistogramTester histogram_tester;
  SetPasswordSyncEnabledPref(true);
  ASSERT_EQ(pref_service()->GetInteger(
                password_manager::prefs::
                    kCurrentMigrationVersionToGoogleMobileServices),
            0);
  pref_service()->SetInteger(kPasswordsUseUPMLocalAndSeparateStores,
                             static_cast<int>(kOn));

  SetUsesSplitStoresAndUPMForLocal(pref_service(), login_db_directory());

  // The histogram records for "Syncing", which is a bit misleading.
  EXPECT_EQ(pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
            static_cast<int>(kOn));
  histogram_tester.ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationError.Syncing", ActivationError::kNone,
      1);
  histogram_tester.ExpectUniqueSample("PasswordManager.LocalUpmActivated", true,
                                      1);
  histogram_tester.ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationStatus", kOn, 1);
}

TEST_F(PasswordManagerAndroidUtilTest,
       SetUsesSplitStoresAndUPMForLocal_DeactivatingSyncUserMovesDBFile) {
  // Set up a healthy syncing user that got previously activated.
  pref_service()->SetInteger(kPasswordsUseUPMLocalAndSeparateStores,
                             static_cast<int>(kOn));
  SetPasswordSyncEnabledPref(true);
  pref_service()->SetInteger(
      password_manager::prefs::kCurrentMigrationVersionToGoogleMobileServices,
      1);
  base::WriteFile(login_db_directory().Append(
                      password_manager::kLoginDataForAccountFileName),
                  "");
  ASSERT_TRUE(base::PathExists(login_db_directory().Append(
      password_manager::kLoginDataForProfileFileName)));
  ASSERT_TRUE(base::PathExists(login_db_directory().Append(
      password_manager::kLoginDataForAccountFileName)));
  base::android::BuildInfo::GetInstance()->set_gms_version_code_for_test(
      base::NumberToString(GetLocalUpmMinGmsVersion() - 1));

  SetUsesSplitStoresAndUPMForLocal(pref_service(), login_db_directory());

  // Downgrading GmsCore undoes the process, including the file move.
  EXPECT_EQ(pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
            static_cast<int>(kOff));
  EXPECT_TRUE(base::PathExists(login_db_directory().Append(
      password_manager::kLoginDataForProfileFileName)));
  EXPECT_FALSE(base::PathExists(login_db_directory().Append(
      password_manager::kLoginDataForAccountFileName)));
}

TEST_F(
    PasswordManagerAndroidUtilTest,
    SetUsesSplitStoresAndUPMForLocal_OldGmsNotActivatedIfSignedOutWithoutPasswords) {
  base::HistogramTester histogram_tester;
  base::android::BuildInfo::GetInstance()->set_gms_version_code_for_test(
      base::NumberToString(GetLocalUpmMinGmsVersion() - 1));
  pref_service()->SetBoolean(
      password_manager::prefs::kEmptyProfileStoreLoginDatabase, true);

  SetUsesSplitStoresAndUPMForLocal(pref_service(), login_db_directory());

  EXPECT_EQ(pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
            static_cast<int>(kOff));
  histogram_tester.ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationError.NonSyncingNoMigration",
      ActivationError::kOutdatedGmsCore, 1);
  histogram_tester.ExpectUniqueSample("PasswordManager.LocalUpmActivated",
                                      false, 1);
  histogram_tester.ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationStatus", kOff, 1);
}

TEST_F(
    PasswordManagerAndroidUtilTest,
    SetUsesSplitStoresAndUPMForLocal_OldGmsNotActivatedIfSignedOutWithPasswords) {
  base::HistogramTester histogram_tester;
  base::android::BuildInfo::GetInstance()->set_gms_version_code_for_test(
      base::NumberToString(GetLocalUpmMinGmsVersion() - 1));
  pref_service()->SetBoolean(
      password_manager::prefs::kEmptyProfileStoreLoginDatabase, false);
  pref_service()->SetBoolean(
      password_manager::prefs::kUserAcknowledgedLocalPasswordsMigrationWarning,
      true);

  SetUsesSplitStoresAndUPMForLocal(pref_service(), login_db_directory());

  histogram_tester.ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationError.NonSyncingWithMigration",
      ActivationError::kOutdatedGmsCore, 1);
  histogram_tester.ExpectUniqueSample("PasswordManager.LocalUpmActivated",
                                      false, 1);
  histogram_tester.ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationStatus", kOff, 1);
}

TEST_F(PasswordManagerAndroidUtilTest,
       SetUsesSplitStoresAndUPMForLocal_OldGmsNotActivatedIfSyncing) {
  base::HistogramTester histogram_tester;
  base::android::BuildInfo::GetInstance()->set_gms_version_code_for_test(
      base::NumberToString(GetLocalUpmMinGmsVersion() - 1));
  SetPasswordSyncEnabledPref(true);
  pref_service()->SetInteger(
      password_manager::prefs::kCurrentMigrationVersionToGoogleMobileServices,
      1);

  SetUsesSplitStoresAndUPMForLocal(pref_service(), login_db_directory());

  histogram_tester.ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationError.Syncing",
      ActivationError::kOutdatedGmsCore, 1);
  histogram_tester.ExpectUniqueSample("PasswordManager.LocalUpmActivated",
                                      false, 1);
  histogram_tester.ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationStatus", kOff, 1);
}

TEST_F(PasswordManagerAndroidUtilTest,
       SetUsesSplitStoresAndUPMForLocal_DowngradingGmsCoreDeactivates) {
  base::HistogramTester histogram_tester;
  base::android::BuildInfo::GetInstance()->set_gms_version_code_for_test(
      base::NumberToString(GetLocalUpmMinGmsVersion() - 1));
  pref_service()->SetInteger(kPasswordsUseUPMLocalAndSeparateStores,
                             static_cast<int>(kOn));
  pref_service()->SetBoolean(
      password_manager::prefs::kEmptyProfileStoreLoginDatabase, true);

  SetUsesSplitStoresAndUPMForLocal(pref_service(), login_db_directory());

  EXPECT_EQ(pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
            static_cast<int>(kOff));
  histogram_tester.ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationError.NonSyncingNoMigration",
      ActivationError::kOutdatedGmsCore, 1);
  histogram_tester.ExpectUniqueSample("PasswordManager.LocalUpmActivated",
                                      false, 1);
  histogram_tester.ExpectUniqueSample(
      "PasswordManager.LocalUpmActivationStatus", kOff, 1);
}

TEST_F(PasswordManagerAndroidUtilTest,
       SetUsesSplitStoresAndUPMForLocal_DowngradingGmsCoreCancelsMigration) {
  // In the past the migration got scheduled, but never finished. GmsCore has
  // since been downgraded.
  base::android::BuildInfo::GetInstance()->set_gms_version_code_for_test(
      base::NumberToString(GetLocalUpmMinGmsVersion() - 1));
  pref_service()->SetInteger(kPasswordsUseUPMLocalAndSeparateStores,
                             static_cast<int>(kOffAndMigrationPending));
  pref_service()->SetBoolean(
      password_manager::prefs::kEmptyProfileStoreLoginDatabase, false);
  pref_service()->SetBoolean(
      password_manager::prefs::kUserAcknowledgedLocalPasswordsMigrationWarning,
      true);

  SetUsesSplitStoresAndUPMForLocal(pref_service(), login_db_directory());

  // Migration should have been canceled.
  EXPECT_EQ(pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
            static_cast<int>(kOff));
}

TEST_F(PasswordManagerAndroidUtilTest,
       SetUsesSplitStoresAndUPMForLocal_DeletesLoginDataFilesForMigratedUsers) {
  base::test::ScopedFeatureList feature_list(
      password_manager::features::kClearLoginDatabaseForAllMigratedUPMUsers);

  base::HistogramTester histogram_tester;
  const char kRemovalStatusProfileMetric[] =
      "PasswordManager.ProfileLoginData.RemovalStatus";
  const char kRemovalStatusAccountMetric[] =
      "PasswordManager.AccountLoginData.RemovalStatus";

  // This is a state of a local user that has just been migrated.
  pref_service()->SetInteger(kPasswordsUseUPMLocalAndSeparateStores,
                             static_cast<int>(kOn));
  pref_service()->SetBoolean(
      password_manager::prefs::kUserAcknowledgedLocalPasswordsMigrationWarning,
      true);
  pref_service()->SetBoolean(
      password_manager::prefs::kEmptyProfileStoreLoginDatabase, false);

  // Creating the login data files for testing.
  base::FilePath profile_db_path = login_db_directory().Append(
      password_manager::kLoginDataForProfileFileName);
  base::FilePath account_db_path = login_db_directory().Append(
      password_manager::kLoginDataForAccountFileName);
  base::FilePath profile_db_journal_path = login_db_directory().Append(
      password_manager::kLoginDataJournalForProfileFileName);
  base::FilePath account_db_journal_path = login_db_directory().Append(
      password_manager::kLoginDataJournalForAccountFileName);

  base::WriteFile(profile_db_path, "Test content");
  base::WriteFile(account_db_path, "Test content");
  base::WriteFile(profile_db_journal_path, "Test content");
  base::WriteFile(account_db_journal_path, "Test content");

  EXPECT_TRUE(PathExists(profile_db_path));
  EXPECT_TRUE(PathExists(account_db_path));
  EXPECT_TRUE(PathExists(profile_db_journal_path));
  EXPECT_TRUE(PathExists(account_db_journal_path));

  SetUsesSplitStoresAndUPMForLocal(pref_service(), login_db_directory());

  // The user wasn't deactivated, so the login data file should have been
  // cleared because the user was already migrated to UPM with split stores.
  EXPECT_EQ(pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
            static_cast<int>(kOn));
  EXPECT_FALSE(PathExists(profile_db_path));
  EXPECT_FALSE(PathExists(account_db_path));
  EXPECT_FALSE(PathExists(profile_db_journal_path));
  EXPECT_FALSE(PathExists(account_db_journal_path));
  EXPECT_TRUE(pref_service()->GetBoolean(
      password_manager::prefs::kEmptyProfileStoreLoginDatabase));

  histogram_tester.ExpectUniqueSample(kRemovalStatusProfileMetric, true, 1);
  histogram_tester.ExpectUniqueSample(kRemovalStatusAccountMetric, true, 1);
}

TEST_F(
    PasswordManagerAndroidUtilTest,
    SetUsesSplitStoresAndUPMForLocal_NoLoginDataFilesCreatedForDeactivatedAccountUsers) {
  // This test simulated a case when the GMS Core version was manually
  // downgraded after UPM activation.
  base::test::ScopedFeatureList enable_clearing_flag{
      password_manager::features::kClearLoginDatabaseForAllMigratedUPMUsers};
  // In this test UPM should get deactivated because of low GMS Core version.
  base::android::BuildInfo::GetInstance()->set_gms_version_code_for_test(
      base::NumberToString(GetLocalUpmMinGmsVersion() - 1));

  // The initial state of the test is that the user is activated for UPM with
  // split stores and the login data files were deleted.
  pref_service()->SetInteger(kPasswordsUseUPMLocalAndSeparateStores,
                             static_cast<int>(kOn));
  pref_service()->SetBoolean(
      password_manager::prefs::kEmptyProfileStoreLoginDatabase, false);

  base::FilePath profile_db_path = login_db_directory().Append(
      password_manager::kLoginDataForProfileFileName);
  base::FilePath account_db_path = login_db_directory().Append(
      password_manager::kLoginDataForAccountFileName);

  base::DeleteFile(profile_db_path);
  base::DeleteFile(account_db_path);

  SetUsesSplitStoresAndUPMForLocal(pref_service(), login_db_directory());

  // The user was deactivated and there are still no login data files.
  EXPECT_EQ(pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
            static_cast<int>(kOff));
  EXPECT_FALSE(PathExists(profile_db_path));
  EXPECT_FALSE(PathExists(account_db_path));
}

// Integration test for UsesSplitStoresAndUPMForLocal(), which emulates restarts
// by creating and destroying TestingProfiles. This doesn't exercise any of the
// Java layers.
// TODO(b/324196888): Replace with PRE_ AndroidBrowserTests when those
// are supported, preferably using a FakePasswordStoreAndroidBackend.
class UsesSplitStoresAndUPMForLocalTest : public ::testing::Test {
 public:
  UsesSplitStoresAndUPMForLocalTest() {
    base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
        syncer::kSyncDeferredStartupTimeoutSeconds, "0");
    // Override the GMS version to be big enough for local UPM support, so these
    // tests still pass in bots with an outdated version.
    base::android::BuildInfo::GetInstance()->set_gms_version_code_for_test(
        base::NumberToString(GetLocalUpmMinGmsVersion()));
  }

  // Can be invoked more than once, calling DestroyProfile() in-between.
  // Most of the relevant sync/passwords state is kept between calls.
  void CreateProfile() {
    ASSERT_FALSE(profile_) << "Call DestroyProfile() first";

    // Use a fixed profile path, so files like the LoginDBs are kept.
    TestingProfile::Builder builder;
    builder.SetPath(profile_path_);

    // Similarly, use a fixed `user_pref_store_`.
    scoped_refptr<user_prefs::PrefRegistrySyncable> pref_registry =
        base::MakeRefCounted<user_prefs::PrefRegistrySyncable>();
    RegisterUserProfilePrefs(pref_registry.get());
    builder.SetPrefService(
        std::make_unique<sync_preferences::TestingPrefServiceSyncable>(
            base::MakeRefCounted<TestingPrefStore>(),
            base::MakeRefCounted<TestingPrefStore>(),
            base::MakeRefCounted<TestingPrefStore>(),
            base::MakeRefCounted<TestingPrefStore>(),
            /*user_pref_store=*/user_pref_store_,
            base::MakeRefCounted<TestingPrefStore>(), pref_registry,
            std::make_unique<PrefNotifierImpl>()));

    // Add the real factories for Sync/Passwords but not the IdentityManager,
    // which is harder to control.
    builder.AddTestingFactories(IdentityTestEnvironmentProfileAdaptor::
                                    GetIdentityTestEnvironmentFactories());
    builder.AddTestingFactories(
        {TestingProfile::TestingFactory{
             TrustedVaultServiceFactory::GetInstance(),
             TrustedVaultServiceFactory::GetDefaultFactory()},
         // Unretained() is safe because `this` outlives `profile_`.
         TestingProfile::TestingFactory{
             SyncServiceFactory::GetInstance(),
             base::BindRepeating(
                 &UsesSplitStoresAndUPMForLocalTest::BuildSyncService,
                 base::Unretained(this))}});
    profile_ = builder.Build();

    SetUpPasswordStores(profile_.get());

    // `identity_test_env_adaptor_` is initialized lazily with the SyncService,
    // force it to happen now.
    ASSERT_FALSE(identity_test_env_adaptor_);
    sync_service();
    ASSERT_TRUE(identity_test_env_adaptor_);
  }

  void SetUpPasswordStores(Profile* profile) {
    // This block of tests is designed to test the behavior of login database
    // (namely that the profile database file is renamed to be the account
    // database file when using the split stores feature).
    std::unique_ptr<password_manager::LoginDatabase> login_db(
        password_manager::CreateLoginDatabaseForProfileStorage(
            profile->GetPath(), profile->GetPrefs()));
    password_manager::LoginDatabase* login_db_ptr = login_db.get();
    std::unique_ptr<password_manager::PasswordStoreBackend> profile_backend =
        std::make_unique<password_manager::PasswordStoreBuiltInBackend>(
            std::move(login_db),
            syncer::WipeModelUponSyncDisabledBehavior::kNever,
            profile->GetPrefs());
    auto is_db_empty_cb =
        base::BindPostTaskToCurrentDefault(base::BindRepeating(
            &password_manager::IntermediateCallbackForSettingPrefs,
            profile_backend->AsWeakPtr(),
            base::BindRepeating(
                &password_manager::SetEmptyStorePref, profile->GetPrefs(),
                password_manager::prefs::kEmptyProfileStoreLoginDatabase)));
    login_db_ptr->SetIsEmptyCb(std::move(is_db_empty_cb));
    ProfilePasswordStoreFactory::GetInstance()->SetTestingFactory(
        profile,
        base::BindRepeating(
            &password_manager::BuildPasswordStoreWithArgs<
                content::BrowserContext, password_manager::PasswordStore,
                std::unique_ptr<password_manager::PasswordStoreBackend>>,
            base::Passed(std::move(profile_backend))));

    std::unique_ptr<password_manager::PasswordStoreBackend> account_backend =
        std::make_unique<password_manager::PasswordStoreBuiltInBackend>(
            password_manager::CreateLoginDatabaseForAccountStorage(
                profile->GetPath(), profile->GetPrefs()),
            syncer::WipeModelUponSyncDisabledBehavior::kAlways,
            profile->GetPrefs());
    AccountPasswordStoreFactory::GetInstance()->SetTestingFactory(
        profile,
        base::BindRepeating(
            &password_manager::BuildPasswordStoreWithArgs<
                content::BrowserContext, password_manager::PasswordStore,
                std::unique_ptr<password_manager::PasswordStoreBackend>>,
            base::Passed(std::move(account_backend))));
  }

  void DestroyProfile() {
    ASSERT_TRUE(profile_) << "Call CreateProfile() first";

    task_environment_.RunUntilIdle();
    identity_test_env_adaptor_.reset();
    profile_.reset();
  }

  std::unique_ptr<KeyedService> BuildSyncService(
      content::BrowserContext* context) {
    identity_test_env_adaptor_ =
        std::make_unique<IdentityTestEnvironmentProfileAdaptor>(
            Profile::FromBrowserContext(context));
    if (signed_in_) {
      // The refresh token is not persisted in the test, so set it again before
      // creating the SyncService.
      identity_test_env_adaptor_->identity_test_env()
          ->SetRefreshTokenForPrimaryAccount();
    }

    std::unique_ptr<KeyedService> sync_service =
        SyncServiceFactory::GetDefaultFactory().Run(context);
    static_cast<syncer::SyncServiceImpl*>(sync_service.get())
        ->OverrideNetworkForTest(
            fake_server::CreateFakeServerHttpPostProviderFactory(
                fake_server_.AsWeakPtr()));
    return sync_service;
  }

  void SignInAndEnableSync() {
    ASSERT_TRUE(identity_test_env_adaptor_);
    signin::IdentityTestEnvironment* env =
        identity_test_env_adaptor_->identity_test_env();
    ASSERT_FALSE(env->identity_manager()->HasPrimaryAccount(
        signin::ConsentLevel::kSync));
    env->SetAutomaticIssueOfAccessTokens(true);
    env->MakePrimaryAccountAvailable("foo@gmail.com",
                                     signin::ConsentLevel::kSync);
    signed_in_ = true;

    // Sync few types to avoid setting up dependencies for most of them.
    std::unique_ptr<syncer::SyncSetupInProgressHandle> handle =
        sync_service()->GetSetupInProgressHandle();
    sync_service()->GetUserSettings()->SetSelectedTypes(
        /*sync_everything=*/false, {syncer::UserSelectableType::kPreferences,
                                    syncer::UserSelectableType::kPasswords});
    sync_service()->GetUserSettings()->SetInitialSyncFeatureSetupComplete(
        syncer::SyncFirstSetupCompleteSource::BASIC_FLOW);
  }

  syncer::SyncService* sync_service() {
    return SyncServiceFactory::GetForProfile(profile_.get());
  }

  password_manager::PasswordStoreInterface* profile_password_store() {
    return ProfilePasswordStoreFactory::GetForProfile(
               profile_.get(), ServiceAccessType::IMPLICIT_ACCESS)
        .get();
  }

  password_manager::PasswordStoreInterface* account_password_store() {
    return AccountPasswordStoreFactory::GetForProfile(
               profile_.get(), ServiceAccessType::IMPLICIT_ACCESS)
        .get();
  }

  PrefService* pref_service() { return profile_->GetPrefs(); }

 private:
  content::BrowserTaskEnvironment task_environment_;
  const base::FilePath profile_path_ =
      base::CreateUniqueTempDirectoryScopedToTest();
  const scoped_refptr<TestingPrefStore> user_pref_store_ =
      base::MakeRefCounted<TestingPrefStore>();
  const ScopedTestingLocalState local_state_ =
      ScopedTestingLocalState(TestingBrowserProcess::GetGlobal());
  fake_server::FakeServer fake_server_;
  std::unique_ptr<TestingProfile> profile_;
  std::unique_ptr<IdentityTestEnvironmentProfileAdaptor>
      identity_test_env_adaptor_;
  bool signed_in_ = false;
};

TEST_F(UsesSplitStoresAndUPMForLocalTest, SignedOutWithoutPasswords) {
  {
    // Prevent activation on the first run by faking an outdated GmsCore.
    base::android::BuildInfo::GetInstance()->set_gms_version_code_for_test(
        base::NumberToString(GetLocalUpmMinGmsVersion() - 1));
    CreateProfile();
    ASSERT_FALSE(UsesSplitStoresAndUPMForLocal(pref_service()));
    DestroyProfile();
  }

  {
    // Now GmsCore was upgraded and activation can proceed.
    base::android::BuildInfo::GetInstance()->set_gms_version_code_for_test(
        base::NumberToString(GetLocalUpmMinGmsVersion()));
    CreateProfile();
    EXPECT_TRUE(UsesSplitStoresAndUPMForLocal(pref_service()));
    DestroyProfile();
  }
}

TEST_F(UsesSplitStoresAndUPMForLocalTest, SignedOutWithPasswords) {
  {
    // Set up a signed-out user, with saved passwords, who already acknowledged
    // the migration warning. Prevent activation before the passwords are added,
    // by faking an outdated GmsCore.
    base::android::BuildInfo::GetInstance()->set_gms_version_code_for_test(
        base::NumberToString(GetLocalUpmMinGmsVersion() - 1));
    CreateProfile();
    profile_password_store()->AddLogin(MakeExampleForm());
    ASSERT_FALSE(UsesSplitStoresAndUPMForLocal(pref_service()));
    pref_service()->SetBoolean(
        password_manager::prefs::
            kUserAcknowledgedLocalPasswordsMigrationWarning,
        true);
    DestroyProfile();
  }

  {
    // Now GmsCore was upgraded, so the migration gets scheduled.
    base::android::BuildInfo::GetInstance()->set_gms_version_code_for_test(
        base::NumberToString(GetLocalUpmMinGmsVersion()));
    CreateProfile();

    // Until the migration finishes, UsesSplitStoresAndUPMForLocal() should be
    // false and password sync should be suppressed.
    ASSERT_FALSE(UsesSplitStoresAndUPMForLocal(pref_service()));
    SignInAndEnableSync();
    ASSERT_TRUE(
        SyncDataTypeActiveWaiter(sync_service(), syncer::PREFERENCES).Wait());
    ASSERT_FALSE(sync_service()->GetUserSettings()->GetSelectedTypes().Has(
        syncer::UserSelectableType::kPasswords));

    // Pretend the migration finished.
    // TODO(b/324196888): Once the migration is implemented, make this a
    // call to a fake instead of directly setting the pref.
    pref_service()->SetInteger(
        password_manager::prefs::kPasswordsUseUPMLocalAndSeparateStores,
        static_cast<int>(kOn));
    EXPECT_TRUE(UsesSplitStoresAndUPMForLocal(pref_service()));
    ASSERT_TRUE(
        SyncDataTypeActiveWaiter(sync_service(), syncer::PASSWORDS).Wait());

    DestroyProfile();
  }
}

TEST_F(UsesSplitStoresAndUPMForLocalTest, SyncingHealthy) {
  {
    // Prevent activation before sync is enabled, by faking an outdated GmsCore.
    base::android::BuildInfo::GetInstance()->set_gms_version_code_for_test(
        base::NumberToString(GetLocalUpmMinGmsVersion() - 1));
    CreateProfile();
    profile_password_store()->AddLogin(MakeExampleForm());
    SignInAndEnableSync();
    ASSERT_TRUE(
        SyncDataTypeActiveWaiter(sync_service(), syncer::PASSWORDS).Wait());
    pref_service()->SetInteger(
        password_manager::prefs::kCurrentMigrationVersionToGoogleMobileServices,
        1);
    ASSERT_FALSE(UsesSplitStoresAndUPMForLocal(pref_service()));
    DestroyProfile();
  }

  {
    // Now GmsCore was upgraded and activation can proceed.
    base::android::BuildInfo::GetInstance()->set_gms_version_code_for_test(
        base::NumberToString(GetLocalUpmMinGmsVersion()));
    CreateProfile();
    ASSERT_TRUE(
        SyncDataTypeActiveWaiter(sync_service(), syncer::PASSWORDS).Wait());
    EXPECT_TRUE(UsesSplitStoresAndUPMForLocal(pref_service()));
    // Passwords in the profile store must have moved to the account store.
    password_manager::PasswordStoreResultsObserver profile_store_observer;
    password_manager::PasswordStoreResultsObserver account_store_observer;
    profile_password_store()->GetAllLogins(profile_store_observer.GetWeakPtr());
    account_password_store()->GetAllLogins(account_store_observer.GetWeakPtr());
    EXPECT_EQ(profile_store_observer.WaitForResults().size(), 0u);
    EXPECT_EQ(account_store_observer.WaitForResults().size(), 1u);
    DestroyProfile();
  }
}

TEST_F(UsesSplitStoresAndUPMForLocalTest, SyncingButUnenrolledAndM4Enabled) {
  // Test setup where one password was saved to profile store and user is
  // unenrolled from UPM.
  {
    // Prevent activation before sync is enabled, by faking an outdated GmsCore.
    base::android::BuildInfo::GetInstance()->set_gms_version_code_for_test(
        base::NumberToString(GetLocalUpmMinGmsVersion() - 1));
    CreateProfile();
    profile_password_store()->AddLogin(MakeExampleForm());
    SignInAndEnableSync();
    ASSERT_TRUE(
        SyncDataTypeActiveWaiter(sync_service(), syncer::PASSWORDS).Wait());
    pref_service()->SetBoolean(
        password_manager::prefs::kUnenrolledFromGoogleMobileServicesDueToErrors,
        true);
    pref_service()->SetInteger(
        password_manager::prefs::kCurrentMigrationVersionToGoogleMobileServices,
        1);
    ASSERT_FALSE(UsesSplitStoresAndUPMForLocal(pref_service()));
    DestroyProfile();
  }

  {
    // Now GmsCore was upgraded and activation can proceed.
    base::android::BuildInfo::GetInstance()->set_gms_version_code_for_test(
        base::NumberToString(GetLocalUpmMinGmsVersion()));
    CreateProfile();

    // The migration is pending.
    EXPECT_EQ(
        pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
        static_cast<int>(kOffAndMigrationPending));

    // Passwords is still in the profile, it was not moved to account even
    // though user was syncing.
    password_manager::PasswordStoreResultsObserver profile_store_observer;
    password_manager::PasswordStoreResultsObserver account_store_observer;
    profile_password_store()->GetAllLogins(profile_store_observer.GetWeakPtr());
    account_password_store()->GetAllLogins(account_store_observer.GetWeakPtr());
    EXPECT_EQ(profile_store_observer.WaitForResults().size(), 1u);
    EXPECT_EQ(account_store_observer.WaitForResults().size(), 0u);
    DestroyProfile();
  }
}

TEST_F(UsesSplitStoresAndUPMForLocalTest,
       SyncingButNoInitialUPMMigrationAndM4Enabled) {
  // Test setup where one password was saved to profile store and user is
  // enrolled into original UPM.
  {
    // Prevent activation before sync is enabled, by faking an outdated GmsCore.
    base::android::BuildInfo::GetInstance()->set_gms_version_code_for_test(
        base::NumberToString(GetLocalUpmMinGmsVersion() - 1));
    CreateProfile();
    profile_password_store()->AddLogin(MakeExampleForm());
    SignInAndEnableSync();
    ASSERT_TRUE(
        SyncDataTypeActiveWaiter(sync_service(), syncer::PASSWORDS).Wait());
    pref_service()->SetInteger(
        password_manager::prefs::kCurrentMigrationVersionToGoogleMobileServices,
        0);
    ASSERT_FALSE(UsesSplitStoresAndUPMForLocal(pref_service()));
    DestroyProfile();
  }

  {
    // Now GmsCore was upgraded and activation can proceed.
    base::android::BuildInfo::GetInstance()->set_gms_version_code_for_test(
        base::NumberToString(GetLocalUpmMinGmsVersion()));
    CreateProfile();

    // The migration is pending.
    EXPECT_EQ(
        pref_service()->GetInteger(kPasswordsUseUPMLocalAndSeparateStores),
        static_cast<int>(kOffAndMigrationPending));

    // Passwords is still in the profile, it was not moved to account even
    // though user was syncing.
    password_manager::PasswordStoreResultsObserver profile_store_observer;
    password_manager::PasswordStoreResultsObserver account_store_observer;
    profile_password_store()->GetAllLogins(profile_store_observer.GetWeakPtr());
    account_password_store()->GetAllLogins(account_store_observer.GetWeakPtr());
    EXPECT_EQ(profile_store_observer.WaitForResults().size(), 1u);
    EXPECT_EQ(account_store_observer.WaitForResults().size(), 0u);
    DestroyProfile();
  }
}

struct GetPasswordAccessLossWarningTypeTestCase {
  std::string test_case_desc;
  std::string gms_core_version;
  bool migration_attempted;
  bool local_passwords_migration_failed;
  bool profile_store_password_count;
  bool is_auto;
  PasswordAccessLossWarningType expected_result;
};

class GetPasswordAccessLossWarningTypeTest
    : public PasswordManagerAndroidUtilTest,
      public testing::WithParamInterface<
          GetPasswordAccessLossWarningTypeTestCase> {
 protected:
  void SetUp() override {
    GetPasswordAccessLossWarningTypeTestCase test_case = GetParam();

    int use_upm_and_separate_stores = 0;
    if (!test_case.migration_attempted) {
      use_upm_and_separate_stores = static_cast<int>(kOff);
    } else if (test_case.local_passwords_migration_failed) {
      use_upm_and_separate_stores = static_cast<int>(kOffAndMigrationPending);
    } else {
      use_upm_and_separate_stores = static_cast<int>(kOn);
    }
    pref_service()->SetInteger(
        password_manager::prefs::kPasswordsUseUPMLocalAndSeparateStores,
        use_upm_and_separate_stores);
    pref_service()->registry()->RegisterIntegerPref(
        password_manager::prefs::kTotalPasswordsAvailableForProfile,
        test_case.profile_store_password_count);
  }
};

TEST_P(GetPasswordAccessLossWarningTypeTest, GetPasswordAccessLossWarningType) {
  if (base::android::BuildInfo::GetInstance()->is_automotive() !=
      GetParam().is_auto) {
    GTEST_SKIP() << "Automotive tests don't need to run on non-auto devices "
                    "and vice-versa.";
  }

  // This call is needed to set the variable whether the migration is failed.
  SetUsesSplitStoresAndUPMForLocal(pref_service(), login_db_directory());

  base::android::BuildInfo::GetInstance()->set_gms_version_code_for_test(
      GetParam().gms_core_version);
  PasswordAccessLossWarningType result =
      GetPasswordAccessLossWarningType(pref_service());

  EXPECT_EQ(GetParam().expected_result, result);
}

INSTANTIATE_TEST_SUITE_P(
    All,
    GetPasswordAccessLossWarningTypeTest,
    testing::Values(
        GetPasswordAccessLossWarningTypeTestCase(
            /*test_case_desc=*/"NoGmsNoPwds",
            /*gms_core_version=*/"",
            /*migration_attempted=*/false,
            /*local_passwords_migration_failed=*/false,
            /*profile_store_password_count=*/0,
            /*is_auto=*/false,
            /*expected_result=*/PasswordAccessLossWarningType::kNone),
        GetPasswordAccessLossWarningTypeTestCase(
            /*test_case_desc=*/"NoGmsButPwds",
            /*gms_core_version=*/"",
            /*migration_attempted=*/false,
            /*local_passwords_migration_failed=*/false,
            /*profile_store_password_count=*/10,
            /*is_auto=*/false,
            /*expected_result=*/PasswordAccessLossWarningType::kNoGmsCore),
        GetPasswordAccessLossWarningTypeTestCase(
            /*test_case_desc=*/"NoUpmNoPwds",
            /*gms_core_version=*/"222912000",
            /*migration_attempted=*/false,
            /*local_passwords_migration_failed=*/false,
            /*profile_store_password_count=*/0,
            /*is_auto=*/false,
            /*expected_result=*/PasswordAccessLossWarningType::kNone),
        GetPasswordAccessLossWarningTypeTestCase(
            /*test_case_desc=*/"NoUpmButPwds",
            /*gms_core_version=*/"222912000",
            /*migration_attempted=*/false,
            /*local_passwords_migration_failed=*/false,
            /*profile_store_password_count=*/10,
            /*is_auto=*/false,
            /*expected_result=*/PasswordAccessLossWarningType::kNoUpm),
        GetPasswordAccessLossWarningTypeTestCase(
            /*test_case_desc=*/"AccountGmsNoPwds",
            /*gms_core_version=*/"223012000",
            /*migration_attempted=*/false,
            /*local_passwords_migration_failed=*/false,
            /*profile_store_password_count=*/0,
            /*is_auto=*/false,
            /*expected_result=*/PasswordAccessLossWarningType::kNone),
        GetPasswordAccessLossWarningTypeTestCase(
            /*test_case_desc=*/"AccountGmsLocalPwds",
            /*gms_core_version=*/"223012000",
            /*migration_attempted=*/true,
            /*local_passwords_migration_failed=*/false,
            /*profile_store_password_count=*/10,
            /*is_auto=*/false,
            /*expected_result=*/PasswordAccessLossWarningType::kOnlyAccountUpm),
        GetPasswordAccessLossWarningTypeTestCase(
            /*test_case_desc=*/"MigrationFailed",
            /*gms_core_version=*/"240212000",
            /*migration_attempted=*/true,
            /*local_passwords_migration_failed=*/true,
            /*profile_store_password_count=*/10,
            /*is_auto=*/false,
            /*expected_result=*/
            PasswordAccessLossWarningType::kNewGmsCoreMigrationFailed),
        GetPasswordAccessLossWarningTypeTestCase(
            /*test_case_desc=*/"MigrationSucceeded",
            /*gms_core_version=*/"240212000",
            /*migration_attempted=*/true,
            /*local_passwords_migration_failed=*/false,
            /*profile_store_password_count=*/10,
            /*is_auto=*/false,
            /*expected_result=*/PasswordAccessLossWarningType::kNone),
        GetPasswordAccessLossWarningTypeTestCase(
            /*test_case_desc=*/"AccountGmsLocalPwdsAuto",
            /*gms_core_version=*/"241412000",
            /*migration_attempted=*/false,
            /*local_passwords_migration_failed=*/false,
            /*profile_store_password_count=*/10,
            /*is_auto=*/true,
            /*expected_result=*/PasswordAccessLossWarningType::kOnlyAccountUpm),
        GetPasswordAccessLossWarningTypeTestCase(
            /*test_case_desc=*/"MigrationFailedAuto",
            /*gms_core_version=*/"241512000",
            /*migration_attempted=*/true,
            /*local_passwords_migration_failed=*/true,
            /*profile_store_password_count=*/10,
            /*is_auto=*/true,
            /*expected_result=*/
            PasswordAccessLossWarningType::kNewGmsCoreMigrationFailed),
        GetPasswordAccessLossWarningTypeTestCase(
            /*test_case_desc=*/"MigrationSucceededAuto",
            /*gms_core_version=*/"241512000",
            /*migration_attempted=*/true,
            /*local_passwords_migration_failed=*/false,
            /*profile_store_password_count=*/10,
            /*is_auto=*/true,
            /*expected_result=*/PasswordAccessLossWarningType::kNone)),
    [](const ::testing::TestParamInfo<GetPasswordAccessLossWarningTypeTestCase>&
           info) { return info.param.test_case_desc; });

}  // namespace
}  // namespace password_manager_android_util