1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chromeos/services/multidevice_setup/grandfathered_easy_unlock_host_disabler.h"
6
7 #include <memory>
8
9 #include "base/macros.h"
10 #include "base/timer/mock_timer.h"
11 #include "chromeos/components/multidevice/remote_device_test_util.h"
12 #include "chromeos/constants/chromeos_features.h"
13 #include "chromeos/services/device_sync/public/cpp/fake_device_sync_client.h"
14 #include "chromeos/services/multidevice_setup/fake_host_backend_delegate.h"
15 #include "components/sync_preferences/testing_pref_service_syncable.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17
18 namespace chromeos {
19
20 namespace multidevice_setup {
21
22 namespace {
23
24 // Parameterized test types, indicating the following test scenarios:
25 enum class TestType {
26 // Use v1 DeviceSync and host does not have an Instance ID.
27 kYesV1NoInstanceId,
28 // Use v1 DeviceSync and host has an Instance ID.
29 kYesV1YesInstanceId,
30 // Do not use v1 DeviceSync and host has an Instance ID.
31 kNoV1YesInstanceId
32 };
33
34 const char kEasyUnlockHostIdToDisablePrefName[] =
35 "multidevice_setup.easy_unlock_host_id_to_disable";
36 const char kEasyUnlockHostInstanceIdToDisablePrefName[] =
37 "multidevice_setup.easy_unlock_host_instance_id_to_disable";
38
39 const char kNoDevice[] = "";
40
41 const size_t kNumTestDevices = 2;
42
43 } // namespace
44
45 class MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest
46 : public ::testing::TestWithParam<TestType> {
47 protected:
MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest()48 MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest()
49 : test_devices_(
50 multidevice::CreateRemoteDeviceRefListForTest(kNumTestDevices)) {}
51 ~MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest() override = default;
52
53 // testing::Test:
SetUp()54 void SetUp() override {
55 SetDeviceSyncFeatureFlags();
56
57 for (auto& device : test_devices_) {
58 if (!HasInstanceId())
59 GetMutableRemoteDevice(device)->instance_id.clear();
60
61 // Don't rely on a legacy device ID if not using v1 DeviceSync, even
62 // though we almost always expect one in practice.
63 if (!features::ShouldUseV1DeviceSync())
64 GetMutableRemoteDevice(device)->public_key.clear();
65 }
66
67 fake_host_backend_delegate_ = std::make_unique<FakeHostBackendDelegate>();
68
69 fake_device_sync_client_ =
70 std::make_unique<device_sync::FakeDeviceSyncClient>();
71 fake_device_sync_client_->set_synced_devices(test_devices_);
72
73 test_pref_service_ =
74 std::make_unique<sync_preferences::TestingPrefServiceSyncable>();
75 GrandfatheredEasyUnlockHostDisabler::RegisterPrefs(
76 test_pref_service_->registry());
77 }
78
SetHost(const base::Optional<multidevice::RemoteDeviceRef> & host_device,multidevice::SoftwareFeature host_type)79 void SetHost(const base::Optional<multidevice::RemoteDeviceRef>& host_device,
80 multidevice::SoftwareFeature host_type) {
81 if (host_type != multidevice::SoftwareFeature::kBetterTogetherHost &&
82 host_type != multidevice::SoftwareFeature::kSmartLockHost)
83 return;
84
85 for (const auto& remote_device : test_devices_) {
86 bool should_be_host =
87 host_device != base::nullopt &&
88 host_device->GetDeviceId() == remote_device.GetDeviceId() &&
89 host_device->instance_id() == remote_device.instance_id();
90
91 GetMutableRemoteDevice(remote_device)->software_features[host_type] =
92 should_be_host ? multidevice::SoftwareFeatureState::kEnabled
93 : multidevice::SoftwareFeatureState::kSupported;
94 }
95
96 if (host_type == multidevice::SoftwareFeature::kBetterTogetherHost)
97 fake_host_backend_delegate_->NotifyHostChangedOnBackend(host_device);
98 }
99
InitializeTest(base::Optional<multidevice::RemoteDeviceRef> initial_device_in_prefs,base::Optional<multidevice::RemoteDeviceRef> initial_better_together_host,base::Optional<multidevice::RemoteDeviceRef> initial_easy_unlock_host)100 void InitializeTest(
101 base::Optional<multidevice::RemoteDeviceRef> initial_device_in_prefs,
102 base::Optional<multidevice::RemoteDeviceRef> initial_better_together_host,
103 base::Optional<multidevice::RemoteDeviceRef> initial_easy_unlock_host) {
104 test_pref_service_->SetString(kEasyUnlockHostIdToDisablePrefName,
105 initial_device_in_prefs
106 ? initial_device_in_prefs->GetDeviceId()
107 : kNoDevice);
108 test_pref_service_->SetString(kEasyUnlockHostInstanceIdToDisablePrefName,
109 initial_device_in_prefs
110 ? initial_device_in_prefs->instance_id()
111 : kNoDevice);
112
113 SetHost(initial_better_together_host,
114 multidevice::SoftwareFeature::kBetterTogetherHost);
115 SetHost(initial_easy_unlock_host,
116 multidevice::SoftwareFeature::kSmartLockHost);
117
118 auto mock_timer = std::make_unique<base::MockOneShotTimer>();
119 mock_timer_ = mock_timer.get();
120
121 grandfathered_easy_unlock_host_disabler_ =
122 GrandfatheredEasyUnlockHostDisabler::Factory::Create(
123 fake_host_backend_delegate_.get(), fake_device_sync_client_.get(),
124 test_pref_service_.get(), std::move(mock_timer));
125 }
126
127 // Verify that the IDs for |expected_device| are stored in prefs. If
128 // |expected_device| is null, prefs should have value |kNoDevice|.
VerifyDeviceInPrefs(const base::Optional<multidevice::RemoteDeviceRef> & expected_device)129 void VerifyDeviceInPrefs(
130 const base::Optional<multidevice::RemoteDeviceRef>& expected_device) {
131 if (!expected_device) {
132 EXPECT_EQ(kNoDevice, test_pref_service_->GetString(
133 kEasyUnlockHostIdToDisablePrefName));
134 EXPECT_EQ(kNoDevice, test_pref_service_->GetString(
135 kEasyUnlockHostInstanceIdToDisablePrefName));
136 return;
137 }
138
139 EXPECT_EQ(
140 expected_device->GetDeviceId().empty() ? kNoDevice
141 : expected_device->GetDeviceId(),
142 test_pref_service_->GetString(kEasyUnlockHostIdToDisablePrefName));
143 EXPECT_EQ(expected_device->instance_id().empty()
144 ? kNoDevice
145 : expected_device->instance_id(),
146 test_pref_service_->GetString(
147 kEasyUnlockHostInstanceIdToDisablePrefName));
148 }
149
VerifyEasyUnlockHostDisableRequest(int expected_queue_size,const base::Optional<multidevice::RemoteDeviceRef> & expected_host)150 void VerifyEasyUnlockHostDisableRequest(
151 int expected_queue_size,
152 const base::Optional<multidevice::RemoteDeviceRef>& expected_host) {
153 EXPECT_EQ(
154 expected_queue_size,
155 features::ShouldUseV1DeviceSync()
156 ? fake_device_sync_client_
157 ->GetSetSoftwareFeatureStateInputsQueueSize()
158 : fake_device_sync_client_->GetSetFeatureStatusInputsQueueSize());
159 if (expected_queue_size > 0) {
160 ASSERT_TRUE(expected_host);
161 VerifyLatestEasyUnlockHostDisableRequest(*expected_host);
162 }
163 }
164
InvokePendingEasyUnlockHostDisableRequestCallback(device_sync::mojom::NetworkRequestResult result_code)165 void InvokePendingEasyUnlockHostDisableRequestCallback(
166 device_sync::mojom::NetworkRequestResult result_code) {
167 if (features::ShouldUseV1DeviceSync()) {
168 fake_device_sync_client_->InvokePendingSetSoftwareFeatureStateCallback(
169 result_code);
170 } else {
171 fake_device_sync_client_->InvokePendingSetFeatureStatusCallback(
172 result_code);
173 }
174 }
175
test_devices() const176 const multidevice::RemoteDeviceRefList& test_devices() const {
177 return test_devices_;
178 }
179
fake_device_sync_client() const180 device_sync::FakeDeviceSyncClient* fake_device_sync_client() const {
181 return fake_device_sync_client_.get();
182 }
183
mock_timer() const184 base::MockOneShotTimer* mock_timer() const { return mock_timer_; }
185
186 private:
HasInstanceId()187 bool HasInstanceId() {
188 switch (GetParam()) {
189 case TestType::kYesV1YesInstanceId:
190 FALLTHROUGH;
191 case TestType::kNoV1YesInstanceId:
192 return true;
193 case TestType::kYesV1NoInstanceId:
194 return false;
195 }
196 }
197
SetDeviceSyncFeatureFlags()198 void SetDeviceSyncFeatureFlags() {
199 bool use_v1;
200 switch (GetParam()) {
201 case TestType::kYesV1YesInstanceId:
202 FALLTHROUGH;
203 case TestType::kYesV1NoInstanceId:
204 use_v1 = true;
205 break;
206 case TestType::kNoV1YesInstanceId:
207 use_v1 = false;
208 break;
209 }
210 }
211
VerifyLatestEasyUnlockHostDisableRequest(const multidevice::RemoteDeviceRef & expected_host)212 void VerifyLatestEasyUnlockHostDisableRequest(
213 const multidevice::RemoteDeviceRef& expected_host) {
214 // Verify inputs to SetSoftwareFeatureState().
215 if (features::ShouldUseV1DeviceSync()) {
216 ASSERT_FALSE(
217 fake_device_sync_client_->set_software_feature_state_inputs_queue()
218 .empty());
219 const device_sync::FakeDeviceSyncClient::SetSoftwareFeatureStateInputs&
220 inputs = fake_device_sync_client_
221 ->set_software_feature_state_inputs_queue()
222 .back();
223 EXPECT_EQ(expected_host.public_key(), inputs.public_key);
224 EXPECT_EQ(multidevice::SoftwareFeature::kSmartLockHost,
225 inputs.software_feature);
226 EXPECT_FALSE(inputs.enabled);
227 EXPECT_FALSE(inputs.is_exclusive);
228 return;
229 }
230
231 // Verify inputs to SetFeatureStatus().
232 ASSERT_FALSE(
233 fake_device_sync_client_->set_feature_status_inputs_queue().empty());
234 const device_sync::FakeDeviceSyncClient::SetFeatureStatusInputs& inputs =
235 fake_device_sync_client_->set_feature_status_inputs_queue().back();
236 EXPECT_EQ(expected_host.instance_id(), inputs.device_instance_id);
237 EXPECT_EQ(multidevice::SoftwareFeature::kSmartLockHost, inputs.feature);
238 EXPECT_EQ(device_sync::FeatureStatusChange::kDisable, inputs.status_change);
239 }
240
241 multidevice::RemoteDeviceRefList test_devices_;
242
243 std::unique_ptr<FakeHostBackendDelegate> fake_host_backend_delegate_;
244 std::unique_ptr<device_sync::FakeDeviceSyncClient> fake_device_sync_client_;
245 std::unique_ptr<sync_preferences::TestingPrefServiceSyncable>
246 test_pref_service_;
247 base::MockOneShotTimer* mock_timer_ = nullptr;
248
249 std::unique_ptr<GrandfatheredEasyUnlockHostDisabler>
250 grandfathered_easy_unlock_host_disabler_;
251
252 DISALLOW_COPY_AND_ASSIGN(
253 MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest);
254 };
255
256 // Situation #1:
257 // BTH = BETTER_TOGETHER_HOST, 0 = disabled, A = devices[0]
258 // EUH = EASY_UNLOCK_HOST, 1 = enabled, B = devices[1]
259 //
260 // | A | B | | A | B |
261 // ---+---+---+ ---+---+---+
262 // BTH| 1 | 0 | BTH| 0 | 0 |
263 // ---+---+---+ ---> ---+---+---+
264 // EUH| 1 | 0 | EUH| 1 | 0 |
265 //
266 // Grandfathering prevents EUH from being disabled automatically. This class
267 // disables EUH manually.
TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,IfBetterTogetherHostChangedFromOneDeviceToNoDeviceThenDisableEasyUnlock)268 TEST_P(
269 MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
270 IfBetterTogetherHostChangedFromOneDeviceToNoDeviceThenDisableEasyUnlock) {
271 InitializeTest(base::nullopt /* initial_device_in_prefs */,
272 test_devices()[0] /* initial_better_together_host */,
273 test_devices()[0] /* initial_easy_unlock_host */);
274
275 SetHost(base::nullopt, multidevice::SoftwareFeature::kBetterTogetherHost);
276
277 VerifyDeviceInPrefs(test_devices()[0]);
278 VerifyEasyUnlockHostDisableRequest(1 /* expected_queue_size */,
279 test_devices()[0]);
280 InvokePendingEasyUnlockHostDisableRequestCallback(
281 device_sync::mojom::NetworkRequestResult::kSuccess);
282
283 VerifyDeviceInPrefs(base::nullopt /* expected_device */);
284 EXPECT_FALSE(mock_timer()->IsRunning());
285 }
286
287 // Situation #2:
288 // BTH = BETTER_TOGETHER_HOST, 0 = disabled, A = devices[0]
289 // EUH = EASY_UNLOCK_HOST, 1 = enabled, B = devices[1]
290 //
291 // | A | B | | A | B |
292 // ---+---+---+ ---+---+---+
293 // BTH| 0 | 0 | BTH| 0 | 1 |
294 // ---+---+---+ ---> ---+---+---+
295 // EUH| 1 | 0 | EUH| 0 | 1 |
296 //
297 // The CryptAuth backend (via GmsCore) disables EUH on device A when BTH is
298 // enabled (exclusively) on another device, B. No action necessary from this
299 // class.
TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,IfBetterTogetherHostChangedFromNoDeviceToADeviceThenDoNothing)300 TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
301 IfBetterTogetherHostChangedFromNoDeviceToADeviceThenDoNothing) {
302 InitializeTest(base::nullopt /* initial_device_in_prefs */,
303 base::nullopt /* initial_better_together_host */,
304 test_devices()[0] /* initial_easy_unlock_host */);
305
306 SetHost(test_devices()[1], multidevice::SoftwareFeature::kBetterTogetherHost);
307
308 VerifyDeviceInPrefs(base::nullopt /* expected_device */);
309 VerifyEasyUnlockHostDisableRequest(0 /* expected_queue_size */,
310 base::nullopt /* expected_host */);
311 }
312
313 // Situation #3:
314 // BTH = BETTER_TOGETHER_HOST, 0 = disabled, A = devices[0]
315 // EUH = EASY_UNLOCK_HOST, 1 = enabled, B = devices[1]
316 //
317 // | A | B | | A | B |
318 // ---+---+---+ ---+---+---+
319 // BTH| 1 | 0 | BTH| 0 | 1 |
320 // ---+---+---+ ---> ---+---+---+
321 // EUH| 1 | 0 | EUH| 0 | 1 |
322 //
323 // The CryptAuth backend (via GmsCore) disables EUH on device A when BTH is
324 // enabled (exclusively) on another device, B. We still attempt to disable
325 // EUH in this case to be safe.
TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,IfBetterTogetherHostChangedFromOneDeviceToAnotherThenDisableEasyUnlock)326 TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
327 IfBetterTogetherHostChangedFromOneDeviceToAnotherThenDisableEasyUnlock) {
328 InitializeTest(base::nullopt /* initial_device_in_prefs */,
329 test_devices()[0] /* initial_better_together_host */,
330 test_devices()[0] /* initial_easy_unlock_host */);
331
332 SetHost(test_devices()[1], multidevice::SoftwareFeature::kBetterTogetherHost);
333
334 VerifyDeviceInPrefs(test_devices()[0]);
335 VerifyEasyUnlockHostDisableRequest(1 /* expected_queue_size */,
336 test_devices()[0]);
337 InvokePendingEasyUnlockHostDisableRequestCallback(
338 device_sync::mojom::NetworkRequestResult::kSuccess);
339
340 VerifyDeviceInPrefs(base::nullopt /* expected_device */);
341 EXPECT_FALSE(mock_timer()->IsRunning());
342 }
343
TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,IfDisablePendingThenConstructorAttemptsToDisableEasyUnlock)344 TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
345 IfDisablePendingThenConstructorAttemptsToDisableEasyUnlock) {
346 InitializeTest(test_devices()[0] /* initial_device_in_prefs */,
347 base::nullopt /* initial_better_together_host */,
348 test_devices()[0] /* initial_easy_unlock_host */);
349
350 VerifyDeviceInPrefs(test_devices()[0]);
351 VerifyEasyUnlockHostDisableRequest(1 /* expected_queue_size */,
352 test_devices()[0]);
353 InvokePendingEasyUnlockHostDisableRequestCallback(
354 device_sync::mojom::NetworkRequestResult::kSuccess);
355 }
356
357 // Situation #1 where device A is removed from list of synced devices:
358 //
359 // | A | B | | A | B |
360 // ---+---+---+ ---+---+---+
361 // BTH| 1 | 0 | BTH| 0 | 0 |
362 // ---+---+---+ ---> ---+---+---+
363 // EUH| 1 | 0 | EUH| 1 | 0 |
TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,IfHostToDisableIsNotInListOfSyncedDevicesThenClearPref)364 TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
365 IfHostToDisableIsNotInListOfSyncedDevicesThenClearPref) {
366 InitializeTest(base::nullopt /* initial_device_in_prefs */,
367 test_devices()[0] /* initial_better_together_host */,
368 test_devices()[0] /* initial_easy_unlock_host */);
369
370 // Remove device[0] from list
371 fake_device_sync_client()->set_synced_devices({test_devices()[1]});
372
373 SetHost(base::nullopt, multidevice::SoftwareFeature::kBetterTogetherHost);
374
375 VerifyDeviceInPrefs(base::nullopt /* expected_device */);
376 VerifyEasyUnlockHostDisableRequest(0 /* expected_queue_size */,
377 base::nullopt /* expected_host */);
378 }
379
380 // Situation #1 with failure:
381 //
382 // | A | B | | A | B |
383 // ---+---+---+ ---+---+---+
384 // BTH| 1 | 0 | BTH| 0 | 0 |
385 // ---+---+---+ ---> ---+---+---+
386 // EUH| 1 | 0 | EUH| 1 | 0 |
TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,IfEasyUnlockDisableUnsuccessfulThenScheduleRetry)387 TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
388 IfEasyUnlockDisableUnsuccessfulThenScheduleRetry) {
389 InitializeTest(base::nullopt /* initial_device_in_prefs */,
390 test_devices()[0] /* initial_better_together_host */,
391 test_devices()[0] /* initial_easy_unlock_host */);
392
393 SetHost(base::nullopt, multidevice::SoftwareFeature::kBetterTogetherHost);
394
395 VerifyEasyUnlockHostDisableRequest(1 /* expected_queue_size */,
396 test_devices()[0]);
397 InvokePendingEasyUnlockHostDisableRequestCallback(
398 device_sync::mojom::NetworkRequestResult::kInternalServerError);
399
400 VerifyEasyUnlockHostDisableRequest(0 /* expected_queue_size */,
401 base::nullopt /* expected_host */);
402
403 VerifyDeviceInPrefs(test_devices()[0]);
404 EXPECT_TRUE(mock_timer()->IsRunning());
405
406 mock_timer()->Fire();
407
408 VerifyEasyUnlockHostDisableRequest(1 /* expected_queue_size */,
409 test_devices()[0]);
410 }
411
TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,IfNoDisablePendingThenConstructorDoesNothing)412 TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
413 IfNoDisablePendingThenConstructorDoesNothing) {
414 InitializeTest(base::nullopt /* initial_device_in_prefs */,
415 base::nullopt /* initial_better_together_host */,
416 test_devices()[0] /* initial_easy_unlock_host */);
417
418 VerifyDeviceInPrefs(base::nullopt /* expected_device */);
419 VerifyEasyUnlockHostDisableRequest(0 /* expected_queue_size */,
420 base::nullopt /* expected_host */);
421 }
422
TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,IfDisablePendingButIsNotCurrentEasyUnlockHostThenClearPref)423 TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
424 IfDisablePendingButIsNotCurrentEasyUnlockHostThenClearPref) {
425 InitializeTest(test_devices()[0] /* initial_device_in_prefs */,
426 test_devices()[1] /* initial_better_together_host */,
427 test_devices()[1] /* initial_easy_unlock_host */);
428
429 VerifyDeviceInPrefs(base::nullopt /* expected_device */);
430 VerifyEasyUnlockHostDisableRequest(0 /* expected_queue_size */,
431 base::nullopt /* expected_host */);
432 }
433
TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,IfDisablePendingButIsCurrentBetterTogetherHostThenClearPref)434 TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
435 IfDisablePendingButIsCurrentBetterTogetherHostThenClearPref) {
436 InitializeTest(test_devices()[0] /* initial_device_in_prefs */,
437 test_devices()[0] /* initial_better_together_host */,
438 test_devices()[0] /* initial_easy_unlock_host */);
439
440 VerifyDeviceInPrefs(base::nullopt /* expected_device */);
441 VerifyEasyUnlockHostDisableRequest(0 /* expected_queue_size */,
442 base::nullopt /* expected_host */);
443 }
444
445 // Simulate:
446 // - Disable BETTER_TOGETHER_HOST on device 0
447 // - GrandfatheredEasyUnlockHostDisabler tries to disable EASY_UNLOCK_HOST on
448 // device 0 but fails
449 // - Timer is running while we wait to retry
450 // - Re-enable BETTER_TOGETHER_HOST on device 0
TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,IfHostChangesWhileRetryTimerIsRunningThenCancelTimerAndClearPref)451 TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
452 IfHostChangesWhileRetryTimerIsRunningThenCancelTimerAndClearPref) {
453 InitializeTest(base::nullopt /* initial_device_in_prefs */,
454 test_devices()[0] /* initial_better_together_host */,
455 test_devices()[0] /* initial_easy_unlock_host */);
456
457 SetHost(base::nullopt, multidevice::SoftwareFeature::kBetterTogetherHost);
458
459 VerifyEasyUnlockHostDisableRequest(1 /* expected_queue_size */,
460 test_devices()[0]);
461 InvokePendingEasyUnlockHostDisableRequestCallback(
462 device_sync::mojom::NetworkRequestResult::kInternalServerError);
463
464 EXPECT_TRUE(mock_timer()->IsRunning());
465
466 SetHost(test_devices()[0], multidevice::SoftwareFeature::kBetterTogetherHost);
467
468 VerifyEasyUnlockHostDisableRequest(0 /* expected_queue_size */,
469 base::nullopt /* expected_host */);
470
471 EXPECT_FALSE(mock_timer()->IsRunning());
472 VerifyDeviceInPrefs(base::nullopt /* expected_device */);
473 }
474
475 // Simulate:
476 // - Set device 0 as host
477 // - Disable host
478 // - Set device 1 as host
479 // - Disable host
480 // - SetSoftwareFeatureState callback for device 0 is called
TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,IfDifferentHostDisabledBeforeFirstCallbackThenFirstCallbackDoesNothing)481 TEST_P(MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
482 IfDifferentHostDisabledBeforeFirstCallbackThenFirstCallbackDoesNothing) {
483 InitializeTest(base::nullopt /* initial_device_in_prefs */,
484 test_devices()[0] /* initial_better_together_host */,
485 test_devices()[0] /* initial_easy_unlock_host */);
486 SetHost(base::nullopt, multidevice::SoftwareFeature::kBetterTogetherHost);
487 VerifyEasyUnlockHostDisableRequest(1 /* expected_queue_size */,
488 test_devices()[0]);
489
490 SetHost(test_devices()[1], multidevice::SoftwareFeature::kBetterTogetherHost);
491 SetHost(test_devices()[1], multidevice::SoftwareFeature::kSmartLockHost);
492 SetHost(base::nullopt, multidevice::SoftwareFeature::kBetterTogetherHost);
493 VerifyEasyUnlockHostDisableRequest(2 /* expected_queue_size */,
494 test_devices()[1]);
495 VerifyDeviceInPrefs(test_devices()[1]);
496
497 InvokePendingEasyUnlockHostDisableRequestCallback(
498 device_sync::mojom::NetworkRequestResult::kSuccess);
499 VerifyDeviceInPrefs(test_devices()[1]);
500 }
501 // Runs tests for the following scenarios.
502 // - Use v1 DeviceSync and host does not have an Instance ID.
503 // - Use v1 DeviceSync and host has an Instance ID.
504 // - Do not use v1 DeviceSync and host has an Instance ID.
505 // TODO(https://crbug.com/1019206): Remove when v1 DeviceSync is disabled,
506 // when all devices should have an Instance ID.
507 INSTANTIATE_TEST_SUITE_P(
508 All,
509 MultiDeviceSetupGrandfatheredEasyUnlockHostDisablerTest,
510 ::testing::Values(TestType::kYesV1NoInstanceId,
511 TestType::kYesV1YesInstanceId,
512 TestType::kNoV1YesInstanceId));
513
514 } // namespace multidevice_setup
515
516 } // namespace chromeos
517