chromium/chromeos/ash/services/multidevice_setup/multidevice_setup_service_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 "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.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/fake_account_status_change_delegate.h"
#include "chromeos/ash/services/multidevice_setup/fake_feature_state_observer.h"
#include "chromeos/ash/services/multidevice_setup/fake_host_status_observer.h"
#include "chromeos/ash/services/multidevice_setup/multidevice_setup_impl.h"
#include "chromeos/ash/services/multidevice_setup/multidevice_setup_service.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/fake_multidevice_setup.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 "components/sync_preferences/testing_pref_service_syncable.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace ash {

namespace multidevice_setup {

namespace {

const size_t kNumTestDevices = 3;

class FakeMultiDeviceSetupFactory : public MultiDeviceSetupImpl::Factory {
 public:
  FakeMultiDeviceSetupFactory(
      sync_preferences::TestingPrefServiceSyncable*
          expected_testing_pref_service,
      device_sync::FakeDeviceSyncClient* expected_device_sync_client,
      FakeAuthTokenValidator* expected_auth_token_validator,
      OobeCompletionTracker* expected_oobe_completion_tracker,
      FakeAndroidSmsAppHelperDelegate* expected_android_sms_app_helper_delegate,
      FakeAndroidSmsPairingStateTracker*
          expected_android_sms_pairing_state_tracker,
      const device_sync::FakeGcmDeviceInfoProvider*
          expected_gcm_device_info_provider,
      bool expected_is_secondary_user)
      : expected_testing_pref_service_(expected_testing_pref_service),
        expected_device_sync_client_(expected_device_sync_client),
        expected_auth_token_validator_(expected_auth_token_validator),
        expected_oobe_completion_tracker_(expected_oobe_completion_tracker),
        expected_android_sms_app_helper_delegate_(
            expected_android_sms_app_helper_delegate),
        expected_android_sms_pairing_state_tracker_(
            expected_android_sms_pairing_state_tracker),
        expected_gcm_device_info_provider_(expected_gcm_device_info_provider),
        expected_is_secondary_user_(expected_is_secondary_user) {}

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

  ~FakeMultiDeviceSetupFactory() override = default;

  FakeMultiDeviceSetup* instance() { return instance_; }

 private:
  std::unique_ptr<MultiDeviceSetupBase> CreateInstance(
      PrefService* pref_service,
      device_sync::DeviceSyncClient* device_sync_client,
      AuthTokenValidator* auth_token_validator,
      OobeCompletionTracker* oobe_completion_tracker,
      AndroidSmsAppHelperDelegate* android_sms_app_helper_delegate,
      AndroidSmsPairingStateTracker* android_sms_pairing_state_tracker,
      const device_sync::GcmDeviceInfoProvider* gcm_device_info_provider,
      bool is_secondary_user) override {
    EXPECT_FALSE(instance_);
    EXPECT_EQ(expected_testing_pref_service_, pref_service);
    EXPECT_EQ(expected_device_sync_client_, device_sync_client);
    EXPECT_EQ(expected_auth_token_validator_, auth_token_validator);
    EXPECT_EQ(expected_oobe_completion_tracker_, oobe_completion_tracker);
    EXPECT_EQ(expected_android_sms_app_helper_delegate_,
              android_sms_app_helper_delegate);
    EXPECT_EQ(expected_android_sms_pairing_state_tracker_,
              android_sms_pairing_state_tracker);
    EXPECT_EQ(expected_gcm_device_info_provider_, gcm_device_info_provider);
    EXPECT_EQ(expected_is_secondary_user_, is_secondary_user);

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

  raw_ptr<sync_preferences::TestingPrefServiceSyncable>
      expected_testing_pref_service_;
  raw_ptr<device_sync::FakeDeviceSyncClient> expected_device_sync_client_;
  raw_ptr<FakeAuthTokenValidator> expected_auth_token_validator_;
  raw_ptr<OobeCompletionTracker> expected_oobe_completion_tracker_;
  raw_ptr<FakeAndroidSmsAppHelperDelegate>
      expected_android_sms_app_helper_delegate_;
  raw_ptr<FakeAndroidSmsPairingStateTracker>
      expected_android_sms_pairing_state_tracker_;
  raw_ptr<const device_sync::FakeGcmDeviceInfoProvider>
      expected_gcm_device_info_provider_;
  bool expected_is_secondary_user_;

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

}  // namespace

class MultiDeviceSetupServiceTest : public testing::Test {
 public:
  MultiDeviceSetupServiceTest(const MultiDeviceSetupServiceTest&) = delete;
  MultiDeviceSetupServiceTest& operator=(const MultiDeviceSetupServiceTest&) =
      delete;

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

  // testing::Test:
  void SetUp() override {
    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_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_multidevice_setup_factory_ =
        std::make_unique<FakeMultiDeviceSetupFactory>(
            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_);
    MultiDeviceSetupImpl::Factory::SetFactoryForTesting(
        fake_multidevice_setup_factory_.get());

    service_ = std::make_unique<MultiDeviceSetupService>(
        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_);

    service_->BindMultiDeviceSetup(
        multidevice_setup_remote_.BindNewPipeAndPassReceiver());
    service_->BindPrivilegedHostDeviceSetter(
        privileged_host_device_setter_remote_.BindNewPipeAndPassReceiver());
  }

  void TearDown() override {
    MultiDeviceSetupImpl::Factory::SetFactoryForTesting(nullptr);
  }

  void CallTriggerEventForDebuggingBeforeInitializationComplete(
      mojom::EventTypeForDebugging type) {
    EXPECT_FALSE(last_debug_event_success_);

    base::RunLoop run_loop;
    multidevice_setup_remote_->TriggerEventForDebugging(
        type,
        base::BindOnce(&MultiDeviceSetupServiceTest::OnDebugEventTriggered,
                       base::Unretained(this), run_loop.QuitClosure()));
    run_loop.Run();

    // Always expected to fail before initialization completes.
    EXPECT_FALSE(*last_debug_event_success_);
    last_debug_event_success_.reset();
  }

  void FinishInitialization() {
    EXPECT_FALSE(fake_multidevice_setup());
    fake_device_sync_client_->set_local_device_metadata(test_devices_[0]);
    fake_device_sync_client_->NotifyReady();
    EXPECT_TRUE(fake_multidevice_setup());
  }

  FakeMultiDeviceSetup* fake_multidevice_setup() {
    return fake_multidevice_setup_factory_->instance();
  }

  mojo::Remote<mojom::MultiDeviceSetup>& multidevice_setup_remote() {
    return multidevice_setup_remote_;
  }

  mojo::Remote<mojom::PrivilegedHostDeviceSetter>&
  privileged_host_device_setter_remote() {
    return privileged_host_device_setter_remote_;
  }

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

  base::test::TaskEnvironment task_environment_;
  const 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<FakeAndroidSmsAppHelperDelegate>
      fake_android_sms_app_helper_delegate_;
  std::unique_ptr<FakeAndroidSmsPairingStateTracker>
      fake_android_sms_pairing_state_tracker_;
  std::unique_ptr<device_sync::FakeGcmDeviceInfoProvider>
      fake_gcm_device_info_provider_;
  bool is_secondary_user_ = false;

  std::unique_ptr<FakeMultiDeviceSetupFactory> fake_multidevice_setup_factory_;

  std::unique_ptr<MultiDeviceSetupService> service_;
  std::optional<bool> last_debug_event_success_;

  mojo::Remote<mojom::MultiDeviceSetup> multidevice_setup_remote_;
  mojo::Remote<mojom::PrivilegedHostDeviceSetter>
      privileged_host_device_setter_remote_;
};

TEST_F(MultiDeviceSetupServiceTest,
       TriggerEventForDebuggingBeforeInitialization) {
  CallTriggerEventForDebuggingBeforeInitializationComplete(
      mojom::EventTypeForDebugging::kNewUserPotentialHostExists);
  CallTriggerEventForDebuggingBeforeInitializationComplete(
      mojom::EventTypeForDebugging::kExistingUserConnectedHostSwitched);
  CallTriggerEventForDebuggingBeforeInitializationComplete(
      mojom::EventTypeForDebugging::kExistingUserNewChromebookAdded);
}

TEST_F(MultiDeviceSetupServiceTest, CallFunctionsBeforeInitialization) {
  // SetAccountStatusChangeDelegate().
  auto fake_account_status_change_delegate =
      std::make_unique<FakeAccountStatusChangeDelegate>();
  multidevice_setup_remote()->SetAccountStatusChangeDelegate(
      fake_account_status_change_delegate->GenerateRemote());
  multidevice_setup_remote().FlushForTesting();

  // AddHostStatusObserver().
  auto fake_host_status_observer = std::make_unique<FakeHostStatusObserver>();
  multidevice_setup_remote()->AddHostStatusObserver(
      fake_host_status_observer->GenerateRemote());
  multidevice_setup_remote().FlushForTesting();

  // AddFeatureStateObserver().
  auto fake_feature_state_observer =
      std::make_unique<FakeFeatureStateObserver>();
  multidevice_setup_remote()->AddFeatureStateObserver(
      fake_feature_state_observer->GenerateRemote());
  multidevice_setup_remote().FlushForTesting();

  // GetEligibleHostDevices().
  multidevice_setup_remote()->GetEligibleHostDevices(base::DoNothing());
  multidevice_setup_remote().FlushForTesting();

  // GetHostStatus().
  multidevice_setup_remote()->GetHostStatus(base::DoNothing());
  multidevice_setup_remote().FlushForTesting();

  // SetFeatureEnabledState().
  multidevice_setup_remote()->SetFeatureEnabledState(
      mojom::Feature::kBetterTogetherSuite, true /* enabled */, "authToken",
      base::DoNothing());
  multidevice_setup_remote().FlushForTesting();

  // GetFeatureStates().
  multidevice_setup_remote()->GetFeatureStates(base::DoNothing());
  multidevice_setup_remote().FlushForTesting();

  // RetrySetHostNow().
  multidevice_setup_remote()->RetrySetHostNow(base::DoNothing());
  multidevice_setup_remote().FlushForTesting();

  // SetQuickStartPhoneInstanceId().
  multidevice_setup_remote()->SetQuickStartPhoneInstanceID("");
  multidevice_setup_remote().FlushForTesting();

  // GetQuickStartPhoneInstanceId();
  multidevice_setup_remote()->GetQuickStartPhoneInstanceID(base::DoNothing());
  multidevice_setup_remote().FlushForTesting();

  // None of these requests should have been processed yet, since initialization
  // was not complete.
  EXPECT_FALSE(fake_multidevice_setup());

  // Finish initialization; all of the pending calls should have been forwarded.
  FinishInitialization();
  EXPECT_TRUE(fake_multidevice_setup()->delegate());
  EXPECT_TRUE(fake_multidevice_setup()->HasAtLeastOneHostStatusObserver());
  EXPECT_TRUE(fake_multidevice_setup()->HasAtLeastOneFeatureStateObserver());
  EXPECT_EQ(1u, fake_multidevice_setup()->get_eligible_hosts_args().size());
  EXPECT_EQ(1u, fake_multidevice_setup()->get_host_args().size());
  EXPECT_EQ(1u, fake_multidevice_setup()->set_feature_enabled_args().size());
  EXPECT_EQ(1u, fake_multidevice_setup()->get_feature_states_args().size());
  EXPECT_EQ(1u, fake_multidevice_setup()->retry_set_host_now_args().size());
  EXPECT_EQ(1u,
            fake_multidevice_setup()->set_qs_phone_instance_id_args().size());
  EXPECT_EQ(1u,
            fake_multidevice_setup()->get_qs_phone_instance_id_args().size());
}

TEST_F(MultiDeviceSetupServiceTest, SetThenRemoveBeforeInitialization) {
  multidevice_setup_remote()->SetHostDevice("id1", "authToken",
                                            base::DoNothing());
  multidevice_setup_remote().FlushForTesting();

  privileged_host_device_setter_remote()->SetHostDevice("id2",
                                                        base::DoNothing());
  privileged_host_device_setter_remote().FlushForTesting();

  multidevice_setup_remote()->RemoveHostDevice();
  multidevice_setup_remote().FlushForTesting();

  EXPECT_FALSE(fake_multidevice_setup());

  // Finish initialization; since the SetHostDevice() call was followed by a
  // RemoveHostDevice() call, only the RemoveHostDevice() call should have been
  // forwarded.
  FinishInitialization();
  EXPECT_TRUE(fake_multidevice_setup()->set_host_args().empty());
  EXPECT_TRUE(fake_multidevice_setup()->set_host_without_auth_args().empty());
  EXPECT_EQ(1u, fake_multidevice_setup()->num_remove_host_calls());
}

TEST_F(MultiDeviceSetupServiceTest, RemoveThenSetThenSetBeforeInitialization) {
  multidevice_setup_remote()->RemoveHostDevice();
  multidevice_setup_remote().FlushForTesting();

  privileged_host_device_setter_remote()->SetHostDevice("id1",
                                                        base::DoNothing());
  privileged_host_device_setter_remote().FlushForTesting();

  multidevice_setup_remote()->SetHostDevice("id2", "authToken2",
                                            base::DoNothing());
  multidevice_setup_remote().FlushForTesting();

  multidevice_setup_remote()->SetHostDevice("id3", "authToken3",
                                            base::DoNothing());
  multidevice_setup_remote().FlushForTesting();

  EXPECT_FALSE(fake_multidevice_setup());

  // Finish initialization; only the second SetHostDevice() call should have
  // been forwarded.
  FinishInitialization();
  EXPECT_EQ(0u, fake_multidevice_setup()->num_remove_host_calls());
  EXPECT_TRUE(fake_multidevice_setup()->set_host_without_auth_args().empty());
  EXPECT_EQ(1u, fake_multidevice_setup()->set_host_args().size());
  EXPECT_EQ("id3", std::get<0>(fake_multidevice_setup()->set_host_args()[0]));
  EXPECT_EQ("authToken3",
            std::get<1>(fake_multidevice_setup()->set_host_args()[0]));
}

TEST_F(MultiDeviceSetupServiceTest,
       RemoveThenSetThenSetBeforeInitialization_NoAuthToken) {
  multidevice_setup_remote()->RemoveHostDevice();
  multidevice_setup_remote().FlushForTesting();

  multidevice_setup_remote()->SetHostDevice("id1", "authToken1",
                                            base::DoNothing());
  multidevice_setup_remote().FlushForTesting();

  multidevice_setup_remote()->SetHostDevice("id2", "authToken2",
                                            base::DoNothing());
  multidevice_setup_remote().FlushForTesting();

  privileged_host_device_setter_remote()->SetHostDevice("id3",
                                                        base::DoNothing());
  privileged_host_device_setter_remote().FlushForTesting();

  EXPECT_FALSE(fake_multidevice_setup());

  // Finish initialization; only the second SetHostDevice() call should have
  // been forwarded.
  FinishInitialization();
  EXPECT_EQ(0u, fake_multidevice_setup()->num_remove_host_calls());
  EXPECT_TRUE(fake_multidevice_setup()->set_host_args().empty());
  EXPECT_EQ(1u, fake_multidevice_setup()->set_host_without_auth_args().size());
  EXPECT_EQ("id3",
            fake_multidevice_setup()->set_host_without_auth_args()[0].first);
}

TEST_F(MultiDeviceSetupServiceTest, FinishInitializationFirst) {
  // Finish initialization before calling anything; this should result in
  // the calls being forwarded immediately.
  FinishInitialization();

  // SetAccountStatusChangeDelegate().
  auto fake_account_status_change_delegate =
      std::make_unique<FakeAccountStatusChangeDelegate>();
  multidevice_setup_remote()->SetAccountStatusChangeDelegate(
      fake_account_status_change_delegate->GenerateRemote());
  multidevice_setup_remote().FlushForTesting();
  EXPECT_TRUE(fake_multidevice_setup()->delegate());

  // AddHostStatusObserver().
  auto fake_host_status_observer = std::make_unique<FakeHostStatusObserver>();
  multidevice_setup_remote()->AddHostStatusObserver(
      fake_host_status_observer->GenerateRemote());
  multidevice_setup_remote().FlushForTesting();
  EXPECT_TRUE(fake_multidevice_setup()->HasAtLeastOneHostStatusObserver());

  // AddFeatureStateObserver().
  auto fake_feature_state_observer =
      std::make_unique<FakeFeatureStateObserver>();
  multidevice_setup_remote()->AddFeatureStateObserver(
      fake_feature_state_observer->GenerateRemote());
  multidevice_setup_remote().FlushForTesting();
  EXPECT_TRUE(fake_multidevice_setup()->HasAtLeastOneFeatureStateObserver());

  // GetEligibleHostDevices().
  multidevice_setup_remote()->GetEligibleHostDevices(base::DoNothing());
  multidevice_setup_remote().FlushForTesting();
  EXPECT_EQ(1u, fake_multidevice_setup()->get_eligible_hosts_args().size());

  // SetHostDevice().
  multidevice_setup_remote()->SetHostDevice("id", "authToken",
                                            base::DoNothing());
  multidevice_setup_remote().FlushForTesting();
  EXPECT_EQ(1u, fake_multidevice_setup()->set_host_args().size());

  // RemoveHostDevice().
  multidevice_setup_remote()->RemoveHostDevice();
  multidevice_setup_remote().FlushForTesting();
  EXPECT_EQ(1u, fake_multidevice_setup()->num_remove_host_calls());

  // GetHostStatus().
  multidevice_setup_remote()->GetHostStatus(base::DoNothing());
  multidevice_setup_remote().FlushForTesting();
  EXPECT_EQ(1u, fake_multidevice_setup()->get_host_args().size());

  // SetFeatureEnabledState().
  multidevice_setup_remote()->SetFeatureEnabledState(
      mojom::Feature::kBetterTogetherSuite, true /* enabled */, "authToken",
      base::DoNothing());
  multidevice_setup_remote().FlushForTesting();
  EXPECT_EQ(1u, fake_multidevice_setup()->set_feature_enabled_args().size());

  // GetFeatureStates().
  multidevice_setup_remote()->GetFeatureStates(base::DoNothing());
  multidevice_setup_remote().FlushForTesting();
  EXPECT_EQ(1u, fake_multidevice_setup()->get_feature_states_args().size());

  // RetrySetHostNow().
  multidevice_setup_remote()->RetrySetHostNow(base::DoNothing());
  multidevice_setup_remote().FlushForTesting();
  EXPECT_EQ(1u, fake_multidevice_setup()->retry_set_host_now_args().size());

  // SetHostDevice(), without an auth token.
  privileged_host_device_setter_remote()->SetHostDevice("id",
                                                        base::DoNothing());
  privileged_host_device_setter_remote().FlushForTesting();
  EXPECT_EQ(1u, fake_multidevice_setup()->set_host_without_auth_args().size());

  // SetQuickStartPhoneInstanceID().
  multidevice_setup_remote()->SetQuickStartPhoneInstanceID("");
  multidevice_setup_remote().FlushForTesting();
  EXPECT_EQ(1u,
            fake_multidevice_setup()->set_qs_phone_instance_id_args().size());

  // GetQuickStartPhoneInstanceID().
  multidevice_setup_remote()->GetQuickStartPhoneInstanceID(base::DoNothing());
  multidevice_setup_remote().FlushForTesting();
  EXPECT_EQ(1u,
            fake_multidevice_setup()->get_qs_phone_instance_id_args().size());
}

}  // namespace multidevice_setup

}  // namespace ash