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

// Copyright 2021 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_store_proxy_backend.h"

#include <memory>
#include <utility>
#include <vector>

#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/strings/strcat.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.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_form_digest.h"
#include "components/password_manager/core/browser/password_manager_test_utils.h"
#include "components/password_manager/core/browser/password_store/mock_password_store_backend.h"
#include "components/password_manager/core/browser/password_store/password_store.h"
#include "components/password_manager/core/browser/password_store/password_store_backend.h"
#include "components/password_manager/core/browser/password_store/password_store_backend_error.h"
#include "components/password_manager/core/browser/password_store/password_store_change.h"
#include "components/password_manager/core/common/password_manager_features.h"
#include "components/password_manager/core/common/password_manager_pref_names.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
#include "components/sync/test/test_sync_service.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"

namespace password_manager {
namespace {

using ::testing::_;
using ::testing::AnyNumber;
using ::testing::AtMost;
using ::testing::ElementsAreArray;
using ::testing::Eq;
using ::testing::Invoke;
using ::testing::Optional;
using ::testing::Pointer;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::StrictMock;
using ::testing::VariantWith;
using ::testing::WithArg;
using Type = PasswordStoreChange::Type;
using RemoveChangesReceived = PasswordStoreBackend::RemoteChangesReceived;

PasswordForm CreateTestForm() {
  PasswordForm form;
  form.username_value = u"Todd Tester";
  form.password_value = u"S3cr3t";
  form.url = GURL(u"https://example.com");
  form.match_type = PasswordForm::MatchType::kExact;
  return form;
}

std::vector<PasswordForm> CreateTestLogins() {
  std::vector<PasswordForm> forms = {
      CreateEntry("Todd Tester", "S3cr3t", GURL(u"https://example.com"),
                  PasswordForm::MatchType::kExact),
      CreateEntry("Marcus McSpartanGregor", "S0m3th1ngCr34t1v3",
                  GURL(u"https://m.example.com"),
                  PasswordForm::MatchType::kPSL)};
  return forms;
}

bool FilterNoUrl(const GURL& gurl) {
  return true;
}

MATCHER_P(PasswordChangesAre, expectations, "") {
  if (absl::holds_alternative<PasswordStoreBackendError>(arg)) {
    return false;
  }

  auto changes = absl::get<PasswordChanges>(arg);
  if (!changes.has_value()) {
    return false;
  }

  return changes.value() == expectations;
}

}  // namespace

class PasswordStoreProxyBackendBaseTest : public testing::Test {
 protected:
  PasswordStoreProxyBackendBaseTest() {
    prefs_.registry()->RegisterIntegerPref(
        prefs::kCurrentMigrationVersionToGoogleMobileServices, 0);
    prefs_.registry()->RegisterBooleanPref(
        prefs::kUnenrolledFromGoogleMobileServicesDueToErrors, false);
    prefs_.registry()->RegisterIntegerPref(
        password_manager::prefs::kPasswordsUseUPMLocalAndSeparateStores,
        static_cast<int>(
            password_manager::prefs::UseUpmLocalAndSeparateStoresState::kOff));
    prefs_.registry()->RegisterBooleanPref(
        prefs::kEmptyProfileStoreLoginDatabase, true);
  }

  void SetUp() override { proxy_backend_ = CreateProxyBackend(); }

  std::unique_ptr<PasswordStoreProxyBackend> CreateProxyBackend() {
    auto built_in_backend =
        std::make_unique<StrictMock<MockPasswordStoreBackend>>();
    auto android_backend =
        std::make_unique<StrictMock<MockPasswordStoreBackend>>();
    built_in_backend_ = built_in_backend.get();
    android_backend_ = android_backend.get();
    return std::make_unique<PasswordStoreProxyBackend>(
        std::move(built_in_backend), std::move(android_backend), &prefs_);
  }

  void TearDown() override {
    EXPECT_CALL(*android_backend_, Shutdown(_));
    EXPECT_CALL(*built_in_backend_, Shutdown(_));
    PasswordStoreBackend* backend = proxy_backend_.get();  // Will be destroyed.
    backend->Shutdown(base::DoNothing());
    proxy_backend_.reset();
  }

  void EnablePasswordSync() {
    sync_service_.GetUserSettings()->SetSelectedTypes(
        /*sync_everything=*/false, {syncer::UserSelectableType::kPasswords});
    sync_service_.FireStateChanged();
  }

  void DisablePasswordSync() {
    sync_service_.GetUserSettings()->SetSelectedTypes(
        /*sync_everything=*/false, /*types=*/{});
    sync_service_.FireStateChanged();
  }

  PasswordStoreBackend& proxy_backend() { return *proxy_backend_; }
  MockPasswordStoreBackend& built_in_backend() { return *built_in_backend_; }
  MockPasswordStoreBackend& android_backend() { return *android_backend_; }
  TestingPrefServiceSimple* prefs() { return &prefs_; }
  syncer::TestSyncService* sync_service() { return &sync_service_; }

  raw_ptr<StrictMock<MockPasswordStoreBackend>> built_in_backend_;
  raw_ptr<StrictMock<MockPasswordStoreBackend>> android_backend_;

 private:
  TestingPrefServiceSimple prefs_;
  std::unique_ptr<PasswordStoreProxyBackend> proxy_backend_;
  syncer::TestSyncService sync_service_;
};

TEST_F(PasswordStoreProxyBackendBaseTest, CallCompletionCallbackAfterInit) {
  base::MockCallback<base::OnceCallback<void(bool)>> completion_callback;

  // Both backends need to be invoked for a successful completion call.
  EXPECT_CALL(built_in_backend(), InitBackend)
      .WillOnce(
          WithArg<3>(Invoke([](base::OnceCallback<void(bool)> reply) -> void {
            std::move(reply).Run(true);
          })));

  base::OnceCallback<void(bool)> captured_android_backend_reply;
  EXPECT_CALL(android_backend(), InitBackend)
      .WillOnce(
          WithArg<3>(Invoke([&](base::OnceCallback<void(bool)> reply) -> void {
            captured_android_backend_reply = std::move(reply);
          })));

  proxy_backend().InitBackend(nullptr, base::DoNothing(), base::DoNothing(),
                              completion_callback.Get());
  // The android backend requires the sync service to be initialized before
  // signaling that the backend initialization is complete.
  EXPECT_CALL(completion_callback, Run(true));
  EXPECT_CALL(android_backend(), OnSyncServiceInitialized(sync_service()))
      .WillOnce(Invoke([&]() -> void {
        std::move(captured_android_backend_reply).Run(true);
      }));
  proxy_backend().OnSyncServiceInitialized(sync_service());
}

TEST_F(PasswordStoreProxyBackendBaseTest,
       CallCompletionWithFailureForAnyError) {
  base::MockCallback<base::OnceCallback<void(bool)>> completion_callback;

  // If one backend fails to initialize, the result of the second is
  // irrelevant.
  EXPECT_CALL(built_in_backend(), InitBackend)
      .WillOnce(
          WithArg<3>(Invoke([](base::OnceCallback<void(bool)> reply) -> void {
            std::move(reply).Run(false);
          })));
  base::OnceCallback<void(bool)> captured_android_backend_reply;
  EXPECT_CALL(android_backend(), InitBackend)
      .Times(AtMost(1))
      .WillOnce(
          WithArg<3>(Invoke([&](base::OnceCallback<void(bool)> reply) -> void {
            captured_android_backend_reply = std::move(reply);
          })));

  proxy_backend().InitBackend(nullptr, base::DoNothing(), base::DoNothing(),
                              completion_callback.Get());
  // The android backend requires the sync service to be initialized before
  // signaling that the backend initialization is complete.
  EXPECT_CALL(completion_callback, Run(false));
  EXPECT_CALL(android_backend(), OnSyncServiceInitialized(sync_service()))
      .WillOnce(Invoke([&]() -> void {
        std::move(captured_android_backend_reply).Run(false);
      }));
  proxy_backend().OnSyncServiceInitialized(sync_service());
}

TEST_F(PasswordStoreProxyBackendBaseTest,
       ProfileNoLocalSupportCallRemoteChangesOnlyForMainBackend) {
  base::MockCallback<RemoveChangesReceived> original_callback;

  // Both backends receive a callback that they trigger for new remote changes.
  RemoveChangesReceived built_in_remote_changes_callback;
  EXPECT_CALL(built_in_backend(), InitBackend)
      .WillOnce(SaveArg<1>(&built_in_remote_changes_callback));
  RemoveChangesReceived android_remote_changes_callback;
  EXPECT_CALL(android_backend(), InitBackend)
      .WillOnce(SaveArg<1>(&android_remote_changes_callback));
  proxy_backend().InitBackend(nullptr, original_callback.Get(),
                              base::DoNothing(), base::DoNothing());
  EXPECT_CALL(android_backend(), OnSyncServiceInitialized(sync_service()));
  proxy_backend().OnSyncServiceInitialized(sync_service());

  // With sync enabled, only the android backend calls the original callback.
  EnablePasswordSync();
  EXPECT_CALL(original_callback, Run);
  android_remote_changes_callback.Run(std::nullopt);
  testing::Mock::VerifyAndClearExpectations(&original_callback);

  EXPECT_CALL(original_callback, Run).Times(0);
  built_in_remote_changes_callback.Run(std::nullopt);
  testing::Mock::VerifyAndClearExpectations(&original_callback);

  // As soon as sync is disabled, only the built-in backend calls the original
  // callback. The callbacks are stable. No new Init call is necessary.
  DisablePasswordSync();

  EXPECT_CALL(original_callback, Run).Times(0);
  android_remote_changes_callback.Run(std::nullopt);
  testing::Mock::VerifyAndClearExpectations(&original_callback);

  EXPECT_CALL(original_callback, Run);
  built_in_remote_changes_callback.Run(std::nullopt);
}

TEST_F(PasswordStoreProxyBackendBaseTest,
       ProfileNoLocalSupportCallSyncCallbackForTheBuiltInBackend) {
  base::MockCallback<base::RepeatingClosure> original_callback;

  // Both backends receive a callback that they trigger for new remote changes.
  base::RepeatingClosure built_in_sync_callback;
  EXPECT_CALL(built_in_backend(), InitBackend)
      .WillOnce(SaveArg<2>(&built_in_sync_callback));
  EXPECT_CALL(android_backend(), InitBackend);
  proxy_backend().InitBackend(nullptr, base::DoNothing(),
                              original_callback.Get(), base::DoNothing());
  EXPECT_CALL(android_backend(), OnSyncServiceInitialized(sync_service()));
  proxy_backend().OnSyncServiceInitialized(sync_service());

  // With sync enabled, only the built-in backend calls the original callback.
  EnablePasswordSync();

  EXPECT_CALL(original_callback, Run);
  built_in_sync_callback.Run();
  testing::Mock::VerifyAndClearExpectations(&original_callback);

  // With sync is disabled, the built-in backend remains the only to call the
  // original callback.
  DisablePasswordSync();

  EXPECT_CALL(original_callback, Run);
  built_in_sync_callback.Run();
}

TEST_F(PasswordStoreProxyBackendBaseTest, BuiltInBackendClearedOnSyncInit) {
  prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1);
  EnablePasswordSync();

  EXPECT_CALL(android_backend(), OnSyncServiceInitialized(sync_service()));
  EXPECT_CALL(built_in_backend(), RemoveLoginsCreatedBetweenAsync(
                                      _, base::Time(), base::Time::Max(), _));
  proxy_backend().OnSyncServiceInitialized(sync_service());
}

TEST_F(PasswordStoreProxyBackendBaseTest,
       BuiltInBackendNotClearedOnSyncInit_WhenUnenrolled) {
  prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1);
  prefs()->SetBoolean(prefs::kUnenrolledFromGoogleMobileServicesDueToErrors,
                      true);
  EnablePasswordSync();

  EXPECT_CALL(android_backend(), OnSyncServiceInitialized(sync_service()));
  EXPECT_CALL(built_in_backend(), RemoveLoginsCreatedBetweenAsync).Times(0);
  proxy_backend().OnSyncServiceInitialized(sync_service());
}

TEST_F(PasswordStoreProxyBackendBaseTest,
       BuiltInBackendNotClearedOnSyncInit_WhenInitialUPMMigrationNotFinished) {
  prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 0);
  EnablePasswordSync();

  EXPECT_CALL(android_backend(), OnSyncServiceInitialized(sync_service()));
  EXPECT_CALL(built_in_backend(), RemoveLoginsCreatedBetweenAsync).Times(0);
  proxy_backend().OnSyncServiceInitialized(sync_service());
}

TEST_F(PasswordStoreProxyBackendBaseTest,
       BuiltInBackendNotClearedOnSyncInit_WhenSyncDisabled) {
  prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 0);

  EXPECT_CALL(android_backend(), OnSyncServiceInitialized(sync_service()));
  EXPECT_CALL(built_in_backend(), RemoveLoginsCreatedBetweenAsync).Times(0);
  proxy_backend().OnSyncServiceInitialized(sync_service());
}

TEST_F(PasswordStoreProxyBackendBaseTest,
       BuiltInBackendClearedOnSyncInit_Metrics) {
  base::HistogramTester histogram_tester;
  const char kStatusMetric[] =
      "PasswordManager.PasswordStoreProxyBackend.PasswordRemovalStatus";
  const char kCountMetric[] =
      "PasswordManager.PasswordStoreProxyBackend.RemovedPasswordCount";

  prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1);
  EnablePasswordSync();

  PasswordStoreChangeList change_list;
  change_list.emplace_back(Type::REMOVE, CreateTestForm());
  change_list.emplace_back(Type::REMOVE, CreateTestForm());

  EXPECT_CALL(android_backend(), OnSyncServiceInitialized(sync_service()));
  EXPECT_CALL(built_in_backend(), RemoveLoginsCreatedBetweenAsync(
                                      _, base::Time(), base::Time::Max(), _))
      .WillOnce(base::test::RunOnceCallback<3>(change_list));
  proxy_backend().OnSyncServiceInitialized(sync_service());

  histogram_tester.ExpectTotalCount(kStatusMetric, 1);
  histogram_tester.ExpectBucketCount(kStatusMetric, true, 1);
  histogram_tester.ExpectTotalCount(kCountMetric, 1);
  histogram_tester.ExpectBucketCount(kCountMetric, change_list.size(), 1);
}

TEST_F(PasswordStoreProxyBackendBaseTest,
       InitialUPMMigrationPrefIsResetOnSyncInit) {
  prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1);
  DisablePasswordSync();

  EXPECT_CALL(android_backend(), OnSyncServiceInitialized(sync_service()));
  proxy_backend().OnSyncServiceInitialized(sync_service());
  EXPECT_EQ(0, prefs()->GetInteger(
                   prefs::kCurrentMigrationVersionToGoogleMobileServices));
}

// Holds the conditions affecting UPM eligibility and the backends
// which should be used for each.
struct UpmVariationParam {
  bool is_sync_enabled = false;
  bool is_unenrolled = false;
  bool is_login_db_empty = false;
  bool is_initial_migration_finished = false;
  bool android_is_main_backend = false;
};

class PasswordStoreProxyBackendTest
    : public PasswordStoreProxyBackendBaseTest,
      public testing::WithParamInterface<UpmVariationParam> {
 public:
  void SetUp() override {
    PasswordStoreProxyBackendBaseTest::SetUp();

    if (GetParam().is_sync_enabled) {
      EnablePasswordSync();
    } else {
      DisablePasswordSync();
    }
    prefs()->SetBoolean(prefs::kUnenrolledFromGoogleMobileServicesDueToErrors,
                        GetParam().is_unenrolled);
    prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices,
                        GetParam().is_initial_migration_finished ? 1 : 0);
    prefs()->SetBoolean(prefs::kEmptyProfileStoreLoginDatabase,
                        GetParam().is_login_db_empty);

    if (GetParam().is_sync_enabled &&
        GetParam().is_initial_migration_finished && !GetParam().is_unenrolled) {
      // The login DB should be cleared for healthy syncing users.
      EXPECT_CALL(built_in_backend(),
                  RemoveLoginsCreatedBetweenAsync(_, base::Time(),
                                                  base::Time::Max(), _));
    }

    EXPECT_CALL(android_backend(), InitBackend);
    EXPECT_CALL(built_in_backend(), InitBackend);
    proxy_backend().InitBackend(nullptr, base::DoNothing(), base::DoNothing(),
                                base::DoNothing());
    EXPECT_CALL(android_backend(), OnSyncServiceInitialized(sync_service()));
    proxy_backend().OnSyncServiceInitialized(sync_service());
  }

  MockPasswordStoreBackend& main_backend() {
    return GetParam().android_is_main_backend ? android_backend()
                                              : built_in_backend();
  }

  MockPasswordStoreBackend& shadow_backend() {
    return GetParam().android_is_main_backend ? built_in_backend()
                                              : android_backend();
  }

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
};

TEST_P(PasswordStoreProxyBackendTest, UseMainBackendToGetAllLoginsAsync) {
  base::MockCallback<LoginsOrErrorReply> mock_reply;
  EXPECT_CALL(
      mock_reply,
      Run(VariantWith<LoginsResult>(ElementsAreArray(CreateTestLogins()))));

  EXPECT_CALL(main_backend(), GetAllLoginsAsync)
      .WillOnce(WithArg<0>(Invoke([](LoginsOrErrorReply reply) -> void {
        std::move(reply).Run(CreateTestLogins());
      })));
  EXPECT_CALL(shadow_backend(), GetAllLoginsAsync).Times(0);

  proxy_backend().GetAllLoginsAsync(mock_reply.Get());
}

TEST_P(PasswordStoreProxyBackendTest,
       UseMainBackendToGetAutofillableLoginsAsync) {
  base::MockCallback<LoginsOrErrorReply> mock_reply;
  EXPECT_CALL(
      mock_reply,
      Run(VariantWith<LoginsResult>(ElementsAreArray(CreateTestLogins()))));

  EXPECT_CALL(main_backend(), GetAutofillableLoginsAsync)
      .WillOnce(WithArg<0>(Invoke([](LoginsOrErrorReply reply) -> void {
        std::move(reply).Run(CreateTestLogins());
      })));
  EXPECT_CALL(shadow_backend(), GetAutofillableLoginsAsync).Times(0);

  proxy_backend().GetAutofillableLoginsAsync(mock_reply.Get());
}

TEST_P(PasswordStoreProxyBackendTest, UseMainBackendToFillMatchingLoginsAsync) {
  base::MockCallback<LoginsOrErrorReply> mock_reply;
  EXPECT_CALL(
      mock_reply,
      Run(VariantWith<LoginsResult>(ElementsAreArray(CreateTestLogins()))));

  EXPECT_CALL(main_backend(), FillMatchingLoginsAsync)
      .WillOnce(WithArg<0>(Invoke([](LoginsOrErrorReply reply) -> void {
        std::move(reply).Run(CreateTestLogins());
      })));
  EXPECT_CALL(shadow_backend(), FillMatchingLoginsAsync).Times(0);

  proxy_backend().FillMatchingLoginsAsync(mock_reply.Get(),
                                          /*include_psl=*/false,
                                          std::vector<PasswordFormDigest>());
}

TEST_P(PasswordStoreProxyBackendTest, UseMainBackendToAddLoginAsync) {
  base::MockCallback<PasswordChangesOrErrorReply> mock_reply;
  PasswordForm form = CreateTestForm();
  PasswordStoreChangeList change_list;
  change_list.push_back(PasswordStoreChange(Type::ADD, form));
  EXPECT_CALL(mock_reply,
              Run(VariantWith<PasswordChanges>(Optional(change_list))));

  EXPECT_CALL(main_backend(), AddLoginAsync(Eq(form), _))
      .WillOnce(WithArg<1>(
          Invoke([&change_list](PasswordChangesOrErrorReply reply) -> void {
            std::move(reply).Run(change_list);
          })));
  EXPECT_CALL(shadow_backend(), AddLoginAsync).Times(0);

  proxy_backend().AddLoginAsync(form, mock_reply.Get());
}

TEST_P(PasswordStoreProxyBackendTest, UseMainBackendToUpdateLoginAsync) {
  base::MockCallback<PasswordChangesOrErrorReply> mock_reply;
  PasswordForm form = CreateTestForm();
  PasswordStoreChangeList change_list;
  change_list.push_back(PasswordStoreChange(Type::UPDATE, form));
  EXPECT_CALL(mock_reply,
              Run(VariantWith<PasswordChanges>(Optional(change_list))));

  EXPECT_CALL(main_backend(), UpdateLoginAsync(Eq(form), _))
      .WillOnce(WithArg<1>(
          Invoke([&change_list](PasswordChangesOrErrorReply reply) -> void {
            std::move(reply).Run(change_list);
          })));
  EXPECT_CALL(shadow_backend(), UpdateLoginAsync).Times(0);

  proxy_backend().UpdateLoginAsync(form, mock_reply.Get());
}

TEST_P(PasswordStoreProxyBackendTest, UseBothBackendsToRemoveLoginAsyncIfUPM) {
  base::MockCallback<PasswordChangesOrErrorReply> mock_reply;
  PasswordForm form = CreateTestForm();
  PasswordStoreChangeList change_list;
  change_list.push_back(PasswordStoreChange(Type::REMOVE, form));
  EXPECT_CALL(mock_reply,
              Run(VariantWith<PasswordChanges>(Optional(change_list))));

  EXPECT_CALL(main_backend(), RemoveLoginAsync(_, Eq(form), _))
      .WillOnce(WithArg<2>(
          Invoke([&change_list](PasswordChangesOrErrorReply reply) -> void {
            std::move(reply).Run(change_list);
          })));

  // The shadow backend should only be called to remove logins if the main
  // backend is the android backend, to ensure the login db passwords are
  // also removed.
  EXPECT_CALL(shadow_backend(), RemoveLoginAsync(_, Eq(form), _))
      .Times(GetParam().android_is_main_backend ? 1 : 0);
  proxy_backend().RemoveLoginAsync(FROM_HERE, form, mock_reply.Get());
}

TEST_P(PasswordStoreProxyBackendTest,
       UseBothBackendsToRemoveLoginsByURLAndTimeAsyncIfUPM) {
  base::Time kStart = base::Time::FromTimeT(111111);
  base::Time kEnd = base::Time::FromTimeT(22222222);
  base::MockCallback<PasswordChangesOrErrorReply> mock_reply;
  PasswordForm form = CreateTestForm();
  PasswordStoreChangeList change_list;
  change_list.push_back(PasswordStoreChange(Type::REMOVE, form));
  EXPECT_CALL(mock_reply,
              Run(VariantWith<PasswordChanges>(Optional(change_list))));

  EXPECT_CALL(main_backend(),
              RemoveLoginsByURLAndTimeAsync(_, _, Eq(kStart), Eq(kEnd), _, _))
      .WillOnce(WithArg<5>(
          Invoke([&change_list](PasswordChangesOrErrorReply reply) -> void {
            std::move(reply).Run(change_list);
          })));

  // The shadow backend should only be called to remove logins if the main
  // backend is the android backend, to ensure the login db passwords are
  // also removed.
  EXPECT_CALL(shadow_backend(),
              RemoveLoginsByURLAndTimeAsync(_, _, Eq(kStart), Eq(kEnd), _, _))
      .Times(GetParam().android_is_main_backend ? 1 : 0);
  proxy_backend().RemoveLoginsByURLAndTimeAsync(
      FROM_HERE, base::BindRepeating(&FilterNoUrl), kStart, kEnd,
      base::NullCallback(), mock_reply.Get());
}

TEST_P(PasswordStoreProxyBackendTest,
       UseBothBackendsToRemoveLoginsCreatedBetweenAsyncIfUPM) {
  base::Time kStart = base::Time::FromTimeT(111111);
  base::Time kEnd = base::Time::FromTimeT(22222222);
  base::MockCallback<PasswordChangesOrErrorReply> mock_reply;
  PasswordStoreChangeList change_list;
  change_list.push_back(PasswordStoreChange(Type::REMOVE, CreateTestForm()));
  EXPECT_CALL(mock_reply,
              Run(VariantWith<PasswordChanges>(Optional(change_list))));

  EXPECT_CALL(main_backend(),
              RemoveLoginsCreatedBetweenAsync(_, Eq(kStart), Eq(kEnd), _))
      .WillOnce(WithArg<3>(
          Invoke([&change_list](PasswordChangesOrErrorReply reply) -> void {
            std::move(reply).Run(change_list);
          })));
  EXPECT_CALL(shadow_backend(),
              RemoveLoginsCreatedBetweenAsync(_, Eq(kStart), Eq(kEnd), _))
      .Times(GetParam().android_is_main_backend ? 1 : 0);
  proxy_backend().RemoveLoginsCreatedBetweenAsync(FROM_HERE, kStart, kEnd,
                                                  mock_reply.Get());
}

TEST_P(PasswordStoreProxyBackendTest,
       UseMainBackendToDisableAutoSignInForOriginsAsync) {
  base::MockCallback<base::OnceClosure> mock_reply;
  EXPECT_CALL(mock_reply, Run);
  EXPECT_CALL(main_backend(), DisableAutoSignInForOriginsAsync)
      .WillOnce(WithArg<1>(
          Invoke([](base::OnceClosure reply) { std::move(reply).Run(); })));
  EXPECT_CALL(shadow_backend(), DisableAutoSignInForOriginsAsync).Times(0);
  proxy_backend().DisableAutoSignInForOriginsAsync(
      base::BindRepeating(&FilterNoUrl), mock_reply.Get());
}

TEST_P(PasswordStoreProxyBackendTest,
       UseMainBackendToGetSmartBubbleStatsStore) {
  EXPECT_CALL(main_backend(), GetSmartBubbleStatsStore);
  EXPECT_CALL(shadow_backend(), GetSmartBubbleStatsStore).Times(0);
  proxy_backend().GetSmartBubbleStatsStore();
}

INSTANTIATE_TEST_SUITE_P(
    PasswordStoreProxyBackendBaseTest,
    PasswordStoreProxyBackendTest,
    // PasswordStoreProxyBackend is created only for `ProfilePasswordStore` when
    // `UseUpmLocalAndSeparateStoresState` is `kOff`. In this configuration
    // there are 5 possible variables which can influence when `android_backend`
    // is used. All 32 configurations are tested here.
    testing::Values(UpmVariationParam{.is_sync_enabled = false,
                                      .is_unenrolled = false,
                                      .is_login_db_empty = false,
                                      .is_initial_migration_finished = false,
                                      .android_is_main_backend = false},
                    UpmVariationParam{.is_sync_enabled = true,
                                      .is_unenrolled = false,
                                      .is_login_db_empty = false,
                                      .is_initial_migration_finished = false,
                                      .android_is_main_backend = false},
                    UpmVariationParam{.is_sync_enabled = false,
                                      .is_unenrolled = true,
                                      .is_login_db_empty = false,
                                      .is_initial_migration_finished = false,
                                      .android_is_main_backend = false},
                    UpmVariationParam{.is_sync_enabled = true,
                                      .is_unenrolled = true,
                                      .is_login_db_empty = false,
                                      .is_initial_migration_finished = false,
                                      .android_is_main_backend = false},
                    UpmVariationParam{.is_sync_enabled = false,
                                      .is_unenrolled = false,
                                      .is_login_db_empty = true,
                                      .is_initial_migration_finished = false,
                                      .android_is_main_backend = false},
                    UpmVariationParam{.is_sync_enabled = true,
                                      .is_unenrolled = false,
                                      .is_login_db_empty = true,
                                      .is_initial_migration_finished = false,
                                      .android_is_main_backend = true},
                    UpmVariationParam{.is_sync_enabled = false,
                                      .is_unenrolled = true,
                                      .is_login_db_empty = true,
                                      .is_initial_migration_finished = false,
                                      .android_is_main_backend = false},
                    UpmVariationParam{.is_sync_enabled = true,
                                      .is_unenrolled = true,
                                      .is_login_db_empty = true,
                                      .is_initial_migration_finished = false,
                                      .android_is_main_backend = true},
                    UpmVariationParam{.is_sync_enabled = false,
                                      .is_unenrolled = false,
                                      .is_login_db_empty = false,
                                      .is_initial_migration_finished = true,
                                      .android_is_main_backend = false},
                    UpmVariationParam{.is_sync_enabled = true,
                                      .is_unenrolled = false,
                                      .is_login_db_empty = false,
                                      .is_initial_migration_finished = true,
                                      .android_is_main_backend = true},
                    UpmVariationParam{.is_sync_enabled = false,
                                      .is_unenrolled = true,
                                      .is_login_db_empty = false,
                                      .is_initial_migration_finished = true,
                                      .android_is_main_backend = false},
                    UpmVariationParam{.is_sync_enabled = true,
                                      .is_unenrolled = true,
                                      .is_login_db_empty = false,
                                      .is_initial_migration_finished = true,
                                      .android_is_main_backend = false},
                    UpmVariationParam{.is_sync_enabled = false,
                                      .is_unenrolled = false,
                                      .is_login_db_empty = true,
                                      .is_initial_migration_finished = true,
                                      .android_is_main_backend = false},
                    UpmVariationParam{.is_sync_enabled = true,
                                      .is_unenrolled = false,
                                      .is_login_db_empty = true,
                                      .is_initial_migration_finished = true,
                                      .android_is_main_backend = true},
                    UpmVariationParam{.is_sync_enabled = false,
                                      .is_unenrolled = true,
                                      .is_login_db_empty = true,
                                      .is_initial_migration_finished = true,
                                      .android_is_main_backend = false},
                    UpmVariationParam{.is_sync_enabled = true,
                                      .is_unenrolled = true,
                                      .is_login_db_empty = true,
                                      .is_initial_migration_finished = true,
                                      .android_is_main_backend = true}),
    [](const ::testing::TestParamInfo<UpmVariationParam>& info) {
      std::string name;
      name += info.param.is_sync_enabled ? "Syncing" : "Local";
      name += info.param.is_unenrolled ? "Unenrolled" : "";
      name += info.param.is_initial_migration_finished ? "" : "NotMigrated";
      name += info.param.is_login_db_empty ? "EmptyDB" : "";
      return name;
    });

}  // namespace password_manager