chromium/chromeos/ash/services/multidevice_setup/multidevice_setup_impl_unittest.cc

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <memory>
#include <vector>

#include "ash/constants/ash_features.h"
#include "base/containers/flat_map.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/ranges/algorithm.h"
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "chromeos/ash/components/multidevice/remote_device_test_util.h"
#include "chromeos/ash/services/device_sync/public/cpp/fake_device_sync_client.h"
#include "chromeos/ash/services/device_sync/public/cpp/fake_gcm_device_info_provider.h"
#include "chromeos/ash/services/multidevice_setup/account_status_change_delegate_notifier_impl.h"
#include "chromeos/ash/services/multidevice_setup/android_sms_app_installing_status_observer.h"
#include "chromeos/ash/services/multidevice_setup/eligible_host_devices_provider_impl.h"
#include "chromeos/ash/services/multidevice_setup/fake_account_status_change_delegate.h"
#include "chromeos/ash/services/multidevice_setup/fake_account_status_change_delegate_notifier.h"
#include "chromeos/ash/services/multidevice_setup/fake_eligible_host_devices_provider.h"
#include "chromeos/ash/services/multidevice_setup/fake_feature_state_manager.h"
#include "chromeos/ash/services/multidevice_setup/fake_feature_state_observer.h"
#include "chromeos/ash/services/multidevice_setup/fake_global_state_feature_manager.h"
#include "chromeos/ash/services/multidevice_setup/fake_host_backend_delegate.h"
#include "chromeos/ash/services/multidevice_setup/fake_host_device_timestamp_manager.h"
#include "chromeos/ash/services/multidevice_setup/fake_host_status_observer.h"
#include "chromeos/ash/services/multidevice_setup/fake_host_status_provider.h"
#include "chromeos/ash/services/multidevice_setup/fake_host_verifier.h"
#include "chromeos/ash/services/multidevice_setup/feature_state_manager_impl.h"
#include "chromeos/ash/services/multidevice_setup/global_state_feature_manager.h"
#include "chromeos/ash/services/multidevice_setup/global_state_feature_manager_impl.h"
#include "chromeos/ash/services/multidevice_setup/grandfathered_easy_unlock_host_disabler.h"
#include "chromeos/ash/services/multidevice_setup/host_backend_delegate_impl.h"
#include "chromeos/ash/services/multidevice_setup/host_device_timestamp_manager_impl.h"
#include "chromeos/ash/services/multidevice_setup/host_status_provider_impl.h"
#include "chromeos/ash/services/multidevice_setup/host_verifier_impl.h"
#include "chromeos/ash/services/multidevice_setup/multidevice_setup_impl.h"
#include "chromeos/ash/services/multidevice_setup/public/cpp/fake_android_sms_app_helper_delegate.h"
#include "chromeos/ash/services/multidevice_setup/public/cpp/fake_android_sms_pairing_state_tracker.h"
#include "chromeos/ash/services/multidevice_setup/public/cpp/fake_auth_token_validator.h"
#include "chromeos/ash/services/multidevice_setup/public/cpp/oobe_completion_tracker.h"
#include "chromeos/ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
#include "chromeos/ash/services/multidevice_setup/wifi_sync_notification_controller.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace ash {

namespace multidevice_setup {

namespace {

const size_t kNumTestDevices = 3;

const char kValidAuthToken[] = "validAuthToken";

multidevice::RemoteDeviceList RefListToRawList(
    const multidevice::RemoteDeviceRefList& ref_list) {
  multidevice::RemoteDeviceList raw_list;
  base::ranges::transform(ref_list, std::back_inserter(raw_list),
                          [](const multidevice::RemoteDeviceRef ref) {
                            return *GetMutableRemoteDevice(ref);
                          });
  return raw_list;
}

std::optional<multidevice::RemoteDevice> RefToRaw(
    const std::optional<multidevice::RemoteDeviceRef>& ref) {
  if (!ref)
    return std::nullopt;

  return *GetMutableRemoteDevice(*ref);
}

class FakeEligibleHostDevicesProviderFactory
    : public EligibleHostDevicesProviderImpl::Factory {
 public:
  explicit FakeEligibleHostDevicesProviderFactory(
      device_sync::FakeDeviceSyncClient* expected_device_sync_client)
      : expected_device_sync_client_(expected_device_sync_client) {}

  FakeEligibleHostDevicesProviderFactory(
      const FakeEligibleHostDevicesProviderFactory&) = delete;
  FakeEligibleHostDevicesProviderFactory& operator=(
      const FakeEligibleHostDevicesProviderFactory&) = delete;

  ~FakeEligibleHostDevicesProviderFactory() override = default;

  FakeEligibleHostDevicesProvider* instance() { return instance_; }

 private:
  // EligibleHostDevicesProviderImpl::Factory:
  std::unique_ptr<EligibleHostDevicesProvider> CreateInstance(
      device_sync::DeviceSyncClient* device_sync_client) override {
    EXPECT_FALSE(instance_);
    EXPECT_EQ(expected_device_sync_client_, device_sync_client);

    auto instance = std::make_unique<FakeEligibleHostDevicesProvider>();
    instance_ = instance.get();
    return instance;
  }

  raw_ptr<device_sync::FakeDeviceSyncClient> expected_device_sync_client_;

  raw_ptr<FakeEligibleHostDevicesProvider, DanglingUntriaged> instance_ =
      nullptr;
};

class FakeHostBackendDelegateFactory : public HostBackendDelegateImpl::Factory {
 public:
  FakeHostBackendDelegateFactory(
      FakeEligibleHostDevicesProviderFactory*
          fake_eligible_host_devices_provider_factory,
      sync_preferences::TestingPrefServiceSyncable*
          expected_testing_pref_service,
      device_sync::FakeDeviceSyncClient* expected_device_sync_client)
      : fake_eligible_host_devices_provider_factory_(
            fake_eligible_host_devices_provider_factory),
        expected_testing_pref_service_(expected_testing_pref_service),
        expected_device_sync_client_(expected_device_sync_client) {}

  FakeHostBackendDelegateFactory(const FakeHostBackendDelegateFactory&) =
      delete;
  FakeHostBackendDelegateFactory& operator=(
      const FakeHostBackendDelegateFactory&) = delete;

  ~FakeHostBackendDelegateFactory() override = default;

  FakeHostBackendDelegate* instance() { return instance_; }

 private:
  // HostBackendDelegateImpl::Factory:
  std::unique_ptr<HostBackendDelegate> CreateInstance(
      EligibleHostDevicesProvider* eligible_host_devices_provider,
      PrefService* pref_service,
      device_sync::DeviceSyncClient* device_sync_client,
      std::unique_ptr<base::OneShotTimer> timer) override {
    EXPECT_FALSE(instance_);
    EXPECT_EQ(fake_eligible_host_devices_provider_factory_->instance(),
              eligible_host_devices_provider);
    EXPECT_EQ(expected_testing_pref_service_, pref_service);
    EXPECT_EQ(expected_device_sync_client_, device_sync_client);

    auto instance = std::make_unique<FakeHostBackendDelegate>();
    instance_ = instance.get();
    return instance;
  }

  raw_ptr<FakeEligibleHostDevicesProviderFactory>
      fake_eligible_host_devices_provider_factory_;
  raw_ptr<sync_preferences::TestingPrefServiceSyncable>
      expected_testing_pref_service_;
  raw_ptr<device_sync::FakeDeviceSyncClient> expected_device_sync_client_;

  raw_ptr<FakeHostBackendDelegate, DanglingUntriaged> instance_ = nullptr;
};

class FakeHostVerifierFactory : public HostVerifierImpl::Factory {
 public:
  FakeHostVerifierFactory(
      FakeHostBackendDelegateFactory* fake_host_backend_delegate_factory,
      device_sync::FakeDeviceSyncClient* expected_device_sync_client,
      sync_preferences::TestingPrefServiceSyncable*
          expected_testing_pref_service)
      : fake_host_backend_delegate_factory_(fake_host_backend_delegate_factory),
        expected_device_sync_client_(expected_device_sync_client),
        expected_testing_pref_service_(expected_testing_pref_service) {}

  FakeHostVerifierFactory(const FakeHostVerifierFactory&) = delete;
  FakeHostVerifierFactory& operator=(const FakeHostVerifierFactory&) = delete;

  ~FakeHostVerifierFactory() override = default;

  FakeHostVerifier* instance() { return instance_; }

 private:
  // HostVerifierImpl::Factory:
  std::unique_ptr<HostVerifier> CreateInstance(
      HostBackendDelegate* host_backend_delegate,
      device_sync::DeviceSyncClient* device_sync_client,
      PrefService* pref_service,
      base::Clock* clock,
      std::unique_ptr<base::OneShotTimer> retry_timer,
      std::unique_ptr<base::OneShotTimer> sync_timer) override {
    EXPECT_FALSE(instance_);
    EXPECT_EQ(fake_host_backend_delegate_factory_->instance(),
              host_backend_delegate);
    EXPECT_EQ(expected_device_sync_client_, device_sync_client);
    EXPECT_EQ(expected_testing_pref_service_, pref_service);

    auto instance = std::make_unique<FakeHostVerifier>();
    instance_ = instance.get();
    return instance;
  }

  raw_ptr<FakeHostBackendDelegateFactory> fake_host_backend_delegate_factory_;
  raw_ptr<device_sync::FakeDeviceSyncClient> expected_device_sync_client_;
  raw_ptr<sync_preferences::TestingPrefServiceSyncable>
      expected_testing_pref_service_;

  raw_ptr<FakeHostVerifier, DanglingUntriaged> instance_ = nullptr;
};

class FakeHostStatusProviderFactory : public HostStatusProviderImpl::Factory {
 public:
  FakeHostStatusProviderFactory(
      FakeEligibleHostDevicesProviderFactory*
          fake_eligible_host_devices_provider_factory,
      FakeHostBackendDelegateFactory* fake_host_backend_delegate_factory,
      FakeHostVerifierFactory* fake_host_verifier_factory,
      device_sync::FakeDeviceSyncClient* expected_device_sync_client)
      : fake_eligible_host_devices_provider_factory_(
            fake_eligible_host_devices_provider_factory),
        fake_host_backend_delegate_factory_(fake_host_backend_delegate_factory),
        fake_host_verifier_factory_(fake_host_verifier_factory),
        expected_device_sync_client_(expected_device_sync_client) {}

  FakeHostStatusProviderFactory(const FakeHostStatusProviderFactory&) = delete;
  FakeHostStatusProviderFactory& operator=(
      const FakeHostStatusProviderFactory&) = delete;

  ~FakeHostStatusProviderFactory() override = default;

  FakeHostStatusProvider* instance() { return instance_; }

 private:
  // HostStatusProviderImpl::Factory:
  std::unique_ptr<HostStatusProvider> CreateInstance(
      EligibleHostDevicesProvider* eligible_host_devices_provider,
      HostBackendDelegate* host_backend_delegate,
      HostVerifier* host_verifier,
      device_sync::DeviceSyncClient* device_sync_client) override {
    EXPECT_FALSE(instance_);
    EXPECT_EQ(fake_eligible_host_devices_provider_factory_->instance(),
              eligible_host_devices_provider);
    EXPECT_EQ(fake_host_backend_delegate_factory_->instance(),
              host_backend_delegate);
    EXPECT_EQ(fake_host_verifier_factory_->instance(), host_verifier);
    EXPECT_EQ(expected_device_sync_client_, device_sync_client);

    auto instance = std::make_unique<FakeHostStatusProvider>();
    instance_ = instance.get();
    return instance;
  }

  raw_ptr<FakeEligibleHostDevicesProviderFactory>
      fake_eligible_host_devices_provider_factory_;
  raw_ptr<FakeHostBackendDelegateFactory> fake_host_backend_delegate_factory_;
  raw_ptr<FakeHostVerifierFactory> fake_host_verifier_factory_;
  raw_ptr<device_sync::FakeDeviceSyncClient> expected_device_sync_client_;

  raw_ptr<FakeHostStatusProvider, DanglingUntriaged> instance_ = nullptr;
};

class FakeGlobalStateFeatureManagerFactory
    : public GlobalStateFeatureManagerImpl::Factory {
 public:
  FakeGlobalStateFeatureManagerFactory(
      FakeHostStatusProviderFactory* fake_host_status_provider_factory,
      sync_preferences::TestingPrefServiceSyncable*
          expected_testing_pref_service,
      device_sync::FakeDeviceSyncClient* expected_device_sync_client)
      : fake_host_status_provider_factory_(fake_host_status_provider_factory),
        expected_testing_pref_service_(expected_testing_pref_service),
        expected_device_sync_client_(expected_device_sync_client) {}

  FakeGlobalStateFeatureManagerFactory(
      const FakeGlobalStateFeatureManagerFactory&) = delete;
  FakeGlobalStateFeatureManagerFactory& operator=(
      const FakeGlobalStateFeatureManagerFactory&) = delete;
  ~FakeGlobalStateFeatureManagerFactory() override = default;

 private:
  // GlobalStateFeatureManagerImpl::Factory:
  std::unique_ptr<GlobalStateFeatureManager> CreateInstance(
      GlobalStateFeatureManagerImpl::Factory::Option option,
      HostStatusProvider* host_status_provider,
      PrefService* pref_service,
      device_sync::DeviceSyncClient* device_sync_client,
      std::unique_ptr<base::OneShotTimer> timer) override {
    EXPECT_EQ(fake_host_status_provider_factory_->instance(),
              host_status_provider);
    EXPECT_EQ(expected_testing_pref_service_, pref_service);
    EXPECT_EQ(expected_device_sync_client_, device_sync_client);

    return std::make_unique<FakeGlobalStateFeatureManager>();
  }

  raw_ptr<FakeHostStatusProviderFactory> fake_host_status_provider_factory_;
  raw_ptr<sync_preferences::TestingPrefServiceSyncable>
      expected_testing_pref_service_;
  raw_ptr<device_sync::FakeDeviceSyncClient> expected_device_sync_client_;
};

class FakeWifiSyncNotificationControllerFactory
    : public WifiSyncNotificationController::Factory {
 public:
  FakeWifiSyncNotificationControllerFactory(
      FakeHostStatusProviderFactory* fake_host_status_provider_factory,
      sync_preferences::TestingPrefServiceSyncable*
          expected_testing_pref_service,
      device_sync::FakeDeviceSyncClient* expected_device_sync_client)
      : fake_host_status_provider_factory_(fake_host_status_provider_factory),
        expected_testing_pref_service_(expected_testing_pref_service),
        expected_device_sync_client_(expected_device_sync_client) {}

  FakeWifiSyncNotificationControllerFactory(
      const FakeWifiSyncNotificationControllerFactory&) = delete;
  FakeWifiSyncNotificationControllerFactory& operator=(
      const FakeWifiSyncNotificationControllerFactory&) = delete;
  ~FakeWifiSyncNotificationControllerFactory() override = default;

 private:
  // WifiSyncNotificationController::Factory:
  std::unique_ptr<WifiSyncNotificationController> CreateInstance(
      GlobalStateFeatureManager* wifi_sync_feature_manager,
      HostStatusProvider* host_status_provider,
      PrefService* pref_service,
      device_sync::DeviceSyncClient* device_sync_client,
      AccountStatusChangeDelegateNotifier* delegate_notifier) override {
    EXPECT_EQ(fake_host_status_provider_factory_->instance(),
              host_status_provider);
    EXPECT_EQ(expected_testing_pref_service_, pref_service);
    EXPECT_EQ(expected_device_sync_client_, device_sync_client);
    // Only check inputs and return nullptr. We do not want to trigger any logic
    // in these unit tests.
    return nullptr;
  }

  raw_ptr<FakeHostStatusProviderFactory> fake_host_status_provider_factory_;
  raw_ptr<sync_preferences::TestingPrefServiceSyncable>
      expected_testing_pref_service_;
  raw_ptr<device_sync::FakeDeviceSyncClient> expected_device_sync_client_;
};

class FakeGrandfatheredEasyUnlockHostDisablerFactory
    : public GrandfatheredEasyUnlockHostDisabler::Factory {
 public:
  FakeGrandfatheredEasyUnlockHostDisablerFactory(
      FakeHostBackendDelegateFactory* fake_host_backend_delegate_factory,
      device_sync::FakeDeviceSyncClient* expected_device_sync_client,
      sync_preferences::TestingPrefServiceSyncable*
          expected_testing_pref_service)
      : fake_host_backend_delegate_factory_(fake_host_backend_delegate_factory),
        expected_device_sync_client_(expected_device_sync_client),
        expected_testing_pref_service_(expected_testing_pref_service) {}

  FakeGrandfatheredEasyUnlockHostDisablerFactory(
      const FakeGrandfatheredEasyUnlockHostDisablerFactory&) = delete;
  FakeGrandfatheredEasyUnlockHostDisablerFactory& operator=(
      const FakeGrandfatheredEasyUnlockHostDisablerFactory&) = delete;

  ~FakeGrandfatheredEasyUnlockHostDisablerFactory() override = default;

 private:
  // GrandfatheredEasyUnlockHostDisabler::Factory:
  std::unique_ptr<GrandfatheredEasyUnlockHostDisabler> CreateInstance(
      HostBackendDelegate* host_backend_delegate,
      device_sync::DeviceSyncClient* device_sync_client,
      PrefService* pref_service,
      std::unique_ptr<base::OneShotTimer> timer) override {
    EXPECT_EQ(fake_host_backend_delegate_factory_->instance(),
              host_backend_delegate);
    EXPECT_EQ(expected_device_sync_client_, device_sync_client);
    EXPECT_EQ(expected_testing_pref_service_, pref_service);
    // Only check inputs and return nullptr. We do not want to trigger any logic
    // in these unit tests.
    return nullptr;
  }

  raw_ptr<FakeHostBackendDelegateFactory> fake_host_backend_delegate_factory_;
  raw_ptr<device_sync::FakeDeviceSyncClient> expected_device_sync_client_;
  raw_ptr<sync_preferences::TestingPrefServiceSyncable>
      expected_testing_pref_service_;
};

class FakeFeatureStateManagerFactory : public FeatureStateManagerImpl::Factory {
 public:
  FakeFeatureStateManagerFactory(
      sync_preferences::TestingPrefServiceSyncable*
          expected_testing_pref_service,
      FakeHostStatusProviderFactory* fake_host_status_provider_factory,
      device_sync::FakeDeviceSyncClient* expected_device_sync_client,
      FakeAndroidSmsPairingStateTracker*
          expected_android_sms_pairing_state_tracker,
      bool expected_is_secondary_user)
      : expected_testing_pref_service_(expected_testing_pref_service),
        fake_host_status_provider_factory_(fake_host_status_provider_factory),
        expected_device_sync_client_(expected_device_sync_client),
        expected_android_sms_pairing_state_tracker_(
            expected_android_sms_pairing_state_tracker),
        expected_is_secondary_user_(expected_is_secondary_user) {}

  FakeFeatureStateManagerFactory(const FakeFeatureStateManagerFactory&) =
      delete;
  FakeFeatureStateManagerFactory& operator=(
      const FakeFeatureStateManagerFactory&) = delete;

  ~FakeFeatureStateManagerFactory() override = default;

  FakeFeatureStateManager* instance() { return instance_; }

 private:
  // FeatureStateManagerImpl::Factory:
  std::unique_ptr<FeatureStateManager> CreateInstance(
      PrefService* pref_service,
      HostStatusProvider* host_status_provider,
      device_sync::DeviceSyncClient* device_sync_client,
      AndroidSmsPairingStateTracker* android_sms_pairing_state_tracker,
      const base::flat_map<mojom::Feature, GlobalStateFeatureManager*>&
          global_state_feature_managers,
      bool is_secondary_user) override {
    EXPECT_FALSE(instance_);
    EXPECT_EQ(expected_testing_pref_service_, pref_service);
    EXPECT_EQ(fake_host_status_provider_factory_->instance(),
              host_status_provider);
    EXPECT_EQ(expected_device_sync_client_, device_sync_client);
    EXPECT_EQ(expected_android_sms_pairing_state_tracker_,
              android_sms_pairing_state_tracker);
    EXPECT_EQ(expected_is_secondary_user_, is_secondary_user);

    auto instance = std::make_unique<FakeFeatureStateManager>();
    instance_ = instance.get();
    return instance;
  }

  raw_ptr<sync_preferences::TestingPrefServiceSyncable>
      expected_testing_pref_service_;
  raw_ptr<FakeHostStatusProviderFactory> fake_host_status_provider_factory_;
  raw_ptr<device_sync::FakeDeviceSyncClient> expected_device_sync_client_;
  raw_ptr<FakeAndroidSmsPairingStateTracker, DanglingUntriaged>
      expected_android_sms_pairing_state_tracker_;
  bool expected_is_secondary_user_;

  raw_ptr<FakeFeatureStateManager, DanglingUntriaged> instance_ = nullptr;
};

class FakeHostDeviceTimestampManagerFactory
    : public HostDeviceTimestampManagerImpl::Factory {
 public:
  FakeHostDeviceTimestampManagerFactory(
      FakeHostStatusProviderFactory* fake_host_status_provider_factory,
      sync_preferences::TestingPrefServiceSyncable*
          expected_testing_pref_service)
      : fake_host_status_provider_factory_(fake_host_status_provider_factory),
        expected_testing_pref_service_(expected_testing_pref_service) {}

  FakeHostDeviceTimestampManagerFactory(
      const FakeHostDeviceTimestampManagerFactory&) = delete;
  FakeHostDeviceTimestampManagerFactory& operator=(
      const FakeHostDeviceTimestampManagerFactory&) = delete;

  ~FakeHostDeviceTimestampManagerFactory() override = default;

  FakeHostDeviceTimestampManager* instance() { return instance_; }

 private:
  // HostDeviceTimestampManagerImpl::Factory:
  std::unique_ptr<HostDeviceTimestampManager> CreateInstance(
      HostStatusProvider* host_status_provider,
      PrefService* pref_service,
      base::Clock* clock) override {
    EXPECT_FALSE(instance_);
    EXPECT_EQ(fake_host_status_provider_factory_->instance(),
              host_status_provider);
    EXPECT_EQ(expected_testing_pref_service_, pref_service);

    auto instance = std::make_unique<FakeHostDeviceTimestampManager>();
    instance_ = instance.get();
    return instance;
  }

  raw_ptr<FakeHostStatusProviderFactory> fake_host_status_provider_factory_;
  raw_ptr<sync_preferences::TestingPrefServiceSyncable>
      expected_testing_pref_service_;

  raw_ptr<FakeHostDeviceTimestampManager, DanglingUntriaged> instance_ =
      nullptr;
};

class FakeAccountStatusChangeDelegateNotifierFactory
    : public AccountStatusChangeDelegateNotifierImpl::Factory {
 public:
  FakeAccountStatusChangeDelegateNotifierFactory(
      FakeHostStatusProviderFactory* fake_host_status_provider_factory,
      sync_preferences::TestingPrefServiceSyncable*
          expected_testing_pref_service,
      FakeHostDeviceTimestampManagerFactory*
          fake_host_device_timestamp_manager_factory,
      OobeCompletionTracker* expected_oobe_completion_tracker)
      : fake_host_status_provider_factory_(fake_host_status_provider_factory),
        expected_testing_pref_service_(expected_testing_pref_service),
        fake_host_device_timestamp_manager_factory_(
            fake_host_device_timestamp_manager_factory),
        expected_oobe_completion_tracker_(expected_oobe_completion_tracker) {}

  FakeAccountStatusChangeDelegateNotifierFactory(
      const FakeAccountStatusChangeDelegateNotifierFactory&) = delete;
  FakeAccountStatusChangeDelegateNotifierFactory& operator=(
      const FakeAccountStatusChangeDelegateNotifierFactory&) = delete;

  ~FakeAccountStatusChangeDelegateNotifierFactory() override = default;

  FakeAccountStatusChangeDelegateNotifier* instance() { return instance_; }

 private:
  // AccountStatusChangeDelegateNotifierImpl::Factory:
  std::unique_ptr<AccountStatusChangeDelegateNotifier> CreateInstance(
      HostStatusProvider* host_status_provider,
      PrefService* pref_service,
      HostDeviceTimestampManager* host_device_timestamp_manager,
      OobeCompletionTracker* oobe_completion_tracker,
      base::Clock* clock) override {
    EXPECT_FALSE(instance_);
    EXPECT_EQ(fake_host_status_provider_factory_->instance(),
              host_status_provider);
    EXPECT_EQ(expected_testing_pref_service_, pref_service);
    EXPECT_EQ(fake_host_device_timestamp_manager_factory_->instance(),
              host_device_timestamp_manager);
    EXPECT_EQ(expected_oobe_completion_tracker_, oobe_completion_tracker);

    auto instance = std::make_unique<FakeAccountStatusChangeDelegateNotifier>();
    instance_ = instance.get();
    return instance;
  }

  raw_ptr<FakeHostStatusProviderFactory> fake_host_status_provider_factory_;
  raw_ptr<sync_preferences::TestingPrefServiceSyncable>
      expected_testing_pref_service_;
  raw_ptr<FakeHostDeviceTimestampManagerFactory>
      fake_host_device_timestamp_manager_factory_;
  raw_ptr<OobeCompletionTracker> expected_oobe_completion_tracker_;

  raw_ptr<FakeAccountStatusChangeDelegateNotifier, DanglingUntriaged>
      instance_ = nullptr;
};

class FakeAndroidSmsAppInstallingStatusObserverFactory
    : public AndroidSmsAppInstallingStatusObserver::Factory {
 public:
  FakeAndroidSmsAppInstallingStatusObserverFactory(
      FakeHostStatusProviderFactory* fake_host_status_provider_factory,
      FakeFeatureStateManagerFactory* fake_feature_state_manager_factory,
      AndroidSmsAppHelperDelegate* expected_android_sms_app_helper_delegate)
      : fake_host_status_provider_factory_(fake_host_status_provider_factory),
        fake_feature_state_manager_factory_(fake_feature_state_manager_factory),
        expected_android_sms_app_helper_delegate_(
            expected_android_sms_app_helper_delegate) {}

  FakeAndroidSmsAppInstallingStatusObserverFactory(
      const FakeAndroidSmsAppInstallingStatusObserverFactory&) = delete;
  FakeAndroidSmsAppInstallingStatusObserverFactory& operator=(
      const FakeAndroidSmsAppInstallingStatusObserverFactory&) = delete;

  ~FakeAndroidSmsAppInstallingStatusObserverFactory() override = default;

 private:
  // AndroidSmsAppInstallingStatusObserver::Factory:
  std::unique_ptr<AndroidSmsAppInstallingStatusObserver> CreateInstance(
      HostStatusProvider* host_status_provider,
      FeatureStateManager* feature_state_manager,
      AndroidSmsAppHelperDelegate* android_sms_app_helper_delegate) override {
    EXPECT_EQ(fake_host_status_provider_factory_->instance(),
              host_status_provider);
    EXPECT_EQ(fake_feature_state_manager_factory_->instance(),
              feature_state_manager);
    EXPECT_EQ(expected_android_sms_app_helper_delegate_,
              android_sms_app_helper_delegate);
    // Only check inputs and return nullptr. We do not want to trigger the
    // AndroidSmsAppInstallingStatusObserver logic in these unit tests.
    return nullptr;
  }

  raw_ptr<FakeHostStatusProviderFactory> fake_host_status_provider_factory_;
  raw_ptr<FakeFeatureStateManagerFactory> fake_feature_state_manager_factory_;
  raw_ptr<AndroidSmsAppHelperDelegate, DanglingUntriaged>
      expected_android_sms_app_helper_delegate_;
};

}  // namespace

class MultiDeviceSetupImplTest : public ::testing::TestWithParam<bool> {
 public:
  MultiDeviceSetupImplTest(const MultiDeviceSetupImplTest&) = delete;
  MultiDeviceSetupImplTest& operator=(const MultiDeviceSetupImplTest&) = delete;

 protected:
  MultiDeviceSetupImplTest()
      : test_devices_(
            multidevice::CreateRemoteDeviceRefListForTest(kNumTestDevices)) {}
  ~MultiDeviceSetupImplTest() override = default;

  void SetUp() override {
    SetDeviceSyncFeatureFlags(IsV1DeviceSyncEnabled());

    test_pref_service_ =
        std::make_unique<sync_preferences::TestingPrefServiceSyncable>();
    fake_device_sync_client_ =
        std::make_unique<device_sync::FakeDeviceSyncClient>();

    fake_auth_token_validator_ = std::make_unique<FakeAuthTokenValidator>();
    fake_auth_token_validator_->set_expected_auth_token(kValidAuthToken);

    fake_oobe_completion_tracker_ = std::make_unique<OobeCompletionTracker>();

    fake_android_sms_app_helper_delegate_ =
        std::make_unique<FakeAndroidSmsAppHelperDelegate>();

    fake_android_sms_pairing_state_tracker_ =
        std::make_unique<FakeAndroidSmsPairingStateTracker>();

    fake_gcm_device_info_provider_ =
        std::make_unique<device_sync::FakeGcmDeviceInfoProvider>(
            cryptauth::GcmDeviceInfo());

    fake_eligible_host_devices_provider_factory_ =
        std::make_unique<FakeEligibleHostDevicesProviderFactory>(
            fake_device_sync_client_.get());
    EligibleHostDevicesProviderImpl::Factory::SetFactoryForTesting(
        fake_eligible_host_devices_provider_factory_.get());

    fake_host_backend_delegate_factory_ =
        std::make_unique<FakeHostBackendDelegateFactory>(
            fake_eligible_host_devices_provider_factory_.get(),
            test_pref_service_.get(), fake_device_sync_client_.get());
    HostBackendDelegateImpl::Factory::SetFactoryForTesting(
        fake_host_backend_delegate_factory_.get());

    fake_host_verifier_factory_ = std::make_unique<FakeHostVerifierFactory>(
        fake_host_backend_delegate_factory_.get(),
        fake_device_sync_client_.get(), test_pref_service_.get());
    HostVerifierImpl::Factory::SetFactoryForTesting(
        fake_host_verifier_factory_.get());

    fake_host_status_provider_factory_ =
        std::make_unique<FakeHostStatusProviderFactory>(
            fake_eligible_host_devices_provider_factory_.get(),
            fake_host_backend_delegate_factory_.get(),
            fake_host_verifier_factory_.get(), fake_device_sync_client_.get());
    HostStatusProviderImpl::Factory::SetFactoryForTesting(
        fake_host_status_provider_factory_.get());

    fake_global_state_feature_manager_factory_ =
        std::make_unique<FakeGlobalStateFeatureManagerFactory>(
            fake_host_status_provider_factory_.get(), test_pref_service_.get(),
            fake_device_sync_client_.get());
    GlobalStateFeatureManagerImpl::Factory::SetFactoryForTesting(
        fake_global_state_feature_manager_factory_.get());

    fake_wifi_sync_notification_controller_factory_ =
        std::make_unique<FakeWifiSyncNotificationControllerFactory>(
            fake_host_status_provider_factory_.get(), test_pref_service_.get(),
            fake_device_sync_client_.get());
    WifiSyncNotificationController::Factory::SetFactoryForTesting(
        fake_wifi_sync_notification_controller_factory_.get());

    fake_grandfathered_easy_unlock_host_disabler_factory_ =
        std::make_unique<FakeGrandfatheredEasyUnlockHostDisablerFactory>(
            fake_host_backend_delegate_factory_.get(),
            fake_device_sync_client_.get(), test_pref_service_.get());
    GrandfatheredEasyUnlockHostDisabler::Factory::SetFactoryForTesting(
        fake_grandfathered_easy_unlock_host_disabler_factory_.get());

    fake_feature_state_manager_factory_ =
        std::make_unique<FakeFeatureStateManagerFactory>(
            test_pref_service_.get(), fake_host_status_provider_factory_.get(),
            fake_device_sync_client_.get(),
            fake_android_sms_pairing_state_tracker_.get(), is_secondary_user_);
    FeatureStateManagerImpl::Factory::SetFactoryForTesting(
        fake_feature_state_manager_factory_.get());

    fake_host_device_timestamp_manager_factory_ =
        std::make_unique<FakeHostDeviceTimestampManagerFactory>(
            fake_host_status_provider_factory_.get(), test_pref_service_.get());
    HostDeviceTimestampManagerImpl::Factory::SetFactoryForTesting(
        fake_host_device_timestamp_manager_factory_.get());

    fake_account_status_change_delegate_notifier_factory_ =
        std::make_unique<FakeAccountStatusChangeDelegateNotifierFactory>(
            fake_host_status_provider_factory_.get(), test_pref_service_.get(),
            fake_host_device_timestamp_manager_factory_.get(),
            fake_oobe_completion_tracker_.get());
    AccountStatusChangeDelegateNotifierImpl::Factory::SetFactoryForTesting(
        fake_account_status_change_delegate_notifier_factory_.get());

    fake_android_sms_app_installing_status_observer_factory_ =
        std::make_unique<FakeAndroidSmsAppInstallingStatusObserverFactory>(
            fake_host_status_provider_factory_.get(),
            fake_feature_state_manager_factory_.get(),
            fake_android_sms_app_helper_delegate_.get());
    AndroidSmsAppInstallingStatusObserver::Factory::SetFactoryForTesting(
        fake_android_sms_app_installing_status_observer_factory_.get());

    multidevice_setup_ = MultiDeviceSetupImpl::Factory::Create(
        test_pref_service_.get(), fake_device_sync_client_.get(),
        fake_auth_token_validator_.get(), fake_oobe_completion_tracker_.get(),
        fake_android_sms_app_helper_delegate_.get(),
        fake_android_sms_pairing_state_tracker_.get(),
        fake_gcm_device_info_provider_.get(), is_secondary_user_);
  }

  void TearDown() override {
    EligibleHostDevicesProviderImpl::Factory::SetFactoryForTesting(nullptr);
    HostBackendDelegateImpl::Factory::SetFactoryForTesting(nullptr);
    HostVerifierImpl::Factory::SetFactoryForTesting(nullptr);
    HostStatusProviderImpl::Factory::SetFactoryForTesting(nullptr);
    GrandfatheredEasyUnlockHostDisabler::Factory::SetFactoryForTesting(nullptr);
    FeatureStateManagerImpl::Factory::SetFactoryForTesting(nullptr);
    HostDeviceTimestampManagerImpl::Factory::SetFactoryForTesting(nullptr);
    AccountStatusChangeDelegateNotifierImpl::Factory::SetFactoryForTesting(
        nullptr);
    AndroidSmsAppInstallingStatusObserver::Factory::SetFactoryForTesting(
        nullptr);
    GlobalStateFeatureManagerImpl::Factory::SetFactoryForTesting(nullptr);
    WifiSyncNotificationController::Factory::SetFactoryForTesting(nullptr);
  }

  bool IsV1DeviceSyncEnabled() { return GetParam(); }

  void CallSetAccountStatusChangeDelegate() {
    EXPECT_FALSE(fake_account_status_change_delegate_);

    fake_account_status_change_delegate_ =
        std::make_unique<FakeAccountStatusChangeDelegate>();

    EXPECT_FALSE(fake_account_status_change_delegate_notifier()->delegate());
    multidevice_setup_->SetAccountStatusChangeDelegate(
        fake_account_status_change_delegate_->GenerateRemote());
    EXPECT_TRUE(fake_account_status_change_delegate_notifier()->delegate());
  }

  multidevice::RemoteDeviceList CallGetEligibleHostDevices() {
    base::RunLoop run_loop;
    multidevice_setup_->GetEligibleHostDevices(
        base::BindOnce(&MultiDeviceSetupImplTest::OnEligibleDevicesFetched,
                       base::Unretained(this), run_loop.QuitClosure()));
    run_loop.Run();

    multidevice::RemoteDeviceList eligible_devices_list =
        *last_eligible_devices_list_;
    last_eligible_devices_list_.reset();

    return eligible_devices_list;
  }

  std::vector<mojom::HostDevicePtr> CallGetEligibleActiveHostDevices() {
    base::RunLoop run_loop;
    multidevice_setup_->GetEligibleActiveHostDevices(base::BindOnce(
        &MultiDeviceSetupImplTest::OnEligibleActiveHostDevicesFetched,
        base::Unretained(this), run_loop.QuitClosure()));
    run_loop.Run();

    std::vector<mojom::HostDevicePtr> eligible_devices_list =
        std::move(*last_eligible_active_devices_list_);
    last_eligible_active_devices_list_.reset();

    return eligible_devices_list;
  }

  bool CallSetHostDevice(
      const std::string& host_instance_id_or_legacy_device_id,
      const std::string& auth_token) {
    base::RunLoop run_loop;
    multidevice_setup_->SetHostDevice(
        host_instance_id_or_legacy_device_id, auth_token,
        base::BindOnce(&MultiDeviceSetupImplTest::OnSetHostDeviceResult,
                       base::Unretained(this), run_loop.QuitClosure()));
    run_loop.Run();

    bool success = *last_set_host_success_;
    last_set_host_success_.reset();

    return success;
  }

  bool CallSetHostDeviceWithoutAuth(
      const std::string& host_instance_id_or_legacy_device_id) {
    base::RunLoop run_loop;
    multidevice_setup_->SetHostDeviceWithoutAuthToken(
        host_instance_id_or_legacy_device_id,
        base::BindOnce(
            &MultiDeviceSetupImplTest::OnSetHostDeviceWithoutAuthResult,
            base::Unretained(this), run_loop.QuitClosure()));
    run_loop.Run();

    bool success = *last_set_host_without_auth_success_;
    last_set_host_without_auth_success_.reset();

    return success;
  }

  std::pair<mojom::HostStatus, std::optional<multidevice::RemoteDevice>>
  CallGetHostStatus() {
    base::RunLoop run_loop;
    multidevice_setup_->GetHostStatus(
        base::BindOnce(&MultiDeviceSetupImplTest::OnHostStatusReceived,
                       base::Unretained(this), run_loop.QuitClosure()));
    run_loop.Run();

    std::pair<mojom::HostStatus, std::optional<multidevice::RemoteDevice>>
        host_status_update = *last_host_status_;
    last_host_status_.reset();

    return host_status_update;
  }

  bool CallSetFeatureEnabledState(
      mojom::Feature feature,
      bool enabled,
      const std::optional<std::string>& auth_token) {
    base::RunLoop run_loop;
    multidevice_setup_->SetFeatureEnabledState(
        feature, enabled, auth_token,
        base::BindOnce(&MultiDeviceSetupImplTest::OnSetFeatureEnabled,
                       base::Unretained(this), run_loop.QuitClosure()));
    run_loop.Run();

    bool success = *last_set_feature_enabled_state_success_;
    last_set_feature_enabled_state_success_.reset();

    return success;
  }

  base::flat_map<mojom::Feature, mojom::FeatureState> CallGetFeatureStates() {
    base::RunLoop run_loop;
    multidevice_setup_->GetFeatureStates(
        base::BindOnce(&MultiDeviceSetupImplTest::OnGetFeatureStates,
                       base::Unretained(this), run_loop.QuitClosure()));
    run_loop.Run();

    base::flat_map<mojom::Feature, mojom::FeatureState> feature_states_map =
        *last_get_feature_states_result_;
    last_get_feature_states_result_.reset();

    return feature_states_map;
  }

  bool CallRetrySetHostNow() {
    base::RunLoop run_loop;
    multidevice_setup_->RetrySetHostNow(
        base::BindOnce(&MultiDeviceSetupImplTest::OnHostRetried,
                       base::Unretained(this), run_loop.QuitClosure()));
    run_loop.Run();

    bool success = *last_retry_success_;
    last_retry_success_.reset();

    return success;
  }

  bool CallTriggerEventForDebugging(mojom::EventTypeForDebugging type) {
    base::RunLoop run_loop;
    multidevice_setup_->TriggerEventForDebugging(
        type, base::BindOnce(&MultiDeviceSetupImplTest::OnDebugEventTriggered,
                             base::Unretained(this), run_loop.QuitClosure()));
    run_loop.Run();

    bool success = *last_debug_event_success_;
    last_debug_event_success_.reset();

    // If the delegate was set, fire off any pending Mojo messages.
    if (success)
      fake_account_status_change_delegate_notifier()->FlushForTesting();

    return success;
  }

  std::optional<std::string> CallGetQuickStartPhoneInstanceID() {
    base::RunLoop run_loop;
    multidevice_setup_->GetQuickStartPhoneInstanceID(base::BindOnce(
        &MultiDeviceSetupImplTest::OnGetQuickStartPhoneInstanceID,
        base::Unretained(this), run_loop.QuitClosure()));
    run_loop.Run();

    std::optional<std::string> qs_phone_instance_id =
        last_qs_phone_instance_id_;
    last_qs_phone_instance_id_.reset();
    return qs_phone_instance_id;
  }

  void VerifyCurrentHostStatus(
      mojom::HostStatus host_status,
      const std::optional<multidevice::RemoteDeviceRef>& host_device,
      FakeHostStatusObserver* observer = nullptr,
      size_t expected_observer_index = 0u) {
    std::pair<mojom::HostStatus, std::optional<multidevice::RemoteDevice>>
        host_status_and_device = CallGetHostStatus();
    EXPECT_EQ(host_status, host_status_and_device.first);
    EXPECT_EQ(RefToRaw(host_device), host_status_and_device.second);

    if (!observer)
      return;

    EXPECT_EQ(host_status,
              observer->host_status_updates()[expected_observer_index].first);
    EXPECT_EQ(RefToRaw(host_device),
              observer->host_status_updates()[expected_observer_index].second);
  }

  void SendPendingObserverMessages() {
    MultiDeviceSetupImpl* derived_ptr =
        static_cast<MultiDeviceSetupImpl*>(multidevice_setup_.get());
    derived_ptr->FlushForTesting();
  }

  FakeAccountStatusChangeDelegate* fake_account_status_change_delegate() {
    return fake_account_status_change_delegate_.get();
  }

  FakeEligibleHostDevicesProvider* fake_eligible_host_devices_provider() {
    return fake_eligible_host_devices_provider_factory_->instance();
  }

  FakeHostBackendDelegate* fake_host_backend_delegate() {
    return fake_host_backend_delegate_factory_->instance();
  }

  FakeHostVerifier* fake_host_verifier() {
    return fake_host_verifier_factory_->instance();
  }

  FakeHostStatusProvider* fake_host_status_provider() {
    return fake_host_status_provider_factory_->instance();
  }

  FakeFeatureStateManager* fake_feature_state_manager() {
    return fake_feature_state_manager_factory_->instance();
  }

  FakeHostDeviceTimestampManager* fake_host_device_timestamp_manager() {
    return fake_host_device_timestamp_manager_factory_->instance();
  }

  FakeAccountStatusChangeDelegateNotifier*
  fake_account_status_change_delegate_notifier() {
    return fake_account_status_change_delegate_notifier_factory_->instance();
  }

  multidevice::RemoteDeviceRefList& test_devices() { return test_devices_; }

  MultiDeviceSetupBase* multidevice_setup() { return multidevice_setup_.get(); }

 private:
  void SetDeviceSyncFeatureFlags(bool use_v1) {
    std::vector<base::test::FeatureRef> enabled_features;
    std::vector<base::test::FeatureRef> disabled_features;

    // These flags have no direct effect; however, v2 Enrollment and v2
    // DeviceSync are prerequisites for disabling v1 DeviceSync.
    enabled_features.push_back(features::kCryptAuthV2Enrollment);
    enabled_features.push_back(features::kCryptAuthV2DeviceSync);

    if (use_v1) {
      disabled_features.push_back(features::kDisableCryptAuthV1DeviceSync);
    } else {
      enabled_features.push_back(features::kDisableCryptAuthV1DeviceSync);
    }

    scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
  }

  void OnEligibleDevicesFetched(
      base::OnceClosure quit_closure,
      const multidevice::RemoteDeviceList& eligible_devices_list) {
    EXPECT_FALSE(last_eligible_devices_list_);
    last_eligible_devices_list_ = eligible_devices_list;
    std::move(quit_closure).Run();
  }

  void OnEligibleActiveHostDevicesFetched(
      base::OnceClosure quit_closure,
      std::vector<mojom::HostDevicePtr> eligible_active_devices_list) {
    EXPECT_FALSE(last_eligible_active_devices_list_);
    last_eligible_active_devices_list_ =
        std::move(eligible_active_devices_list);
    std::move(quit_closure).Run();
  }

  void OnSetHostDeviceResult(base::OnceClosure quit_closure, bool success) {
    EXPECT_FALSE(last_set_host_success_);
    last_set_host_success_ = success;
    std::move(quit_closure).Run();
  }

  void OnSetHostDeviceWithoutAuthResult(base::OnceClosure quit_closure,
                                        bool success) {
    EXPECT_FALSE(last_set_host_without_auth_success_);
    last_set_host_without_auth_success_ = success;
    std::move(quit_closure).Run();
  }

  void OnHostStatusReceived(
      base::OnceClosure quit_closure,
      mojom::HostStatus host_status,
      const std::optional<multidevice::RemoteDevice>& host_device) {
    EXPECT_FALSE(last_host_status_);
    last_host_status_ = std::make_pair(host_status, host_device);
    std::move(quit_closure).Run();
  }

  void OnSetFeatureEnabled(base::OnceClosure quit_closure, bool success) {
    EXPECT_FALSE(last_set_feature_enabled_state_success_);
    last_set_feature_enabled_state_success_ = success;
    std::move(quit_closure).Run();
  }

  void OnGetFeatureStates(
      base::OnceClosure quit_closure,
      const base::flat_map<mojom::Feature, mojom::FeatureState>&
          feature_states_map) {
    EXPECT_FALSE(last_get_feature_states_result_);
    last_get_feature_states_result_ = feature_states_map;
    std::move(quit_closure).Run();
  }

  void OnHostRetried(base::OnceClosure quit_closure, bool success) {
    EXPECT_FALSE(last_retry_success_);
    last_retry_success_ = success;
    std::move(quit_closure).Run();
  }

  void OnDebugEventTriggered(base::OnceClosure quit_closure, bool success) {
    EXPECT_FALSE(last_debug_event_success_);
    last_debug_event_success_ = success;
    std::move(quit_closure).Run();
  }

  void OnGetQuickStartPhoneInstanceID(
      base::OnceClosure quit_closure,
      const std::optional<std::string>& qs_phone_instance_id) {
    EXPECT_EQ(std::nullopt, last_qs_phone_instance_id_);
    last_qs_phone_instance_id_ = qs_phone_instance_id;
    std::move(quit_closure).Run();
  }

  base::test::TaskEnvironment task_environment_;

  multidevice::RemoteDeviceRefList test_devices_;

  std::unique_ptr<sync_preferences::TestingPrefServiceSyncable>
      test_pref_service_;
  std::unique_ptr<device_sync::FakeDeviceSyncClient> fake_device_sync_client_;
  std::unique_ptr<FakeAuthTokenValidator> fake_auth_token_validator_;
  std::unique_ptr<OobeCompletionTracker> fake_oobe_completion_tracker_;
  std::unique_ptr<device_sync::FakeGcmDeviceInfoProvider>
      fake_gcm_device_info_provider_;
  bool is_secondary_user_ = false;

  std::unique_ptr<FakeEligibleHostDevicesProviderFactory>
      fake_eligible_host_devices_provider_factory_;
  std::unique_ptr<FakeHostBackendDelegateFactory>
      fake_host_backend_delegate_factory_;
  std::unique_ptr<FakeHostVerifierFactory> fake_host_verifier_factory_;
  std::unique_ptr<FakeHostStatusProviderFactory>
      fake_host_status_provider_factory_;
  std::unique_ptr<FakeGlobalStateFeatureManagerFactory>
      fake_global_state_feature_manager_factory_;
  std::unique_ptr<FakeWifiSyncNotificationControllerFactory>
      fake_wifi_sync_notification_controller_factory_;
  std::unique_ptr<FakeGrandfatheredEasyUnlockHostDisablerFactory>
      fake_grandfathered_easy_unlock_host_disabler_factory_;
  std::unique_ptr<FakeFeatureStateManagerFactory>
      fake_feature_state_manager_factory_;
  std::unique_ptr<FakeHostDeviceTimestampManagerFactory>
      fake_host_device_timestamp_manager_factory_;
  std::unique_ptr<FakeAccountStatusChangeDelegateNotifierFactory>
      fake_account_status_change_delegate_notifier_factory_;
  std::unique_ptr<FakeAndroidSmsAppInstallingStatusObserverFactory>
      fake_android_sms_app_installing_status_observer_factory_;
  std::unique_ptr<FakeAndroidSmsAppHelperDelegate>
      fake_android_sms_app_helper_delegate_;
  std::unique_ptr<FakeAndroidSmsPairingStateTracker>
      fake_android_sms_pairing_state_tracker_;

  std::unique_ptr<FakeAccountStatusChangeDelegate>
      fake_account_status_change_delegate_;

  base::test::ScopedFeatureList scoped_feature_list_;

  std::optional<bool> last_debug_event_success_;
  std::optional<multidevice::RemoteDeviceList> last_eligible_devices_list_;
  std::optional<std::vector<mojom::HostDevicePtr>>
      last_eligible_active_devices_list_;
  std::optional<bool> last_set_host_success_;
  std::optional<bool> last_set_host_without_auth_success_;
  std::optional<
      std::pair<mojom::HostStatus, std::optional<multidevice::RemoteDevice>>>
      last_host_status_;
  std::optional<bool> last_set_feature_enabled_state_success_;
  std::optional<base::flat_map<mojom::Feature, mojom::FeatureState>>
      last_get_feature_states_result_;
  std::optional<bool> last_retry_success_;
  std::optional<std::string> last_qs_phone_instance_id_;

  std::unique_ptr<MultiDeviceSetupBase> multidevice_setup_;
};

TEST_P(MultiDeviceSetupImplTest, AccountStatusChangeDelegate) {
  // All requests to trigger debug events should fail before the delegate has
  // been set.
  EXPECT_FALSE(CallTriggerEventForDebugging(
      mojom::EventTypeForDebugging::kNewUserPotentialHostExists));
  EXPECT_FALSE(CallTriggerEventForDebugging(
      mojom::EventTypeForDebugging::kExistingUserConnectedHostSwitched));
  EXPECT_FALSE(CallTriggerEventForDebugging(
      mojom::EventTypeForDebugging::kExistingUserNewChromebookAdded));

  CallSetAccountStatusChangeDelegate();

  // All debug trigger events should now succeed.
  EXPECT_TRUE(CallTriggerEventForDebugging(
      mojom::EventTypeForDebugging::kNewUserPotentialHostExists));
  EXPECT_EQ(1u, fake_account_status_change_delegate()
                    ->num_new_user_potential_host_events_handled());

  EXPECT_TRUE(CallTriggerEventForDebugging(
      mojom::EventTypeForDebugging::kExistingUserConnectedHostSwitched));
  EXPECT_EQ(1u, fake_account_status_change_delegate()
                    ->num_existing_user_host_switched_events_handled());

  EXPECT_TRUE(CallTriggerEventForDebugging(
      mojom::EventTypeForDebugging::kExistingUserNewChromebookAdded));
  EXPECT_EQ(1u, fake_account_status_change_delegate()
                    ->num_existing_user_chromebook_added_events_handled());
}

// The feature mojom::Feature::kInstantTethering is used throughout this test
// because it never requires authentication for either enabling or disabling.
TEST_P(MultiDeviceSetupImplTest, FeatureStateChanges_NoAuthTokenRequired) {
  auto observer = std::make_unique<FakeFeatureStateObserver>();
  multidevice_setup()->AddFeatureStateObserver(observer->GenerateRemote());

  EXPECT_EQ(mojom::FeatureState::kUnavailableNoVerifiedHost_NoEligibleHosts,
            CallGetFeatureStates()[mojom::Feature::kInstantTethering]);

  fake_feature_state_manager()->SetFeatureState(
      mojom::Feature::kInstantTethering,
      mojom::FeatureState::kNotSupportedByChromebook);
  SendPendingObserverMessages();
  EXPECT_EQ(mojom::FeatureState::kNotSupportedByChromebook,
            CallGetFeatureStates()[mojom::Feature::kInstantTethering]);
  EXPECT_EQ(1u, observer->feature_state_updates().size());

  fake_feature_state_manager()->SetFeatureState(
      mojom::Feature::kInstantTethering, mojom::FeatureState::kEnabledByUser);
  SendPendingObserverMessages();
  EXPECT_EQ(mojom::FeatureState::kEnabledByUser,
            CallGetFeatureStates()[mojom::Feature::kInstantTethering]);
  EXPECT_EQ(2u, observer->feature_state_updates().size());

  EXPECT_TRUE(CallSetFeatureEnabledState(mojom::Feature::kInstantTethering,
                                         false /* enabled */,
                                         std::nullopt /* auth_token */));
  SendPendingObserverMessages();
  EXPECT_EQ(mojom::FeatureState::kDisabledByUser,
            CallGetFeatureStates()[mojom::Feature::kInstantTethering]);
  EXPECT_EQ(3u, observer->feature_state_updates().size());
}

// mojom::Feature::kSmartLock requires authentication when attempting to enable.
TEST_P(MultiDeviceSetupImplTest,
       FeatureStateChanges_AuthTokenRequired_SmartLock) {
  auto observer = std::make_unique<FakeFeatureStateObserver>();
  multidevice_setup()->AddFeatureStateObserver(observer->GenerateRemote());

  EXPECT_EQ(mojom::FeatureState::kUnavailableNoVerifiedHost_NoEligibleHosts,
            CallGetFeatureStates()[mojom::Feature::kSmartLock]);

  fake_feature_state_manager()->SetFeatureState(
      mojom::Feature::kSmartLock, mojom::FeatureState::kEnabledByUser);
  SendPendingObserverMessages();
  EXPECT_EQ(mojom::FeatureState::kEnabledByUser,
            CallGetFeatureStates()[mojom::Feature::kSmartLock]);
  EXPECT_EQ(1u, observer->feature_state_updates().size());

  // No authentication is required to disable the feature.
  EXPECT_TRUE(CallSetFeatureEnabledState(mojom::Feature::kSmartLock,
                                         false /* enabled */,
                                         std::nullopt /* auth_token */));
  SendPendingObserverMessages();
  EXPECT_EQ(mojom::FeatureState::kDisabledByUser,
            CallGetFeatureStates()[mojom::Feature::kSmartLock]);
  EXPECT_EQ(2u, observer->feature_state_updates().size());

  // However, authentication is required to enable the feature.
  EXPECT_FALSE(CallSetFeatureEnabledState(
      mojom::Feature::kSmartLock, true /* enabled */, "invalidAuthToken"));
  SendPendingObserverMessages();
  EXPECT_EQ(mojom::FeatureState::kDisabledByUser,
            CallGetFeatureStates()[mojom::Feature::kSmartLock]);
  EXPECT_EQ(2u, observer->feature_state_updates().size());

  // Now, send a valid auth token; it should successfully enable.
  EXPECT_TRUE(CallSetFeatureEnabledState(mojom::Feature::kSmartLock,
                                         true /* enabled */, kValidAuthToken));
  SendPendingObserverMessages();
  EXPECT_EQ(mojom::FeatureState::kEnabledByUser,
            CallGetFeatureStates()[mojom::Feature::kSmartLock]);
  EXPECT_EQ(3u, observer->feature_state_updates().size());
}

// mojom::Feature::kBetterTogetherSuite requires authentication when attempting
// to enable, but only if the Smart Lock pref is enabled.
TEST_P(MultiDeviceSetupImplTest,
       FeatureStateChanges_AuthTokenRequired_BetterTogetherSuite) {
  auto observer = std::make_unique<FakeFeatureStateObserver>();
  multidevice_setup()->AddFeatureStateObserver(observer->GenerateRemote());

  EXPECT_EQ(mojom::FeatureState::kUnavailableNoVerifiedHost_NoEligibleHosts,
            CallGetFeatureStates()[mojom::Feature::kBetterTogetherSuite]);

  fake_feature_state_manager()->SetFeatureState(
      mojom::Feature::kBetterTogetherSuite,
      mojom::FeatureState::kEnabledByUser);
  SendPendingObserverMessages();
  EXPECT_EQ(mojom::FeatureState::kEnabledByUser,
            CallGetFeatureStates()[mojom::Feature::kBetterTogetherSuite]);
  EXPECT_EQ(1u, observer->feature_state_updates().size());

  // No authentication is required to disable the feature.
  EXPECT_TRUE(CallSetFeatureEnabledState(mojom::Feature::kBetterTogetherSuite,
                                         false /* enabled */,
                                         std::nullopt /* auth_token */));
  SendPendingObserverMessages();
  EXPECT_EQ(mojom::FeatureState::kDisabledByUser,
            CallGetFeatureStates()[mojom::Feature::kBetterTogetherSuite]);
  EXPECT_EQ(2u, observer->feature_state_updates().size());

  // Authentication is required to enable the feature if SmartLock's state is
  // kUnavailableInsufficientSecurity.
  fake_feature_state_manager()->SetFeatureState(
      mojom::Feature::kSmartLock,
      mojom::FeatureState::kUnavailableInsufficientSecurity);
  EXPECT_FALSE(CallSetFeatureEnabledState(mojom::Feature::kBetterTogetherSuite,
                                          true /* enabled */,
                                          "invalidAuthToken"));
  SendPendingObserverMessages();
  EXPECT_EQ(mojom::FeatureState::kDisabledByUser,
            CallGetFeatureStates()[mojom::Feature::kBetterTogetherSuite]);
  EXPECT_EQ(3u, observer->feature_state_updates().size());

  // Now, send a valid auth token; it should successfully enable.
  EXPECT_TRUE(CallSetFeatureEnabledState(mojom::Feature::kBetterTogetherSuite,
                                         true /* enabled */, kValidAuthToken));
  SendPendingObserverMessages();
  EXPECT_EQ(mojom::FeatureState::kEnabledByUser,
            CallGetFeatureStates()[mojom::Feature::kBetterTogetherSuite]);
  EXPECT_EQ(4u, observer->feature_state_updates().size());

  // Disable one more time.
  EXPECT_TRUE(CallSetFeatureEnabledState(mojom::Feature::kBetterTogetherSuite,
                                         false /* enabled */,
                                         std::nullopt /* auth_token */));
  SendPendingObserverMessages();
  EXPECT_EQ(mojom::FeatureState::kDisabledByUser,
            CallGetFeatureStates()[mojom::Feature::kBetterTogetherSuite]);
  EXPECT_EQ(5u, observer->feature_state_updates().size());

  // Authentication is required to enable the feature if SmartLock's state is
  // kUnavailableSuiteDisabled.
  fake_feature_state_manager()->SetFeatureState(
      mojom::Feature::kSmartLock,
      mojom::FeatureState::kUnavailableSuiteDisabled);
  EXPECT_FALSE(CallSetFeatureEnabledState(mojom::Feature::kBetterTogetherSuite,
                                          true /* enabled */,
                                          "invalidAuthToken"));
  SendPendingObserverMessages();
  EXPECT_EQ(mojom::FeatureState::kDisabledByUser,
            CallGetFeatureStates()[mojom::Feature::kBetterTogetherSuite]);
  EXPECT_EQ(6u, observer->feature_state_updates().size());

  // Now, send a valid auth token; it should successfully enable.
  EXPECT_TRUE(CallSetFeatureEnabledState(mojom::Feature::kBetterTogetherSuite,
                                         true /* enabled */, kValidAuthToken));
  SendPendingObserverMessages();
  EXPECT_EQ(mojom::FeatureState::kEnabledByUser,
            CallGetFeatureStates()[mojom::Feature::kBetterTogetherSuite]);
  EXPECT_EQ(7u, observer->feature_state_updates().size());
}

TEST_P(MultiDeviceSetupImplTest, ComprehensiveHostTest) {
  // Start with no eligible devices.
  EXPECT_TRUE(CallGetEligibleHostDevices().empty());
  VerifyCurrentHostStatus(mojom::HostStatus::kNoEligibleHosts,
                          std::nullopt /* host_device */);

  // Cannot retry without a host.
  EXPECT_FALSE(CallRetrySetHostNow());

  // Add a status observer.
  auto observer = std::make_unique<FakeHostStatusObserver>();
  multidevice_setup()->AddHostStatusObserver(observer->GenerateRemote());

  // Simulate a sync occurring; now, all of the test devices are eligible hosts.
  fake_eligible_host_devices_provider()->set_eligible_host_devices(
      test_devices());
  EXPECT_EQ(RefListToRawList(test_devices()), CallGetEligibleHostDevices());
  fake_host_status_provider()->SetHostWithStatus(
      mojom::HostStatus::kEligibleHostExistsButNoHostSet,
      std::nullopt /* host_device */);
  SendPendingObserverMessages();
  VerifyCurrentHostStatus(mojom::HostStatus::kEligibleHostExistsButNoHostSet,
                          std::nullopt /* host_device */, observer.get(),
                          0u /* expected_observer_index */);

  // There are eligible hosts, but none is set; thus, cannot retry.
  EXPECT_FALSE(CallRetrySetHostNow());

  // Set an invalid host as the host device; this should fail.
  EXPECT_FALSE(CallSetHostDevice("invalidHostDeviceId", kValidAuthToken));
  EXPECT_FALSE(fake_host_backend_delegate()->HasPendingHostRequest());

  // Set device 0 as the host; this should succeed.
  std::string host_id = IsV1DeviceSyncEnabled()
                            ? test_devices()[0].GetDeviceId()
                            : test_devices()[0].instance_id();
  EXPECT_TRUE(CallSetHostDevice(host_id, kValidAuthToken));
  EXPECT_TRUE(fake_host_backend_delegate()->HasPendingHostRequest());
  EXPECT_EQ(test_devices()[0],
            fake_host_backend_delegate()->GetPendingHostRequest());
  fake_host_status_provider()->SetHostWithStatus(
      mojom::HostStatus::kHostSetLocallyButWaitingForBackendConfirmation,
      test_devices()[0]);
  SendPendingObserverMessages();
  VerifyCurrentHostStatus(
      mojom::HostStatus::kHostSetLocallyButWaitingForBackendConfirmation,
      test_devices()[0], observer.get(), 1u /* expected_observer_index */);

  // It should now be possible to retry.
  EXPECT_TRUE(CallRetrySetHostNow());

  // Simulate the retry succeeding and the host being set on the back-end.
  fake_host_backend_delegate()->NotifyHostChangedOnBackend(test_devices()[0]);
  fake_host_status_provider()->SetHostWithStatus(
      mojom::HostStatus::kHostSetButNotYetVerified, test_devices()[0]);
  SendPendingObserverMessages();
  VerifyCurrentHostStatus(mojom::HostStatus::kHostSetButNotYetVerified,
                          test_devices()[0], observer.get(),
                          2u /* expected_observer_index */);

  // It should still be possible to retry (this time, retrying verification).
  EXPECT_TRUE(CallRetrySetHostNow());

  // Simulate verification succeeding.
  fake_host_verifier()->set_is_host_verified(true);
  fake_host_status_provider()->SetHostWithStatus(
      mojom::HostStatus::kHostVerified, test_devices()[0]);
  SendPendingObserverMessages();
  VerifyCurrentHostStatus(mojom::HostStatus::kHostVerified, test_devices()[0],
                          observer.get(), 3u /* expected_observer_index */);

  // Remove the host.
  multidevice_setup()->RemoveHostDevice();
  fake_host_verifier()->set_is_host_verified(false);
  fake_host_status_provider()->SetHostWithStatus(
      mojom::HostStatus::kEligibleHostExistsButNoHostSet,
      std::nullopt /* host_device */);
  SendPendingObserverMessages();
  VerifyCurrentHostStatus(mojom::HostStatus::kEligibleHostExistsButNoHostSet,
                          std::nullopt /* host_device */, observer.get(),
                          4u /* expected_observer_index */);

  // Simulate the host being removed on the back-end.
  fake_host_backend_delegate()->NotifyHostChangedOnBackend(std::nullopt);
}

TEST_P(MultiDeviceSetupImplTest, TestGetEligibleActiveHosts) {
  // Start with no eligible devices.
  EXPECT_TRUE(CallGetEligibleActiveHostDevices().empty());

  multidevice::DeviceWithConnectivityStatusList host_device_list;
  for (auto remote_device_ref : test_devices()) {
    host_device_list.emplace_back(multidevice::DeviceWithConnectivityStatus(
        remote_device_ref, cryptauthv2::ConnectivityStatus::ONLINE));
  }
  // Simulate a sync occurring; now, all of the test devices are eligible hosts.
  fake_eligible_host_devices_provider()->set_eligible_active_host_devices(
      host_device_list);

  std::vector<mojom::HostDevicePtr> result_hosts =
      CallGetEligibleActiveHostDevices();
  for (size_t i = 0; i < kNumTestDevices; i++) {
    EXPECT_EQ(*GetMutableRemoteDevice(test_devices()[i]),
              result_hosts[i]->remote_device);
    EXPECT_EQ(cryptauthv2::ConnectivityStatus::ONLINE,
              result_hosts[i]->connectivity_status);
  }
}

TEST_P(MultiDeviceSetupImplTest, TestSetHostDevice_InvalidAuthToken) {
  // Start valid eligible host devices.
  fake_eligible_host_devices_provider()->set_eligible_host_devices(
      test_devices());
  EXPECT_EQ(RefListToRawList(test_devices()), CallGetEligibleHostDevices());
  fake_host_status_provider()->SetHostWithStatus(
      mojom::HostStatus::kEligibleHostExistsButNoHostSet,
      std::nullopt /* host_device */);

  // Set a valid host as the host device, but pass an invalid token.
  std::string host_id = IsV1DeviceSyncEnabled()
                            ? test_devices()[0].GetDeviceId()
                            : test_devices()[0].instance_id();
  EXPECT_FALSE(CallSetHostDevice(host_id, "invalidAuthToken"));
  EXPECT_FALSE(fake_host_backend_delegate()->HasPendingHostRequest());
}

TEST_P(MultiDeviceSetupImplTest, TestSetHostDeviceWithoutAuthToken) {
  // Add a status observer.
  auto observer = std::make_unique<FakeHostStatusObserver>();
  multidevice_setup()->AddHostStatusObserver(observer->GenerateRemote());

  // Start valid eligible host devices.
  fake_eligible_host_devices_provider()->set_eligible_host_devices(
      test_devices());
  EXPECT_EQ(RefListToRawList(test_devices()), CallGetEligibleHostDevices());
  fake_host_status_provider()->SetHostWithStatus(
      mojom::HostStatus::kEligibleHostExistsButNoHostSet,
      std::nullopt /* host_device */);
  SendPendingObserverMessages();
  VerifyCurrentHostStatus(mojom::HostStatus::kEligibleHostExistsButNoHostSet,
                          std::nullopt /* host_device */, observer.get(),
                          0u /* expected_observer_index */);

  // Set a valid host as the host device without an auth token.
  std::string host_id = IsV1DeviceSyncEnabled()
                            ? test_devices()[0].GetDeviceId()
                            : test_devices()[0].instance_id();
  EXPECT_TRUE(CallSetHostDeviceWithoutAuth(host_id));
  EXPECT_TRUE(fake_host_backend_delegate()->HasPendingHostRequest());
  EXPECT_EQ(test_devices()[0],
            fake_host_backend_delegate()->GetPendingHostRequest());
  fake_host_status_provider()->SetHostWithStatus(
      mojom::HostStatus::kHostSetLocallyButWaitingForBackendConfirmation,
      test_devices()[0]);
  SendPendingObserverMessages();
  VerifyCurrentHostStatus(
      mojom::HostStatus::kHostSetLocallyButWaitingForBackendConfirmation,
      test_devices()[0], observer.get(), 1u /* expected_observer_index */);
}

TEST_P(MultiDeviceSetupImplTest,
       TestSetHostDevice_AcceptInstanceIdOrLegacyDeviceId) {
  // We can set the host using the legacy device ID or the Instance ID if and
  // only if v1 DeviceSync is enabled.
  if (!IsV1DeviceSyncEnabled())
    return;

  // Add a status observer.
  auto observer = std::make_unique<FakeHostStatusObserver>();
  multidevice_setup()->AddHostStatusObserver(observer->GenerateRemote());

  // Start valid eligible host devices.
  fake_eligible_host_devices_provider()->set_eligible_host_devices(
      test_devices());
  EXPECT_EQ(RefListToRawList(test_devices()), CallGetEligibleHostDevices());
  fake_host_status_provider()->SetHostWithStatus(
      mojom::HostStatus::kEligibleHostExistsButNoHostSet,
      std::nullopt /* host_device */);
  SendPendingObserverMessages();
  VerifyCurrentHostStatus(mojom::HostStatus::kEligibleHostExistsButNoHostSet,
                          std::nullopt /* host_device */, observer.get(),
                          0u /* expected_observer_index */);

  // Set the host device using its legacy device ID.
  EXPECT_TRUE(
      CallSetHostDevice(test_devices()[0].GetDeviceId(), kValidAuthToken));
  EXPECT_TRUE(fake_host_backend_delegate()->HasPendingHostRequest());
  EXPECT_EQ(test_devices()[0],
            fake_host_backend_delegate()->GetPendingHostRequest());
  fake_host_status_provider()->SetHostWithStatus(
      mojom::HostStatus::kHostSetLocallyButWaitingForBackendConfirmation,
      test_devices()[0]);
  SendPendingObserverMessages();
  VerifyCurrentHostStatus(
      mojom::HostStatus::kHostSetLocallyButWaitingForBackendConfirmation,
      test_devices()[0], observer.get(), 1u /* expected_observer_index */);

  // Set the host device using its Instance ID.
  EXPECT_TRUE(
      CallSetHostDevice(test_devices()[1].instance_id(), kValidAuthToken));
  EXPECT_TRUE(fake_host_backend_delegate()->HasPendingHostRequest());
  EXPECT_EQ(test_devices()[1],
            fake_host_backend_delegate()->GetPendingHostRequest());
  fake_host_status_provider()->SetHostWithStatus(
      mojom::HostStatus::kHostSetLocallyButWaitingForBackendConfirmation,
      test_devices()[1]);
  SendPendingObserverMessages();
  VerifyCurrentHostStatus(
      mojom::HostStatus::kHostSetLocallyButWaitingForBackendConfirmation,
      test_devices()[1], observer.get(), 2u /* expected_observer_index */);
}

TEST_P(MultiDeviceSetupImplTest, SetAndGetQuickStartPhoneInstanceID) {
  EXPECT_EQ(std::nullopt, CallGetQuickStartPhoneInstanceID());
  const std::string& expected_qs_phone_instance_id = "qsPhoneInstanceID1";
  multidevice_setup()->SetQuickStartPhoneInstanceID(
      expected_qs_phone_instance_id);
  EXPECT_EQ(expected_qs_phone_instance_id, CallGetQuickStartPhoneInstanceID());
}

// Runs tests twice; once with v1 DeviceSync enabled and once with it disabled.
// TODO(crbug.com/40105247): Remove when v1 DeviceSync is disabled,
// when all devices should have an Instance ID.
INSTANTIATE_TEST_SUITE_P(All, MultiDeviceSetupImplTest, ::testing::Bool());

}  // namespace multidevice_setup

}  // namespace ash