| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.h" |
| |
| #include <algorithm> |
| #include <tuple> |
| #include <utility> |
| |
| #include "base/memory/scoped_refptr.h" |
| #include "base/optional.h" |
| #include "base/run_loop.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/test/scoped_task_environment.h" |
| #include "chromeos/components/multidevice/remote_device_test_util.h" |
| #include "chromeos/services/multidevice_setup/multidevice_setup_initializer.h" |
| #include "chromeos/services/multidevice_setup/multidevice_setup_service.h" |
| #include "chromeos/services/multidevice_setup/public/cpp/android_sms_app_helper_delegate.h" |
| #include "chromeos/services/multidevice_setup/public/cpp/android_sms_pairing_state_tracker.h" |
| #include "chromeos/services/multidevice_setup/public/cpp/fake_multidevice_setup.h" |
| #include "chromeos/services/multidevice_setup/public/mojom/constants.mojom.h" |
| #include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h" |
| #include "services/service_manager/public/cpp/test/test_connector_factory.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace chromeos { |
| |
| namespace multidevice_setup { |
| |
| namespace { |
| |
| const size_t kNumTestDevices = 5u; |
| |
| class FakeMultiDeviceSetupInitializerFactory |
| : public MultiDeviceSetupInitializer::Factory { |
| public: |
| explicit FakeMultiDeviceSetupInitializerFactory( |
| std::unique_ptr<FakeMultiDeviceSetup> fake_multidevice_setup) |
| : fake_multidevice_setup_(std::move(fake_multidevice_setup)) {} |
| |
| ~FakeMultiDeviceSetupInitializerFactory() override = default; |
| |
| // MultiDeviceSetupInitializer::Factory: |
| std::unique_ptr<MultiDeviceSetupBase> BuildInstance( |
| PrefService* pref_service, |
| device_sync::DeviceSyncClient* device_sync_client, |
| AuthTokenValidator* auth_token_validator, |
| OobeCompletionTracker* oobe_completion_tracker, |
| std::unique_ptr<AndroidSmsAppHelperDelegate> |
| android_sms_app_helper_delegate, |
| std::unique_ptr<AndroidSmsPairingStateTracker> |
| android_sms_pairing_state_tracker, |
| const device_sync::GcmDeviceInfoProvider* gcm_device_info_provider) |
| override { |
| EXPECT_TRUE(fake_multidevice_setup_); |
| return std::move(fake_multidevice_setup_); |
| } |
| |
| private: |
| std::unique_ptr<FakeMultiDeviceSetup> fake_multidevice_setup_; |
| }; |
| |
| class TestMultiDeviceSetupClientObserver |
| : public MultiDeviceSetupClient::Observer { |
| public: |
| TestMultiDeviceSetupClientObserver() = default; |
| ~TestMultiDeviceSetupClientObserver() override = default; |
| |
| const std::vector<MultiDeviceSetupClient::HostStatusWithDevice>& |
| host_status_updates() const { |
| return host_status_updates_; |
| } |
| |
| const std::vector<MultiDeviceSetupClient::FeatureStatesMap>& |
| feature_state_updates() const { |
| return feature_state_updates_; |
| } |
| |
| private: |
| // MultiDeviceSetupClient::Observer: |
| void OnHostStatusChanged(const MultiDeviceSetupClient::HostStatusWithDevice& |
| host_device_with_status) override { |
| host_status_updates_.push_back(host_device_with_status); |
| } |
| |
| void OnFeatureStatesChanged(const MultiDeviceSetupClient::FeatureStatesMap& |
| feature_states_map) override { |
| feature_state_updates_.push_back(feature_states_map); |
| } |
| |
| std::vector<MultiDeviceSetupClient::HostStatusWithDevice> |
| host_status_updates_; |
| std::vector<MultiDeviceSetupClient::FeatureStatesMap> feature_state_updates_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TestMultiDeviceSetupClientObserver); |
| }; |
| |
| base::Optional<multidevice::RemoteDevice> GetRemoteDeviceFromRef( |
| const base::Optional<multidevice::RemoteDeviceRef>& remote_device_ref) { |
| if (!remote_device_ref) |
| return base::Optional<multidevice::RemoteDevice>(); |
| |
| return *multidevice::GetMutableRemoteDevice(*remote_device_ref); |
| } |
| |
| } // namespace |
| |
| class MultiDeviceSetupClientImplTest : public testing::Test { |
| protected: |
| MultiDeviceSetupClientImplTest() |
| : test_remote_device_list_( |
| multidevice::CreateRemoteDeviceListForTest(kNumTestDevices)), |
| test_remote_device_ref_list_( |
| multidevice::CreateRemoteDeviceRefListForTest(kNumTestDevices)) {} |
| |
| // testing::Test: |
| void SetUp() override { |
| auto fake_multidevice_setup = std::make_unique<FakeMultiDeviceSetup>(); |
| fake_multidevice_setup_ = fake_multidevice_setup.get(); |
| fake_multidevice_setup_impl_factory_ = |
| std::make_unique<FakeMultiDeviceSetupInitializerFactory>( |
| std::move(fake_multidevice_setup)); |
| MultiDeviceSetupInitializer::Factory::SetFactoryForTesting( |
| fake_multidevice_setup_impl_factory_.get()); |
| |
| service_ = std::make_unique<MultiDeviceSetupService>( |
| connector_factory_.RegisterInstance(mojom::kServiceName), |
| nullptr /* pref_service */, nullptr /* device_sync_client */, |
| nullptr /* auth_token_validator */, |
| nullptr /* oobe_completion_tracker */, |
| nullptr /* android_sms_app_helper_delegate */, |
| nullptr /* android_sms_pairing_state_tracker */, |
| nullptr /* gcm_device_info_provider */); |
| } |
| |
| void InitializeClient( |
| const MultiDeviceSetupClient::HostStatusWithDevice& |
| host_status_with_device = |
| MultiDeviceSetupClient::GenerateDefaultHostStatusWithDevice(), |
| const MultiDeviceSetupClient::FeatureStatesMap& feature_states_map = |
| MultiDeviceSetupClient::GenerateDefaultFeatureStatesMap()) { |
| client_ = MultiDeviceSetupClientImpl::Factory::Get()->BuildInstance( |
| connector_factory_.GetDefaultConnector()); |
| SendPendingMojoMessages(); |
| |
| // When |client_| is created, it requests the current host status and |
| // feature states. |
| std::move(fake_multidevice_setup_->get_host_args()[0]) |
| .Run(host_status_with_device.first, |
| GetRemoteDeviceFromRef(host_status_with_device.second)); |
| std::move(fake_multidevice_setup_->get_feature_states_args()[0]) |
| .Run(feature_states_map); |
| fake_multidevice_setup_->FlushForTesting(); |
| |
| test_observer_ = std::make_unique<TestMultiDeviceSetupClientObserver>(); |
| client_->AddObserver(test_observer_.get()); |
| } |
| |
| void TearDown() override { |
| MultiDeviceSetupInitializer::Factory::SetFactoryForTesting(nullptr); |
| client_->RemoveObserver(test_observer_.get()); |
| } |
| |
| void SimulateHostStatusChange( |
| const MultiDeviceSetupClient::HostStatusWithDevice& |
| host_status_with_device) { |
| size_t initial_results_size = test_observer_->host_status_updates().size(); |
| |
| fake_multidevice_setup_->NotifyHostStatusChanged( |
| host_status_with_device.first, |
| GetRemoteDeviceFromRef(host_status_with_device.second)); |
| fake_multidevice_setup_->FlushForTesting(); |
| |
| EXPECT_EQ(initial_results_size + 1, |
| test_observer_->host_status_updates().size()); |
| EXPECT_EQ( |
| host_status_with_device.first, |
| test_observer_->host_status_updates()[initial_results_size].first); |
| EXPECT_EQ( |
| host_status_with_device.second, |
| test_observer_->host_status_updates()[initial_results_size].second); |
| } |
| |
| void SimulateFeatureStatesChange( |
| const MultiDeviceSetupClient::FeatureStatesMap& feature_states_map) { |
| size_t initial_results_size = |
| test_observer_->feature_state_updates().size(); |
| |
| fake_multidevice_setup_->NotifyFeatureStateChanged(feature_states_map); |
| fake_multidevice_setup_->FlushForTesting(); |
| |
| EXPECT_EQ(initial_results_size + 1, |
| test_observer_->feature_state_updates().size()); |
| EXPECT_EQ(feature_states_map, |
| test_observer_->feature_state_updates()[initial_results_size]); |
| } |
| |
| void CallGetEligibleHostDevices( |
| const multidevice::RemoteDeviceList& expected_eligible_host_devices) { |
| base::RunLoop run_loop; |
| |
| client_->GetEligibleHostDevices(base::BindOnce( |
| &MultiDeviceSetupClientImplTest::OnGetEligibleHostDevicesCompleted, |
| base::Unretained(this), run_loop.QuitClosure())); |
| |
| SendPendingMojoMessages(); |
| |
| std::vector<mojom::MultiDeviceSetup::GetEligibleHostDevicesCallback>& |
| callbacks = fake_multidevice_setup_->get_eligible_hosts_args(); |
| EXPECT_EQ(1u, callbacks.size()); |
| std::move(callbacks[0]).Run(expected_eligible_host_devices); |
| |
| run_loop.Run(); |
| |
| VerifyRemoteDeviceRefListAndRemoteDeviceListAreEqual( |
| *eligible_host_devices_, expected_eligible_host_devices); |
| } |
| |
| void CallSetHostDevice(const std::string& public_key, |
| const std::string& auth_token, |
| bool expect_success) { |
| base::RunLoop run_loop; |
| |
| client_->SetHostDevice( |
| public_key, auth_token, |
| base::BindOnce( |
| &MultiDeviceSetupClientImplTest::OnSetHostDeviceCompleted, |
| base::Unretained(this), run_loop.QuitClosure())); |
| |
| SendPendingMojoMessages(); |
| |
| std::vector<std::tuple<std::string, std::string, |
| mojom::MultiDeviceSetup::SetHostDeviceCallback>>& |
| callbacks = fake_multidevice_setup_->set_host_args(); |
| EXPECT_EQ(1u, callbacks.size()); |
| EXPECT_EQ(public_key, std::get<0>(callbacks[0])); |
| EXPECT_EQ(auth_token, std::get<1>(callbacks[0])); |
| std::move(std::get<2>(callbacks[0])).Run(expect_success); |
| |
| run_loop.Run(); |
| |
| EXPECT_EQ(expect_success, set_host_device_success_); |
| } |
| |
| void CallRemoveHostDevice() { |
| EXPECT_EQ(0u, fake_multidevice_setup_->num_remove_host_calls()); |
| |
| client_->RemoveHostDevice(); |
| |
| SendPendingMojoMessages(); |
| |
| EXPECT_EQ(1u, fake_multidevice_setup_->num_remove_host_calls()); |
| } |
| |
| void CallSetFeatureEnabledState(mojom::Feature feature, |
| bool enabled, |
| const base::Optional<std::string>& auth_token, |
| bool should_succeed) { |
| size_t num_set_feature_enabled_args_before_call = |
| fake_multidevice_setup_->set_feature_enabled_args().size(); |
| |
| base::RunLoop run_loop; |
| client_->SetFeatureEnabledState( |
| feature, enabled, auth_token, |
| base::BindOnce( |
| &MultiDeviceSetupClientImplTest::OnSetFeatureEnabledStateCompleted, |
| base::Unretained(this), run_loop.QuitClosure())); |
| SendPendingMojoMessages(); |
| |
| EXPECT_EQ(num_set_feature_enabled_args_before_call + 1u, |
| fake_multidevice_setup_->set_feature_enabled_args().size()); |
| EXPECT_EQ(feature, |
| std::get<0>( |
| fake_multidevice_setup_->set_feature_enabled_args().back())); |
| EXPECT_EQ(enabled, |
| std::get<1>( |
| fake_multidevice_setup_->set_feature_enabled_args().back())); |
| EXPECT_EQ(auth_token, |
| std::get<2>( |
| fake_multidevice_setup_->set_feature_enabled_args().back())); |
| std::move( |
| std::get<3>(fake_multidevice_setup_->set_feature_enabled_args().back())) |
| .Run(should_succeed /* success */); |
| |
| run_loop.Run(); |
| EXPECT_EQ(should_succeed, *set_feature_enabled_state_success_); |
| } |
| |
| void CallRetrySetHostNow(bool expect_success) { |
| base::RunLoop run_loop; |
| |
| client_->RetrySetHostNow(base::BindOnce( |
| &MultiDeviceSetupClientImplTest::OnRetrySetHostNowCompleted, |
| base::Unretained(this), run_loop.QuitClosure())); |
| |
| SendPendingMojoMessages(); |
| |
| std::vector<mojom::MultiDeviceSetup::RetrySetHostNowCallback>& callbacks = |
| fake_multidevice_setup_->retry_set_host_now_args(); |
| EXPECT_EQ(1u, callbacks.size()); |
| std::move(callbacks[0]).Run(expect_success); |
| |
| run_loop.Run(); |
| |
| EXPECT_EQ(expect_success, retry_set_host_now_success_); |
| } |
| |
| void CallTriggerEventForDebugging(mojom::EventTypeForDebugging type, |
| bool expect_success) { |
| base::RunLoop run_loop; |
| |
| client_->TriggerEventForDebugging( |
| type, base::BindOnce(&MultiDeviceSetupClientImplTest:: |
| OnTriggerEventForDebuggingCompleted, |
| base::Unretained(this), run_loop.QuitClosure())); |
| |
| SendPendingMojoMessages(); |
| |
| std::vector< |
| std::pair<mojom::EventTypeForDebugging, |
| mojom::MultiDeviceSetup::TriggerEventForDebuggingCallback>>& |
| callbacks = fake_multidevice_setup_->triggered_debug_events(); |
| EXPECT_EQ(1u, callbacks.size()); |
| EXPECT_EQ(type, callbacks[0].first); |
| std::move(callbacks[0].second).Run(expect_success); |
| |
| run_loop.Run(); |
| |
| EXPECT_EQ(expect_success, trigger_event_for_debugging_success_); |
| } |
| |
| MultiDeviceSetupClient* client() { return client_.get(); } |
| |
| multidevice::RemoteDeviceList test_remote_device_list_; |
| const multidevice::RemoteDeviceRefList test_remote_device_ref_list_; |
| std::unique_ptr<TestMultiDeviceSetupClientObserver> test_observer_; |
| |
| private: |
| void SendPendingMojoMessages() { |
| static_cast<MultiDeviceSetupClientImpl*>(client_.get())->FlushForTesting(); |
| } |
| |
| // MultiDeviceSetupClientImpl cached its devices in a RemoteDeviceCache, which |
| // stores devices in an unordered_map -- retrieved devices thus need to be |
| // sorted before comparison. |
| void VerifyRemoteDeviceRefListAndRemoteDeviceListAreEqual( |
| multidevice::RemoteDeviceRefList remote_device_ref_list, |
| multidevice::RemoteDeviceList remote_device_list) { |
| std::sort(remote_device_list.begin(), remote_device_list.end()); |
| std::sort(remote_device_ref_list.begin(), remote_device_ref_list.end()); |
| |
| EXPECT_EQ(remote_device_list.size(), remote_device_ref_list.size()); |
| for (size_t i = 0; i < remote_device_list.size(); ++i) { |
| EXPECT_EQ(remote_device_list[i].public_key, |
| remote_device_ref_list[i].public_key()); |
| } |
| } |
| |
| void OnGetEligibleHostDevicesCompleted( |
| base::OnceClosure quit_closure, |
| const multidevice::RemoteDeviceRefList& eligible_host_devices) { |
| eligible_host_devices_ = eligible_host_devices; |
| std::move(quit_closure).Run(); |
| } |
| |
| void OnSetHostDeviceCompleted(base::OnceClosure quit_closure, bool success) { |
| set_host_device_success_ = success; |
| std::move(quit_closure).Run(); |
| } |
| |
| void OnGetHostStatusCompleted( |
| base::OnceClosure quit_closure, |
| mojom::HostStatus host_status, |
| const base::Optional<multidevice::RemoteDeviceRef>& host_device) { |
| get_host_status_result_ = std::make_pair(host_status, host_device); |
| std::move(quit_closure).Run(); |
| } |
| |
| void OnSetFeatureEnabledStateCompleted(base::OnceClosure quit_closure, |
| bool success) { |
| set_feature_enabled_state_success_ = success; |
| std::move(quit_closure).Run(); |
| } |
| |
| void OnGetFeatureStatesCompleted( |
| base::OnceClosure quit_closure, |
| const base::flat_map<mojom::Feature, mojom::FeatureState>& |
| feature_states_map) { |
| get_feature_states_result_ = feature_states_map; |
| std::move(quit_closure).Run(); |
| } |
| |
| void OnRetrySetHostNowCompleted(base::OnceClosure quit_closure, |
| bool success) { |
| retry_set_host_now_success_ = success; |
| std::move(quit_closure).Run(); |
| } |
| |
| void OnTriggerEventForDebuggingCompleted(base::OnceClosure quit_closure, |
| bool success) { |
| trigger_event_for_debugging_success_ = success; |
| std::move(quit_closure).Run(); |
| } |
| |
| const base::test::ScopedTaskEnvironment scoped_task_environment_; |
| |
| FakeMultiDeviceSetup* fake_multidevice_setup_; |
| std::unique_ptr<FakeMultiDeviceSetupInitializerFactory> |
| fake_multidevice_setup_impl_factory_; |
| service_manager::TestConnectorFactory connector_factory_; |
| std::unique_ptr<MultiDeviceSetupService> service_; |
| std::unique_ptr<MultiDeviceSetupClient> client_; |
| |
| base::Optional<multidevice::RemoteDeviceRefList> eligible_host_devices_; |
| base::Optional<bool> set_host_device_success_; |
| base::Optional<std::pair<mojom::HostStatus, |
| base::Optional<multidevice::RemoteDeviceRef>>> |
| get_host_status_result_; |
| base::Optional<bool> set_feature_enabled_state_success_; |
| base::Optional<base::flat_map<mojom::Feature, mojom::FeatureState>> |
| get_feature_states_result_; |
| base::Optional<bool> retry_set_host_now_success_; |
| base::Optional<bool> trigger_event_for_debugging_success_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MultiDeviceSetupClientImplTest); |
| }; |
| |
| TEST_F(MultiDeviceSetupClientImplTest, GetHostStatus) { |
| InitializeClient(); |
| EXPECT_TRUE(test_observer_->host_status_updates().empty()); |
| |
| MultiDeviceSetupClient::HostStatusWithDevice host_status_with_device = |
| std::make_pair(mojom::HostStatus::kNoEligibleHosts, |
| base::nullopt /* expected_host_device */); |
| SimulateHostStatusChange(host_status_with_device); |
| EXPECT_EQ(host_status_with_device, client()->GetHostStatus()); |
| |
| host_status_with_device = std::make_pair(mojom::HostStatus::kHostVerified, |
| test_remote_device_ref_list_[0]); |
| SimulateHostStatusChange(host_status_with_device); |
| EXPECT_EQ(host_status_with_device, client()->GetHostStatus()); |
| } |
| |
| TEST_F(MultiDeviceSetupClientImplTest, TestGetEligibleHostDevices) { |
| InitializeClient(); |
| CallGetEligibleHostDevices(test_remote_device_list_); |
| } |
| |
| TEST_F(MultiDeviceSetupClientImplTest, TestSetHostDevice_Success) { |
| InitializeClient(); |
| CallSetHostDevice(test_remote_device_list_[0].public_key, "authToken", |
| true /* expect_success */); |
| } |
| |
| TEST_F(MultiDeviceSetupClientImplTest, TestSetHostDevice_Failure) { |
| InitializeClient(); |
| CallSetHostDevice(test_remote_device_list_[0].public_key, "authToken", |
| false /* expect_success */); |
| } |
| |
| TEST_F(MultiDeviceSetupClientImplTest, TestRemoveHostDevice) { |
| InitializeClient(); |
| CallRemoveHostDevice(); |
| } |
| |
| TEST_F(MultiDeviceSetupClientImplTest, InitializeWithValues) { |
| MultiDeviceSetupClient::HostStatusWithDevice initial_host_status_with_device = |
| std::make_pair(mojom::HostStatus::kHostVerified, |
| test_remote_device_ref_list_[0]); |
| |
| MultiDeviceSetupClient::FeatureStatesMap initial_feature_states_map{ |
| {mojom::Feature::kBetterTogetherSuite, |
| mojom::FeatureState::kEnabledByUser}, |
| {mojom::Feature::kInstantTethering, mojom::FeatureState::kEnabledByUser}, |
| {mojom::Feature::kMessages, mojom::FeatureState::kEnabledByUser}, |
| {mojom::Feature::kSmartLock, mojom::FeatureState::kEnabledByUser}}; |
| |
| InitializeClient(initial_host_status_with_device, initial_feature_states_map); |
| EXPECT_EQ(initial_host_status_with_device, client()->GetHostStatus()); |
| EXPECT_EQ(initial_feature_states_map, client()->GetFeatureStates()); |
| } |
| |
| TEST_F(MultiDeviceSetupClientImplTest, SetFeatureEnabledState) { |
| InitializeClient(); |
| CallSetFeatureEnabledState(mojom::Feature::kBetterTogetherSuite, |
| true /* enabled */, "authToken1", |
| true /* should_succeed */); |
| CallSetFeatureEnabledState(mojom::Feature::kBetterTogetherSuite, |
| false /* enabled */, "authToken2", |
| false /* should_succeed */); |
| CallSetFeatureEnabledState(mojom::Feature::kBetterTogetherSuite, |
| false /* enabled */, "authToken3", |
| true /* should_succeed */); |
| } |
| |
| TEST_F(MultiDeviceSetupClientImplTest, GetFeatureState) { |
| InitializeClient(); |
| |
| MultiDeviceSetupClient::FeatureStatesMap feature_states_map{ |
| {mojom::Feature::kBetterTogetherSuite, |
| mojom::FeatureState::kEnabledByUser}, |
| {mojom::Feature::kInstantTethering, mojom::FeatureState::kEnabledByUser}, |
| {mojom::Feature::kMessages, mojom::FeatureState::kEnabledByUser}, |
| {mojom::Feature::kSmartLock, mojom::FeatureState::kEnabledByUser}}; |
| SimulateFeatureStatesChange(feature_states_map); |
| EXPECT_EQ(feature_states_map, client()->GetFeatureStates()); |
| } |
| |
| TEST_F(MultiDeviceSetupClientImplTest, TestRetrySetHostNow_Success) { |
| InitializeClient(); |
| CallRetrySetHostNow(true /* expect_success */); |
| } |
| |
| TEST_F(MultiDeviceSetupClientImplTest, TestRetrySetHostNow_Failure) { |
| InitializeClient(); |
| CallRetrySetHostNow(false /* expect_success */); |
| } |
| |
| TEST_F(MultiDeviceSetupClientImplTest, TestTriggerEventForDebugging_Success) { |
| InitializeClient(); |
| CallTriggerEventForDebugging( |
| mojom::EventTypeForDebugging::kNewUserPotentialHostExists, |
| true /* expect_success */); |
| } |
| |
| TEST_F(MultiDeviceSetupClientImplTest, TestTriggerEventForDebugging_Failure) { |
| InitializeClient(); |
| CallTriggerEventForDebugging( |
| mojom::EventTypeForDebugging::kNewUserPotentialHostExists, |
| false /* expect_success */); |
| } |
| |
| } // namespace multidevice_setup |
| |
| } // namespace chromeos |