1 // Copyright 2020 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 "chrome/browser/federated_learning/floc_id_provider_impl.h"
6
7 #include "base/files/scoped_temp_dir.h"
8 #include "base/strings/strcat.h"
9 #include "base/test/bind.h"
10 #include "base/test/scoped_feature_list.h"
11 #include "build/build_config.h"
12 #include "chrome/browser/browser_process.h"
13 #include "chrome/browser/federated_learning/floc_remote_permission_service.h"
14 #include "chrome/common/chrome_features.h"
15 #include "chrome/test/base/testing_browser_process.h"
16 #include "chrome/test/base/testing_profile.h"
17 #include "components/content_settings/core/browser/content_settings_registry.h"
18 #include "components/content_settings/core/browser/cookie_settings.h"
19 #include "components/content_settings/core/common/pref_names.h"
20 #include "components/history/core/browser/history_database_params.h"
21 #include "components/history/core/browser/history_service.h"
22 #include "components/history/core/test/test_history_database.h"
23 #include "components/sync/driver/test_sync_service.h"
24 #include "components/sync_preferences/testing_pref_service_syncable.h"
25 #include "components/sync_user_events/fake_user_event_service.h"
26 #include "content/public/test/browser_task_environment.h"
27 #include "content/public/test/test_utils.h"
28 #include "testing/gtest/include/gtest/gtest.h"
29
30 namespace federated_learning {
31
32 namespace {
33
34 using ComputeFlocTrigger = FlocIdProviderImpl::ComputeFlocTrigger;
35 using ComputeFlocResult = FlocIdProviderImpl::ComputeFlocResult;
36 using ComputeFlocCompletedCallback =
37 FlocIdProviderImpl::ComputeFlocCompletedCallback;
38 using CanComputeFlocCallback = FlocIdProviderImpl::CanComputeFlocCallback;
39
40 class MockFlocSortingLshService : public FlocSortingLshClustersService {
41 public:
42 using FlocSortingLshClustersService::FlocSortingLshClustersService;
43
ConfigureSortingLsh(const std::unordered_map<uint64_t,base::Optional<uint64_t>> & sorting_lsh_map,base::Version version)44 void ConfigureSortingLsh(
45 const std::unordered_map<uint64_t, base::Optional<uint64_t>>&
46 sorting_lsh_map,
47 base::Version version) {
48 sorting_lsh_map_ = sorting_lsh_map;
49 version_ = version;
50 }
51
ApplySortingLsh(uint64_t sim_hash,ApplySortingLshCallback callback)52 void ApplySortingLsh(uint64_t sim_hash,
53 ApplySortingLshCallback callback) override {
54 if (sorting_lsh_map_.count(sim_hash)) {
55 std::move(callback).Run(sorting_lsh_map_.at(sim_hash), version_);
56 return;
57 }
58
59 std::move(callback).Run(base::nullopt, version_);
60 }
61
62 private:
63 std::unordered_map<uint64_t, base::Optional<uint64_t>> sorting_lsh_map_;
64 base::Version version_;
65 };
66
67 class FakeFlocRemotePermissionService : public FlocRemotePermissionService {
68 public:
69 using FlocRemotePermissionService::FlocRemotePermissionService;
70
QueryFlocPermission(QueryFlocPermissionCallback callback,const net::PartialNetworkTrafficAnnotationTag & partial_traffic_annotation)71 void QueryFlocPermission(QueryFlocPermissionCallback callback,
72 const net::PartialNetworkTrafficAnnotationTag&
73 partial_traffic_annotation) override {
74 std::move(callback).Run(swaa_nac_account_enabled_);
75 }
76
set_swaa_nac_account_enabled(bool enabled)77 void set_swaa_nac_account_enabled(bool enabled) {
78 swaa_nac_account_enabled_ = enabled;
79 }
80
81 private:
82 bool swaa_nac_account_enabled_ = true;
83 };
84
85 class FakeCookieSettings : public content_settings::CookieSettings {
86 public:
87 using content_settings::CookieSettings::CookieSettings;
88
GetCookieSettingInternal(const GURL & url,const GURL & first_party_url,bool is_third_party_request,content_settings::SettingSource * source,ContentSetting * cookie_setting) const89 void GetCookieSettingInternal(const GURL& url,
90 const GURL& first_party_url,
91 bool is_third_party_request,
92 content_settings::SettingSource* source,
93 ContentSetting* cookie_setting) const override {
94 *cookie_setting =
95 allow_cookies_internal_ ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK;
96 }
97
ShouldBlockThirdPartyCookies() const98 bool ShouldBlockThirdPartyCookies() const override {
99 return should_block_third_party_cookies_;
100 }
101
set_should_block_third_party_cookies(bool should_block_third_party_cookies)102 void set_should_block_third_party_cookies(
103 bool should_block_third_party_cookies) {
104 should_block_third_party_cookies_ = should_block_third_party_cookies;
105 }
106
set_allow_cookies_internal(bool allow_cookies_internal)107 void set_allow_cookies_internal(bool allow_cookies_internal) {
108 allow_cookies_internal_ = allow_cookies_internal;
109 }
110
111 private:
112 ~FakeCookieSettings() override = default;
113
114 bool should_block_third_party_cookies_ = false;
115 bool allow_cookies_internal_ = true;
116 };
117
118 class MockFlocIdProvider : public FlocIdProviderImpl {
119 public:
120 using FlocIdProviderImpl::FlocIdProviderImpl;
121
OnComputeFlocCompleted(ComputeFlocTrigger trigger,ComputeFlocResult result)122 void OnComputeFlocCompleted(ComputeFlocTrigger trigger,
123 ComputeFlocResult result) override {
124 if (should_pause_before_compute_floc_completed_) {
125 DCHECK(!paused_);
126 paused_ = true;
127 paused_trigger_ = trigger;
128 paused_result_ = result;
129 return;
130 }
131
132 ++compute_floc_completed_count_;
133 FlocIdProviderImpl::OnComputeFlocCompleted(trigger, result);
134 }
135
ContinueLastOnComputeFlocCompleted()136 void ContinueLastOnComputeFlocCompleted() {
137 DCHECK(paused_);
138 paused_ = false;
139 ++compute_floc_completed_count_;
140 FlocIdProviderImpl::OnComputeFlocCompleted(paused_trigger_, paused_result_);
141 }
142
LogFlocComputedEvent(ComputeFlocTrigger trigger,const ComputeFlocResult & result)143 void LogFlocComputedEvent(ComputeFlocTrigger trigger,
144 const ComputeFlocResult& result) override {
145 ++log_event_count_;
146 last_log_event_trigger_ = trigger;
147 last_log_event_result_ = result;
148 FlocIdProviderImpl::LogFlocComputedEvent(trigger, result);
149 }
150
compute_floc_completed_count() const151 size_t compute_floc_completed_count() const {
152 return compute_floc_completed_count_;
153 }
154
set_should_pause_before_compute_floc_completed(bool should_pause)155 void set_should_pause_before_compute_floc_completed(bool should_pause) {
156 should_pause_before_compute_floc_completed_ = should_pause;
157 }
158
paused_result() const159 ComputeFlocResult paused_result() const {
160 DCHECK(paused_);
161 return paused_result_;
162 }
163
paused_trigger() const164 ComputeFlocTrigger paused_trigger() const {
165 DCHECK(paused_);
166 return paused_trigger_;
167 }
168
log_event_count() const169 size_t log_event_count() const { return log_event_count_; }
170
last_log_event_trigger() const171 ComputeFlocTrigger last_log_event_trigger() const {
172 DCHECK_LT(0u, log_event_count_);
173 return last_log_event_trigger_;
174 }
175
last_log_event_result() const176 ComputeFlocResult last_log_event_result() const {
177 DCHECK_LT(0u, log_event_count_);
178 return last_log_event_result_;
179 }
180
181 private:
182 base::OnceCallback<void()> callback_before_compute_floc_completed_;
183
184 // Add the support to be able to pause on the OnComputeFlocCompleted
185 // execution and let it yield to other tasks posted to the same task runner.
186 bool should_pause_before_compute_floc_completed_ = false;
187 bool paused_ = false;
188 ComputeFlocTrigger paused_trigger_;
189 ComputeFlocResult paused_result_;
190
191 size_t compute_floc_completed_count_ = 0u;
192 size_t log_event_count_ = 0u;
193 ComputeFlocTrigger last_log_event_trigger_;
194 ComputeFlocResult last_log_event_result_;
195 };
196
197 } // namespace
198
199 class FlocIdProviderUnitTest : public testing::Test {
200 public:
FlocIdProviderUnitTest()201 FlocIdProviderUnitTest()
202 : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
203
204 ~FlocIdProviderUnitTest() override = default;
205
SetUp()206 void SetUp() override {
207 EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
208
209 content_settings::ContentSettingsRegistry::GetInstance()->ResetForTest();
210 content_settings::CookieSettings::RegisterProfilePrefs(prefs_.registry());
211 HostContentSettingsMap::RegisterProfilePrefs(prefs_.registry());
212 settings_map_ = new HostContentSettingsMap(
213 &prefs_, /*is_off_the_record=*/false, /*store_last_modified=*/false,
214 /*restore_session=*/false);
215
216 auto sorting_lsh_service = std::make_unique<MockFlocSortingLshService>();
217 sorting_lsh_service_ = sorting_lsh_service.get();
218 TestingBrowserProcess::GetGlobal()->SetFlocSortingLshClustersService(
219 std::move(sorting_lsh_service));
220
221 history_service_ = std::make_unique<history::HistoryService>();
222 history_service_->Init(
223 history::TestHistoryDatabaseParamsForPath(temp_dir_.GetPath()));
224
225 test_sync_service_ = std::make_unique<syncer::TestSyncService>();
226 test_sync_service_->SetTransportState(
227 syncer::SyncService::TransportState::DISABLED);
228
229 fake_user_event_service_ = std::make_unique<syncer::FakeUserEventService>();
230
231 fake_floc_remote_permission_service_ =
232 std::make_unique<FakeFlocRemotePermissionService>(
233 /*url_loader_factory=*/nullptr);
234
235 fake_cookie_settings_ = base::MakeRefCounted<FakeCookieSettings>(
236 settings_map_.get(), &prefs_, false, "chrome-extension");
237
238 floc_id_provider_ = std::make_unique<MockFlocIdProvider>(
239 test_sync_service_.get(), fake_cookie_settings_,
240 fake_floc_remote_permission_service_.get(), history_service_.get(),
241 fake_user_event_service_.get());
242
243 task_environment_.RunUntilIdle();
244 }
245
TearDown()246 void TearDown() override {
247 settings_map_->ShutdownOnUIThread();
248 history_service_->RemoveObserver(floc_id_provider_.get());
249 }
250
ApplySortingLshPostProcessing(ComputeFlocCompletedCallback callback,uint64_t sim_hash,base::Time history_begin_time,base::Time history_end_time)251 void ApplySortingLshPostProcessing(ComputeFlocCompletedCallback callback,
252 uint64_t sim_hash,
253 base::Time history_begin_time,
254 base::Time history_end_time) {
255 floc_id_provider_->ApplySortingLshPostProcessing(
256 std::move(callback), sim_hash, history_begin_time, history_end_time);
257 }
258
CheckCanComputeFloc(CanComputeFlocCallback callback)259 void CheckCanComputeFloc(CanComputeFlocCallback callback) {
260 floc_id_provider_->CheckCanComputeFloc(std::move(callback));
261 }
262
IsSwaaNacAccountEnabled(CanComputeFlocCallback callback)263 void IsSwaaNacAccountEnabled(CanComputeFlocCallback callback) {
264 floc_id_provider_->IsSwaaNacAccountEnabled(std::move(callback));
265 }
266
OnURLsDeleted(history::HistoryService * history_service,const history::DeletionInfo & deletion_info)267 void OnURLsDeleted(history::HistoryService* history_service,
268 const history::DeletionInfo& deletion_info) {
269 floc_id_provider_->OnURLsDeleted(history_service, deletion_info);
270 }
271
OnGetRecentlyVisitedURLsCompleted(ComputeFlocTrigger trigger,history::QueryResults results)272 void OnGetRecentlyVisitedURLsCompleted(ComputeFlocTrigger trigger,
273 history::QueryResults results) {
274 auto compute_floc_completed_callback =
275 base::BindOnce(&FlocIdProviderImpl::OnComputeFlocCompleted,
276 base::Unretained(floc_id_provider_.get()), trigger);
277
278 floc_id_provider_->OnGetRecentlyVisitedURLsCompleted(
279 std::move(compute_floc_completed_callback), std::move(results));
280 }
281
ExpireHistoryBeforeUninclusive(base::Time end_time)282 void ExpireHistoryBeforeUninclusive(base::Time end_time) {
283 base::CancelableTaskTracker tracker;
284 base::RunLoop run_loop;
285 history_service_->ExpireHistoryBetween(
286 /*restrict_urls=*/{}, /*begin_time=*/base::Time(), end_time,
287 /*user_initiated=*/true, run_loop.QuitClosure(), &tracker);
288 run_loop.Run();
289 }
290
floc_id() const291 FlocId floc_id() const { return floc_id_provider_->floc_id_; }
292
set_floc_id(const FlocId & floc_id) const293 void set_floc_id(const FlocId& floc_id) const {
294 floc_id_provider_->floc_id_ = floc_id;
295 }
296
floc_computation_in_progress() const297 bool floc_computation_in_progress() const {
298 return floc_id_provider_->floc_computation_in_progress_;
299 }
300
set_floc_computation_in_progress(bool floc_computation_in_progress)301 void set_floc_computation_in_progress(bool floc_computation_in_progress) {
302 floc_id_provider_->floc_computation_in_progress_ =
303 floc_computation_in_progress;
304 }
305
first_floc_computation_triggered() const306 bool first_floc_computation_triggered() const {
307 return floc_id_provider_->first_floc_computation_triggered_;
308 }
309
set_first_floc_computation_triggered(bool triggered)310 void set_first_floc_computation_triggered(bool triggered) {
311 floc_id_provider_->first_floc_computation_triggered_ = triggered;
312 }
313
set_floc_id(const FlocId & floc_id)314 void set_floc_id(const FlocId& floc_id) {
315 floc_id_provider_->floc_id_ = floc_id;
316 }
317
need_recompute()318 bool need_recompute() { return floc_id_provider_->need_recompute_; }
319
SetRemoteSwaaNacAccountEnabled(bool enabled)320 void SetRemoteSwaaNacAccountEnabled(bool enabled) {
321 fake_floc_remote_permission_service_->set_swaa_nac_account_enabled(enabled);
322 }
323
ForceScheduledUpdate()324 void ForceScheduledUpdate() {
325 floc_id_provider_->OnComputeFlocScheduledUpdate();
326 }
327
328 protected:
329 base::test::ScopedFeatureList feature_list_;
330
331 content::BrowserTaskEnvironment task_environment_;
332
333 sync_preferences::TestingPrefServiceSyncable prefs_;
334 scoped_refptr<HostContentSettingsMap> settings_map_;
335
336 std::unique_ptr<history::HistoryService> history_service_;
337 std::unique_ptr<syncer::TestSyncService> test_sync_service_;
338 std::unique_ptr<syncer::FakeUserEventService> fake_user_event_service_;
339 std::unique_ptr<FakeFlocRemotePermissionService>
340 fake_floc_remote_permission_service_;
341 scoped_refptr<FakeCookieSettings> fake_cookie_settings_;
342 std::unique_ptr<MockFlocIdProvider> floc_id_provider_;
343
344 MockFlocSortingLshService* sorting_lsh_service_;
345
346 base::ScopedTempDir temp_dir_;
347
348 DISALLOW_COPY_AND_ASSIGN(FlocIdProviderUnitTest);
349 };
350
TEST_F(FlocIdProviderUnitTest,QualifiedInitialHistory)351 TEST_F(FlocIdProviderUnitTest, QualifiedInitialHistory) {
352 // Add a history entry with a timestamp exactly 7 days back from now.
353 std::string domain = "foo.com";
354 const base::Time kTime = base::Time::Now() - base::TimeDelta::FromDays(7);
355
356 history::HistoryAddPageArgs add_page_args;
357 add_page_args.url = GURL(base::StrCat({"https://www.", domain}));
358 add_page_args.time = kTime;
359 add_page_args.publicly_routable = true;
360 history_service_->AddPage(add_page_args);
361
362 task_environment_.RunUntilIdle();
363
364 // Expect that the floc computation hasn't started, as the floc_id_provider
365 // hasn't been notified about state of the sync_service.
366 EXPECT_EQ(0u, floc_id_provider_->compute_floc_completed_count());
367 EXPECT_EQ(0u, floc_id_provider_->log_event_count());
368 EXPECT_FALSE(floc_id().IsValid());
369 EXPECT_FALSE(first_floc_computation_triggered());
370
371 // Trigger the 1st floc computation.
372 test_sync_service_->SetTransportState(
373 syncer::SyncService::TransportState::ACTIVE);
374 test_sync_service_->FireStateChanged();
375
376 task_environment_.RunUntilIdle();
377
378 // Expect that the 1st computation has completed.
379 EXPECT_EQ(1u, floc_id_provider_->compute_floc_completed_count());
380 EXPECT_EQ(1u, floc_id_provider_->log_event_count());
381 EXPECT_EQ(FlocId(FlocId::SimHashHistory({domain}), kTime, kTime, 0),
382 floc_id());
383 EXPECT_TRUE(first_floc_computation_triggered());
384
385 // Advance the clock by 1 day. Expect a computation, as there's no history in
386 // the last 7 days so the id has been reset to empty.
387 task_environment_.FastForwardBy(base::TimeDelta::FromDays(1));
388
389 EXPECT_EQ(2u, floc_id_provider_->compute_floc_completed_count());
390 EXPECT_EQ(2u, floc_id_provider_->log_event_count());
391 EXPECT_FALSE(floc_id().IsValid());
392 }
393
TEST_F(FlocIdProviderUnitTest,UnqualifiedInitialHistory)394 TEST_F(FlocIdProviderUnitTest, UnqualifiedInitialHistory) {
395 std::string domain = "foo.com";
396
397 // Add a history entry with a timestamp 8 days back from now.
398 history::HistoryAddPageArgs add_page_args;
399 add_page_args.url = GURL(base::StrCat({"https://www.", domain}));
400 add_page_args.time = base::Time::Now() - base::TimeDelta::FromDays(8);
401 add_page_args.publicly_routable = true;
402 history_service_->AddPage(add_page_args);
403
404 task_environment_.RunUntilIdle();
405
406 // Expect that the floc computation hasn't started, as the floc_id_provider
407 // hasn't been notified about state of the sync_service.
408 EXPECT_EQ(0u, floc_id_provider_->compute_floc_completed_count());
409 EXPECT_EQ(0u, floc_id_provider_->log_event_count());
410 EXPECT_FALSE(floc_id().IsValid());
411 EXPECT_FALSE(first_floc_computation_triggered());
412
413 // Trigger the 1st floc computation.
414 test_sync_service_->SetTransportState(
415 syncer::SyncService::TransportState::ACTIVE);
416 test_sync_service_->FireStateChanged();
417
418 task_environment_.RunUntilIdle();
419
420 // Expect that the 1st computation has completed.
421 EXPECT_EQ(1u, floc_id_provider_->compute_floc_completed_count());
422 EXPECT_EQ(1u, floc_id_provider_->log_event_count());
423 EXPECT_TRUE(first_floc_computation_triggered());
424
425 // Add a history entry with a timestamp 6 days back from now.
426 const base::Time kTime = base::Time::Now() - base::TimeDelta::FromDays(6);
427 add_page_args.time = kTime;
428 history_service_->AddPage(add_page_args);
429
430 // Advance the clock by 23 hours. Expect no more computation, as the id
431 // refresh interval is 24 hours.
432 task_environment_.FastForwardBy(base::TimeDelta::FromHours(23));
433
434 EXPECT_EQ(1u, floc_id_provider_->compute_floc_completed_count());
435 EXPECT_EQ(1u, floc_id_provider_->log_event_count());
436
437 // Advance the clock by 1 hour. Expect one more computation, as the refresh
438 // time is reached and there's a valid history entry in the last 7 days.
439 task_environment_.FastForwardBy(base::TimeDelta::FromHours(1));
440
441 EXPECT_EQ(2u, floc_id_provider_->compute_floc_completed_count());
442 EXPECT_EQ(2u, floc_id_provider_->log_event_count());
443
444 EXPECT_EQ(FlocId(FlocId::SimHashHistory({domain}), kTime, kTime, 0),
445 floc_id());
446 }
447
TEST_F(FlocIdProviderUnitTest,HistoryDeleteAndScheduledUpdate)448 TEST_F(FlocIdProviderUnitTest, HistoryDeleteAndScheduledUpdate) {
449 std::string domain1 = "foo.com";
450 std::string domain2 = "bar.com";
451
452 // Add a history entry with a timestamp exactly 7 days back from now.
453 history::HistoryAddPageArgs add_page_args;
454 const base::Time kTime1 = base::Time::Now() - base::TimeDelta::FromDays(7);
455 add_page_args.url = GURL(base::StrCat({"https://www.", domain1}));
456 add_page_args.time = kTime1;
457 add_page_args.publicly_routable = true;
458 history_service_->AddPage(add_page_args);
459
460 // Add a history entry with a timestamp exactly 6 days back from now.
461 add_page_args.url = GURL(base::StrCat({"https://www.", domain2}));
462 const base::Time kTime2 = base::Time::Now() - base::TimeDelta::FromDays(6);
463 add_page_args.time = kTime2;
464 history_service_->AddPage(add_page_args);
465
466 task_environment_.RunUntilIdle();
467
468 // Trigger the 1st floc computation.
469 test_sync_service_->SetTransportState(
470 syncer::SyncService::TransportState::ACTIVE);
471 test_sync_service_->FireStateChanged();
472
473 task_environment_.RunUntilIdle();
474
475 // Expect that the 1st computation has completed.
476 EXPECT_EQ(1u, floc_id_provider_->compute_floc_completed_count());
477 EXPECT_EQ(1u, floc_id_provider_->log_event_count());
478 EXPECT_EQ(
479 FlocId(FlocId::SimHashHistory({domain1, domain2}), kTime1, kTime2, 0),
480 floc_id());
481
482 // Advance the clock by 12 hours. Expect no more computation.
483 task_environment_.FastForwardBy(base::TimeDelta::FromHours(12));
484 EXPECT_EQ(1u, floc_id_provider_->compute_floc_completed_count());
485 EXPECT_EQ(1u, floc_id_provider_->log_event_count());
486
487 // Expire the oldest history entry.
488 ExpireHistoryBeforeUninclusive(kTime2);
489 task_environment_.RunUntilIdle();
490
491 // Expect that the floc has been invalidated. Expect no more floc computation,
492 // but one more logging.
493 EXPECT_EQ(1u, floc_id_provider_->compute_floc_completed_count());
494 EXPECT_EQ(2u, floc_id_provider_->log_event_count());
495 EXPECT_FALSE(floc_id().IsValid());
496
497 // Advance the clock by 12 hours. Expect one more computation, which implies
498 // the timer didn't get reset due to the history invalidation. Expect that
499 // the floc is derived from domain2.
500 task_environment_.FastForwardBy(base::TimeDelta::FromHours(12));
501 EXPECT_EQ(2u, floc_id_provider_->compute_floc_completed_count());
502 EXPECT_EQ(3u, floc_id_provider_->log_event_count());
503 EXPECT_EQ(FlocId(FlocId::SimHashHistory({domain2}), kTime2, kTime2, 0),
504 floc_id());
505 }
506
TEST_F(FlocIdProviderUnitTest,ScheduledUpdateSameFloc)507 TEST_F(FlocIdProviderUnitTest, ScheduledUpdateSameFloc) {
508 std::string domain = "foo.com";
509 const base::Time kTime = base::Time::Now() - base::TimeDelta::FromDays(2);
510
511 // Add a history entry with a timestamp 2 days back from now.
512 history::HistoryAddPageArgs add_page_args;
513 add_page_args.url = GURL(base::StrCat({"https://www.", domain}));
514 add_page_args.time = kTime;
515 add_page_args.publicly_routable = true;
516 history_service_->AddPage(add_page_args);
517
518 task_environment_.RunUntilIdle();
519
520 // Trigger the 1st floc computation.
521 test_sync_service_->SetTransportState(
522 syncer::SyncService::TransportState::ACTIVE);
523 test_sync_service_->FireStateChanged();
524
525 task_environment_.RunUntilIdle();
526
527 // Expect that the 1st computation has completed.
528 EXPECT_EQ(1u, floc_id_provider_->compute_floc_completed_count());
529 EXPECT_EQ(1u, floc_id_provider_->log_event_count());
530 EXPECT_EQ(FlocId(FlocId::SimHashHistory({domain}), kTime, kTime, 0),
531 floc_id());
532
533 // Advance the clock by 1 day. Expect one more computation, but the floc
534 // didn't change.
535 task_environment_.FastForwardBy(base::TimeDelta::FromDays(1));
536
537 EXPECT_EQ(2u, floc_id_provider_->compute_floc_completed_count());
538 EXPECT_EQ(2u, floc_id_provider_->log_event_count());
539 EXPECT_EQ(FlocId(FlocId::SimHashHistory({domain}), kTime, kTime, 0),
540 floc_id());
541 }
542
TEST_F(FlocIdProviderUnitTest,CheckCanComputeFloc_Success)543 TEST_F(FlocIdProviderUnitTest, CheckCanComputeFloc_Success) {
544 test_sync_service_->SetTransportState(
545 syncer::SyncService::TransportState::ACTIVE);
546
547 base::OnceCallback<void(bool)> cb = base::BindOnce(
548 [](bool can_compute_floc) { EXPECT_TRUE(can_compute_floc); });
549
550 CheckCanComputeFloc(std::move(cb));
551 task_environment_.RunUntilIdle();
552 }
553
TEST_F(FlocIdProviderUnitTest,CheckCanComputeFloc_Failure_SyncDisabled)554 TEST_F(FlocIdProviderUnitTest, CheckCanComputeFloc_Failure_SyncDisabled) {
555 base::OnceCallback<void(bool)> cb = base::BindOnce(
556 [](bool can_compute_floc) { EXPECT_FALSE(can_compute_floc); });
557
558 CheckCanComputeFloc(std::move(cb));
559 task_environment_.RunUntilIdle();
560 }
561
TEST_F(FlocIdProviderUnitTest,CheckCanComputeFloc_Failure_BlockThirdPartyCookies)562 TEST_F(FlocIdProviderUnitTest,
563 CheckCanComputeFloc_Failure_BlockThirdPartyCookies) {
564 test_sync_service_->SetTransportState(
565 syncer::SyncService::TransportState::ACTIVE);
566
567 fake_cookie_settings_->set_should_block_third_party_cookies(true);
568
569 base::OnceCallback<void(bool)> cb = base::BindOnce(
570 [](bool can_compute_floc) { EXPECT_FALSE(can_compute_floc); });
571
572 CheckCanComputeFloc(std::move(cb));
573 task_environment_.RunUntilIdle();
574 }
575
TEST_F(FlocIdProviderUnitTest,CheckCanComputeFloc_Failure_SwaaNacAccountDisabled)576 TEST_F(FlocIdProviderUnitTest,
577 CheckCanComputeFloc_Failure_SwaaNacAccountDisabled) {
578 test_sync_service_->SetTransportState(
579 syncer::SyncService::TransportState::ACTIVE);
580
581 SetRemoteSwaaNacAccountEnabled(false);
582
583 base::OnceCallback<void(bool)> cb = base::BindOnce(
584 [](bool can_compute_floc) { EXPECT_FALSE(can_compute_floc); });
585
586 CheckCanComputeFloc(std::move(cb));
587 task_environment_.RunUntilIdle();
588 }
589
TEST_F(FlocIdProviderUnitTest,EventLogging)590 TEST_F(FlocIdProviderUnitTest, EventLogging) {
591 const base::Time kTime1 = base::Time::FromTimeT(1);
592 const base::Time kTime2 = base::Time::FromTimeT(2);
593
594 // Event logging for browser start.
595 floc_id_provider_->LogFlocComputedEvent(
596 ComputeFlocTrigger::kBrowserStart,
597 ComputeFlocResult(12345ULL, FlocId(123ULL, kTime1, kTime1, 999)));
598
599 EXPECT_EQ(1u, fake_user_event_service_->GetRecordedUserEvents().size());
600 const sync_pb::UserEventSpecifics& specifics1 =
601 fake_user_event_service_->GetRecordedUserEvents()[0];
602 EXPECT_EQ(specifics1.event_time_usec(),
603 base::Time::Now().ToDeltaSinceWindowsEpoch().InMicroseconds());
604
605 EXPECT_EQ(sync_pb::UserEventSpecifics::kFlocIdComputedEvent,
606 specifics1.event_case());
607
608 const sync_pb::UserEventSpecifics_FlocIdComputed& event1 =
609 specifics1.floc_id_computed_event();
610 EXPECT_EQ(sync_pb::UserEventSpecifics::FlocIdComputed::NEW,
611 event1.event_trigger());
612 EXPECT_EQ(12345ULL, event1.floc_id());
613
614 task_environment_.FastForwardBy(base::TimeDelta::FromDays(3));
615
616 // Event logging for scheduled update.
617 floc_id_provider_->LogFlocComputedEvent(
618 ComputeFlocTrigger::kScheduledUpdate,
619 ComputeFlocResult(999ULL, FlocId(777ULL, kTime1, kTime2, 888)));
620
621 EXPECT_EQ(2u, fake_user_event_service_->GetRecordedUserEvents().size());
622 const sync_pb::UserEventSpecifics& specifics2 =
623 fake_user_event_service_->GetRecordedUserEvents()[1];
624 EXPECT_EQ(specifics2.event_time_usec(),
625 base::Time::Now().ToDeltaSinceWindowsEpoch().InMicroseconds());
626 EXPECT_EQ(sync_pb::UserEventSpecifics::kFlocIdComputedEvent,
627 specifics2.event_case());
628
629 const sync_pb::UserEventSpecifics_FlocIdComputed& event2 =
630 specifics2.floc_id_computed_event();
631 EXPECT_EQ(sync_pb::UserEventSpecifics::FlocIdComputed::REFRESHED,
632 event2.event_trigger());
633 EXPECT_EQ(999ULL, event2.floc_id());
634
635 // Event logging for when sim hash is not computed.
636 floc_id_provider_->LogFlocComputedEvent(ComputeFlocTrigger::kScheduledUpdate,
637 ComputeFlocResult());
638
639 EXPECT_EQ(3u, fake_user_event_service_->GetRecordedUserEvents().size());
640 const sync_pb::UserEventSpecifics& specifics3 =
641 fake_user_event_service_->GetRecordedUserEvents()[2];
642 EXPECT_EQ(specifics3.event_time_usec(),
643 base::Time::Now().ToDeltaSinceWindowsEpoch().InMicroseconds());
644 EXPECT_EQ(sync_pb::UserEventSpecifics::kFlocIdComputedEvent,
645 specifics3.event_case());
646
647 const sync_pb::UserEventSpecifics_FlocIdComputed& event3 =
648 specifics3.floc_id_computed_event();
649 EXPECT_EQ(sync_pb::UserEventSpecifics::FlocIdComputed::REFRESHED,
650 event3.event_trigger());
651 EXPECT_FALSE(event3.has_floc_id());
652
653 // Event logging for history-delete invalidation.
654 floc_id_provider_->LogFlocComputedEvent(ComputeFlocTrigger::kHistoryDelete,
655 ComputeFlocResult());
656
657 EXPECT_EQ(4u, fake_user_event_service_->GetRecordedUserEvents().size());
658 const sync_pb::UserEventSpecifics& specifics4 =
659 fake_user_event_service_->GetRecordedUserEvents()[3];
660 EXPECT_EQ(specifics4.event_time_usec(),
661 base::Time::Now().ToDeltaSinceWindowsEpoch().InMicroseconds());
662 EXPECT_EQ(sync_pb::UserEventSpecifics::kFlocIdComputedEvent,
663 specifics4.event_case());
664
665 const sync_pb::UserEventSpecifics_FlocIdComputed& event4 =
666 specifics4.floc_id_computed_event();
667 EXPECT_EQ(sync_pb::UserEventSpecifics::FlocIdComputed::HISTORY_DELETE,
668 event4.event_trigger());
669 EXPECT_FALSE(event4.has_floc_id());
670
671 // Event logging for blocked floc.
672 floc_id_provider_->LogFlocComputedEvent(ComputeFlocTrigger::kScheduledUpdate,
673 ComputeFlocResult(87654, FlocId()));
674
675 EXPECT_EQ(5u, fake_user_event_service_->GetRecordedUserEvents().size());
676 const sync_pb::UserEventSpecifics& specifics5 =
677 fake_user_event_service_->GetRecordedUserEvents()[4];
678 EXPECT_EQ(specifics5.event_time_usec(),
679 base::Time::Now().ToDeltaSinceWindowsEpoch().InMicroseconds());
680 EXPECT_EQ(sync_pb::UserEventSpecifics::kFlocIdComputedEvent,
681 specifics5.event_case());
682
683 const sync_pb::UserEventSpecifics_FlocIdComputed& event5 =
684 specifics5.floc_id_computed_event();
685 EXPECT_EQ(sync_pb::UserEventSpecifics::FlocIdComputed::REFRESHED,
686 event5.event_trigger());
687 EXPECT_EQ(87654ULL, event5.floc_id());
688 }
689
TEST_F(FlocIdProviderUnitTest,HistoryDelete_AllHistory)690 TEST_F(FlocIdProviderUnitTest, HistoryDelete_AllHistory) {
691 const base::Time kTime1 = base::Time::FromTimeT(1);
692 const base::Time kTime2 = base::Time::FromTimeT(2);
693
694 set_floc_id(FlocId(123, kTime1, kTime2, 0));
695 set_first_floc_computation_triggered(true);
696
697 OnURLsDeleted(history_service_.get(), history::DeletionInfo::ForAllHistory());
698
699 EXPECT_FALSE(floc_id().IsValid());
700 }
701
TEST_F(FlocIdProviderUnitTest,HistoryDelete_InvalidTimeRange)702 TEST_F(FlocIdProviderUnitTest, HistoryDelete_InvalidTimeRange) {
703 const base::Time kTime1 = base::Time::FromTimeT(1);
704 const base::Time kTime2 = base::Time::FromTimeT(2);
705
706 GURL url_a = GURL("https://a.test");
707
708 history::URLResult url_result(url_a, kTime1);
709 url_result.set_publicly_routable(true);
710
711 history::QueryResults query_results;
712 query_results.SetURLResults({url_result});
713
714 const FlocId expected_floc =
715 FlocId(FlocId::SimHashHistory({"a.test"}), kTime1, kTime2, 0);
716
717 set_floc_id(expected_floc);
718 set_first_floc_computation_triggered(true);
719
720 OnURLsDeleted(history_service_.get(),
721 history::DeletionInfo::ForUrls(
722 {history::URLResult(url_a, kTime1)}, /*favicon_urls=*/{}));
723
724 EXPECT_EQ(expected_floc, floc_id());
725 }
726
TEST_F(FlocIdProviderUnitTest,HistoryDelete_TimeRangeNoOverlap)727 TEST_F(FlocIdProviderUnitTest, HistoryDelete_TimeRangeNoOverlap) {
728 const base::Time kTime1 = base::Time::FromTimeT(1);
729 const base::Time kTime2 = base::Time::FromTimeT(2);
730 const base::Time kTime3 = base::Time::FromTimeT(3);
731 const base::Time kTime4 = base::Time::FromTimeT(4);
732
733 const FlocId expected_floc =
734 FlocId(FlocId::SimHashHistory({"a.test"}), kTime1, kTime2, 0);
735
736 set_floc_id(expected_floc);
737 set_first_floc_computation_triggered(true);
738
739 history::DeletionInfo deletion_info(
740 history::DeletionTimeRange(kTime3, kTime4),
741 /*is_from_expiration=*/false, /*deleted_rows=*/{}, /*favicon_urls=*/{},
742 /*restrict_urls=*/base::nullopt);
743 OnURLsDeleted(history_service_.get(), deletion_info);
744
745 EXPECT_EQ(expected_floc, floc_id());
746 }
747
TEST_F(FlocIdProviderUnitTest,HistoryDelete_TimeRangePartialOverlap)748 TEST_F(FlocIdProviderUnitTest, HistoryDelete_TimeRangePartialOverlap) {
749 const base::Time kTime1 = base::Time::FromTimeT(1);
750 const base::Time kTime2 = base::Time::FromTimeT(2);
751 const base::Time kTime3 = base::Time::FromTimeT(3);
752
753 const FlocId expected_floc =
754 FlocId(FlocId::SimHashHistory({"a.test"}), kTime1, kTime2, 0);
755
756 set_floc_id(expected_floc);
757 set_first_floc_computation_triggered(true);
758
759 history::DeletionInfo deletion_info(
760 history::DeletionTimeRange(kTime2, kTime3),
761 /*is_from_expiration=*/false, /*deleted_rows=*/{}, /*favicon_urls=*/{},
762 /*restrict_urls=*/base::nullopt);
763 OnURLsDeleted(history_service_.get(), deletion_info);
764
765 EXPECT_FALSE(floc_id().IsValid());
766 }
767
TEST_F(FlocIdProviderUnitTest,HistoryDelete_TimeRangeFullOverlap)768 TEST_F(FlocIdProviderUnitTest, HistoryDelete_TimeRangeFullOverlap) {
769 const base::Time kTime1 = base::Time::FromTimeT(1);
770 const base::Time kTime2 = base::Time::FromTimeT(2);
771
772 const FlocId expected_floc =
773 FlocId(FlocId::SimHashHistory({"a.test"}), kTime1, kTime2, 0);
774
775 set_floc_id(expected_floc);
776 set_first_floc_computation_triggered(true);
777
778 history::DeletionInfo deletion_info(
779 history::DeletionTimeRange(kTime1, kTime2),
780 /*is_from_expiration=*/false, /*deleted_rows=*/{}, /*favicon_urls=*/{},
781 /*restrict_urls=*/base::nullopt);
782 OnURLsDeleted(history_service_.get(), deletion_info);
783
784 EXPECT_FALSE(floc_id().IsValid());
785 }
786
TEST_F(FlocIdProviderUnitTest,HistoryEntriesWithPrivateIP)787 TEST_F(FlocIdProviderUnitTest, HistoryEntriesWithPrivateIP) {
788 history::QueryResults query_results;
789 query_results.SetURLResults(
790 {history::URLResult(GURL("https://a.test"),
791 base::Time::Now() - base::TimeDelta::FromDays(1))});
792
793 set_first_floc_computation_triggered(true);
794 set_floc_computation_in_progress(true);
795
796 OnGetRecentlyVisitedURLsCompleted(ComputeFlocTrigger::kBrowserStart,
797 std::move(query_results));
798
799 EXPECT_FALSE(floc_id().IsValid());
800 }
801
TEST_F(FlocIdProviderUnitTest,MultipleHistoryEntries)802 TEST_F(FlocIdProviderUnitTest, MultipleHistoryEntries) {
803 const base::Time kTime1 = base::Time::FromTimeT(1);
804 const base::Time kTime2 = base::Time::FromTimeT(2);
805 const base::Time kTime3 = base::Time::FromTimeT(3);
806
807 history::URLResult url_result_a(GURL("https://a.test"), kTime1);
808 url_result_a.set_publicly_routable(true);
809
810 history::URLResult url_result_b(GURL("https://b.test"), kTime2);
811 url_result_b.set_publicly_routable(true);
812
813 history::URLResult url_result_c(GURL("https://c.test"), kTime3);
814
815 std::vector<history::URLResult> url_results{url_result_a, url_result_b,
816 url_result_c};
817
818 history::QueryResults query_results;
819 query_results.SetURLResults(std::move(url_results));
820
821 set_first_floc_computation_triggered(true);
822 set_floc_computation_in_progress(true);
823
824 OnGetRecentlyVisitedURLsCompleted(ComputeFlocTrigger::kBrowserStart,
825 std::move(query_results));
826
827 EXPECT_EQ(
828 FlocId(FlocId::SimHashHistory({"a.test", "b.test"}), kTime1, kTime2, 0),
829 floc_id());
830 }
831
TEST_F(FlocIdProviderUnitTest,TurnSyncOffAndOn)832 TEST_F(FlocIdProviderUnitTest, TurnSyncOffAndOn) {
833 std::string domain = "foo.com";
834 const base::Time kTime = base::Time::Now() - base::TimeDelta::FromDays(1);
835
836 history::HistoryAddPageArgs add_page_args;
837 add_page_args.url = GURL(base::StrCat({"https://www.", domain}));
838 add_page_args.time = kTime;
839 add_page_args.publicly_routable = true;
840 history_service_->AddPage(add_page_args);
841
842 task_environment_.RunUntilIdle();
843
844 // Trigger the 1st floc computation.
845 test_sync_service_->SetTransportState(
846 syncer::SyncService::TransportState::ACTIVE);
847 test_sync_service_->FireStateChanged();
848
849 task_environment_.RunUntilIdle();
850
851 // Expect that the 1st computation has completed.
852 EXPECT_EQ(1u, floc_id_provider_->compute_floc_completed_count());
853 EXPECT_EQ(1u, floc_id_provider_->log_event_count());
854 EXPECT_EQ(FlocId(FlocId::SimHashHistory({domain}), kTime, kTime, 0),
855 floc_id());
856
857 // Turn off sync.
858 test_sync_service_->SetTransportState(
859 syncer::SyncService::TransportState::DISABLED);
860
861 // Advance the clock by 1 day. Expect one more computation, as the sync was
862 // turned off so the id has been reset to empty.
863 task_environment_.FastForwardBy(base::TimeDelta::FromDays(1));
864
865 EXPECT_EQ(2u, floc_id_provider_->compute_floc_completed_count());
866 EXPECT_EQ(2u, floc_id_provider_->log_event_count());
867 EXPECT_FALSE(floc_id().IsValid());
868
869 // Turn on sync.
870 test_sync_service_->SetTransportState(
871 syncer::SyncService::TransportState::ACTIVE);
872
873 // Advance the clock by 1 day. Expect one more floc computation.
874 task_environment_.FastForwardBy(base::TimeDelta::FromDays(1));
875
876 EXPECT_EQ(3u, floc_id_provider_->compute_floc_completed_count());
877 EXPECT_EQ(3u, floc_id_provider_->log_event_count());
878 EXPECT_EQ(FlocId(FlocId::SimHashHistory({domain}), kTime, kTime, 0),
879 floc_id());
880 }
881
TEST_F(FlocIdProviderUnitTest,GetInterestCohortForJsApiMethod)882 TEST_F(FlocIdProviderUnitTest, GetInterestCohortForJsApiMethod) {
883 test_sync_service_->SetTransportState(
884 syncer::SyncService::TransportState::ACTIVE);
885 const base::Time kTime = base::Time::Now() - base::TimeDelta::FromDays(1);
886 const FlocId expected_floc = FlocId(123, kTime, kTime, 999);
887
888 set_floc_id(expected_floc);
889
890 EXPECT_EQ(expected_floc.ToStringForJsApi(),
891 floc_id_provider_->GetInterestCohortForJsApi(
892 /*requesting_origin=*/{}, /*site_for_cookies=*/{}));
893 }
894
TEST_F(FlocIdProviderUnitTest,GetInterestCohortForJsApiMethod_SyncHistoryDisabled)895 TEST_F(FlocIdProviderUnitTest,
896 GetInterestCohortForJsApiMethod_SyncHistoryDisabled) {
897 const base::Time kTime = base::Time::Now() - base::TimeDelta::FromDays(1);
898
899 set_floc_id(FlocId(123, kTime, kTime, 888));
900
901 EXPECT_EQ(std::string(),
902 floc_id_provider_->GetInterestCohortForJsApi(
903 /*requesting_origin=*/{}, /*site_for_cookies=*/{}));
904 }
905
TEST_F(FlocIdProviderUnitTest,GetInterestCohortForJsApiMethod_ThirdPartyCookiesDisabled)906 TEST_F(FlocIdProviderUnitTest,
907 GetInterestCohortForJsApiMethod_ThirdPartyCookiesDisabled) {
908 test_sync_service_->SetTransportState(
909 syncer::SyncService::TransportState::ACTIVE);
910 fake_cookie_settings_->set_should_block_third_party_cookies(true);
911
912 const base::Time kTime = base::Time::Now() - base::TimeDelta::FromDays(1);
913
914 set_floc_id(FlocId(123, kTime, kTime, 999));
915
916 EXPECT_EQ(std::string(),
917 floc_id_provider_->GetInterestCohortForJsApi(
918 /*requesting_origin=*/{}, /*site_for_cookies=*/{}));
919 }
920
TEST_F(FlocIdProviderUnitTest,GetInterestCohortForJsApiMethod_CookiesContentSettingsDisallowed)921 TEST_F(FlocIdProviderUnitTest,
922 GetInterestCohortForJsApiMethod_CookiesContentSettingsDisallowed) {
923 test_sync_service_->SetTransportState(
924 syncer::SyncService::TransportState::ACTIVE);
925 fake_cookie_settings_->set_allow_cookies_internal(false);
926
927 const base::Time kTime = base::Time::Now() - base::TimeDelta::FromDays(1);
928
929 set_floc_id(FlocId(123, kTime, kTime, 999));
930
931 EXPECT_EQ(std::string(),
932 floc_id_provider_->GetInterestCohortForJsApi(
933 /*requesting_origin=*/{}, /*site_for_cookies=*/{}));
934 }
935
TEST_F(FlocIdProviderUnitTest,GetInterestCohortForJsApiMethod_FlocUnavailable)936 TEST_F(FlocIdProviderUnitTest,
937 GetInterestCohortForJsApiMethod_FlocUnavailable) {
938 test_sync_service_->SetTransportState(
939 syncer::SyncService::TransportState::ACTIVE);
940
941 EXPECT_EQ(std::string(),
942 floc_id_provider_->GetInterestCohortForJsApi(
943 /*requesting_origin=*/{}, /*site_for_cookies=*/{}));
944 }
945
TEST_F(FlocIdProviderUnitTest,HistoryDeleteDuringInProgressComputation)946 TEST_F(FlocIdProviderUnitTest, HistoryDeleteDuringInProgressComputation) {
947 base::test::ScopedFeatureList feature_list;
948 feature_list.InitWithFeatures({features::kFlocIdSortingLshBasedComputation},
949 {});
950
951 std::string domain1 = "foo.com";
952 std::string domain2 = "bar.com";
953 std::string domain3 = "baz.com";
954 const base::Time kTime1 = base::Time::Now() - base::TimeDelta::FromDays(7);
955 const base::Time kTime2 = base::Time::Now() - base::TimeDelta::FromDays(6);
956 const base::Time kTime3 = base::Time::Now() - base::TimeDelta::FromDays(5);
957
958 // Add a history entry with a timestamp exactly 7 days back from now.
959 history::HistoryAddPageArgs add_page_args;
960 add_page_args.url = GURL(base::StrCat({"https://www.", domain1}));
961 add_page_args.time = kTime1;
962 add_page_args.publicly_routable = true;
963 history_service_->AddPage(add_page_args);
964
965 // Add a history entry with a timestamp exactly 6 days back from now.
966 add_page_args.url = GURL(base::StrCat({"https://www.", domain2}));
967 add_page_args.time = kTime2;
968 history_service_->AddPage(add_page_args);
969
970 // Add a history entry with a timestamp exactly 5 days back from now.
971 add_page_args.url = GURL(base::StrCat({"https://www.", domain3}));
972 add_page_args.time = kTime3;
973 history_service_->AddPage(add_page_args);
974
975 // Map SimHashHistory({domain1, domain2, domain3}) to 123.
976 // Map SimHashHistory({domain2, domain3}) to 456.
977 // Map SimHashHistory({domain3}) to 789.
978 sorting_lsh_service_->ConfigureSortingLsh(
979 {{FlocId::SimHashHistory({domain1, domain2, domain3}), 123},
980 {FlocId::SimHashHistory({domain2, domain3}), 456},
981 {FlocId::SimHashHistory({domain3}), 789}},
982 base::Version("999.0.0"));
983
984 // Trigger the 1st floc computation.
985 test_sync_service_->SetTransportState(
986 syncer::SyncService::TransportState::ACTIVE);
987 test_sync_service_->FireStateChanged();
988 sorting_lsh_service_->OnSortingLshClustersFileReady(base::FilePath(),
989 base::Version());
990
991 task_environment_.RunUntilIdle();
992
993 // Expect that the 1st computation has completed.
994 EXPECT_EQ(1u, floc_id_provider_->compute_floc_completed_count());
995 EXPECT_EQ(1u, floc_id_provider_->log_event_count());
996 EXPECT_TRUE(floc_id().IsValid());
997 EXPECT_EQ(FlocId(123, kTime1, kTime3, 999), floc_id());
998
999 // Advance the clock by 1 day. The "domain1" should expire. However, we pause
1000 // before the computation completes.
1001 floc_id_provider_->set_should_pause_before_compute_floc_completed(true);
1002 task_environment_.FastForwardBy(base::TimeDelta::FromDays(1));
1003
1004 EXPECT_TRUE(floc_computation_in_progress());
1005 EXPECT_FALSE(need_recompute());
1006 EXPECT_EQ(FlocId(123, kTime1, kTime3, 999), floc_id());
1007 EXPECT_EQ(FlocId(456, kTime2, kTime3, 999),
1008 floc_id_provider_->paused_result().floc_id);
1009 EXPECT_EQ(ComputeFlocTrigger::kScheduledUpdate,
1010 floc_id_provider_->paused_trigger());
1011
1012 // Expire the "domain2" history entry right before the floc computation
1013 // completes. Since the computation is still considered to be in-progress, we
1014 // will recompute right after this computation completes.
1015 ExpireHistoryBeforeUninclusive(kTime3);
1016
1017 EXPECT_TRUE(need_recompute());
1018
1019 floc_id_provider_->set_should_pause_before_compute_floc_completed(false);
1020 floc_id_provider_->ContinueLastOnComputeFlocCompleted();
1021 task_environment_.RunUntilIdle();
1022
1023 // Expect 2 more compute completion events and 1 more log event. This is
1024 // because we won't send log event if there's a recompute event scheduled.
1025 // The compute trigger should be the original trigger (i.e. kScheduledUpdate),
1026 // rather than kHistoryDelete.
1027 EXPECT_EQ(3u, floc_id_provider_->compute_floc_completed_count());
1028 EXPECT_EQ(2u, floc_id_provider_->log_event_count());
1029 EXPECT_EQ(ComputeFlocTrigger::kScheduledUpdate,
1030 floc_id_provider_->last_log_event_trigger());
1031 EXPECT_FALSE(need_recompute());
1032
1033 // The final floc should be derived from "domain3".
1034 EXPECT_TRUE(floc_id().IsValid());
1035 EXPECT_EQ(FlocId(789, kTime3, kTime3, 999), floc_id());
1036 }
1037
1038 class FlocIdProviderUnitTestSortingLshEnabled : public FlocIdProviderUnitTest {
1039 public:
FlocIdProviderUnitTestSortingLshEnabled()1040 FlocIdProviderUnitTestSortingLshEnabled() {
1041 feature_list_.InitAndEnableFeature(
1042 features::kFlocIdSortingLshBasedComputation);
1043 }
1044 };
1045
TEST_F(FlocIdProviderUnitTestSortingLshEnabled,SyncHistoryEnabledFollowedBySortingLshLoaded)1046 TEST_F(FlocIdProviderUnitTestSortingLshEnabled,
1047 SyncHistoryEnabledFollowedBySortingLshLoaded) {
1048 // Turn on sync & sync-history. The 1st floc computation should not be
1049 // triggered as the sorting-lsh file hasn't been loaded yet.
1050 test_sync_service_->SetTransportState(
1051 syncer::SyncService::TransportState::ACTIVE);
1052 test_sync_service_->FireStateChanged();
1053
1054 EXPECT_FALSE(first_floc_computation_triggered());
1055
1056 // Trigger the sorting-lsh ready event. The 1st floc computation should be
1057 // triggered now as sync & sync-history are enabled the sorting-lsh is ready.
1058 sorting_lsh_service_->OnSortingLshClustersFileReady(base::FilePath(),
1059 base::Version());
1060
1061 EXPECT_TRUE(first_floc_computation_triggered());
1062 }
1063
TEST_F(FlocIdProviderUnitTestSortingLshEnabled,SortingLshLoadedFollowedBySyncHistoryEnabled)1064 TEST_F(FlocIdProviderUnitTestSortingLshEnabled,
1065 SortingLshLoadedFollowedBySyncHistoryEnabled) {
1066 // Trigger the sorting-lsh ready event. The 1st floc computation should not be
1067 // triggered as sync & sync-history are not enabled yet.
1068 sorting_lsh_service_->OnSortingLshClustersFileReady(base::FilePath(),
1069 base::Version());
1070
1071 EXPECT_FALSE(first_floc_computation_triggered());
1072
1073 // Turn on sync & sync-history. The 1st floc computation should be triggered
1074 // now as sync & sync-history are enabled the sorting-lsh is loaded.
1075 test_sync_service_->SetTransportState(
1076 syncer::SyncService::TransportState::ACTIVE);
1077 test_sync_service_->FireStateChanged();
1078
1079 EXPECT_TRUE(first_floc_computation_triggered());
1080 }
1081
TEST_F(FlocIdProviderUnitTestSortingLshEnabled,ApplyAdditionalFiltering_SortingLsh)1082 TEST_F(FlocIdProviderUnitTestSortingLshEnabled,
1083 ApplyAdditionalFiltering_SortingLsh) {
1084 const base::Time kTime1 = base::Time::FromTimeT(1);
1085 const base::Time kTime2 = base::Time::FromTimeT(2);
1086
1087 bool callback_called = false;
1088 auto callback = base::BindLambdaForTesting([&](ComputeFlocResult result) {
1089 EXPECT_FALSE(callback_called);
1090 EXPECT_EQ(result.sim_hash, 3u);
1091 EXPECT_EQ(result.floc_id, FlocId(2, kTime1, kTime2, 99));
1092 callback_called = true;
1093 });
1094
1095 // Map 3 to 2
1096 sorting_lsh_service_->OnSortingLshClustersFileReady(base::FilePath(),
1097 base::Version());
1098 sorting_lsh_service_->ConfigureSortingLsh({{3, 2}}, base::Version("99.0"));
1099
1100 ApplySortingLshPostProcessing(std::move(callback), /*sim_hash=*/3, kTime1,
1101 kTime2);
1102 task_environment_.RunUntilIdle();
1103 EXPECT_TRUE(callback_called);
1104 }
1105
TEST_F(FlocIdProviderUnitTestSortingLshEnabled,ApplySortingLshPostProcessing_FileCorrupted)1106 TEST_F(FlocIdProviderUnitTestSortingLshEnabled,
1107 ApplySortingLshPostProcessing_FileCorrupted) {
1108 const base::Time kTime1 = base::Time::FromTimeT(1);
1109 const base::Time kTime2 = base::Time::FromTimeT(2);
1110
1111 bool callback_called = false;
1112 auto callback = base::BindLambdaForTesting([&](ComputeFlocResult result) {
1113 EXPECT_FALSE(callback_called);
1114 EXPECT_EQ(result.sim_hash, 3u);
1115 EXPECT_EQ(result.floc_id, FlocId());
1116 callback_called = true;
1117 });
1118
1119 sorting_lsh_service_->OnSortingLshClustersFileReady(base::FilePath(),
1120 base::Version());
1121 sorting_lsh_service_->ConfigureSortingLsh({}, base::Version("3.4.5"));
1122
1123 ApplySortingLshPostProcessing(std::move(callback), /*sim_hash=*/3, kTime1,
1124 kTime2);
1125 task_environment_.RunUntilIdle();
1126 EXPECT_TRUE(callback_called);
1127 }
1128
TEST_F(FlocIdProviderUnitTestSortingLshEnabled,SortingLshPostProcessing)1129 TEST_F(FlocIdProviderUnitTestSortingLshEnabled, SortingLshPostProcessing) {
1130 std::string domain = "foo.com";
1131 const base::Time kTime = base::Time::Now() - base::TimeDelta::FromDays(1);
1132
1133 history::HistoryAddPageArgs add_page_args;
1134 add_page_args.url = GURL(base::StrCat({"https://www.", domain}));
1135 add_page_args.time = kTime;
1136 add_page_args.publicly_routable = true;
1137 history_service_->AddPage(add_page_args);
1138
1139 task_environment_.RunUntilIdle();
1140
1141 uint64_t sim_hash = FlocId::SimHashHistory({domain});
1142
1143 // Configure the |sorting_lsh_service_| to map |sim_hash| to 12345.
1144 sorting_lsh_service_->ConfigureSortingLsh({{sim_hash, 12345}},
1145 base::Version("99.0"));
1146
1147 // Trigger the sorting-lsh ready event, and turn on sync & sync-history to
1148 // trigger the 1st floc computation.
1149 sorting_lsh_service_->OnSortingLshClustersFileReady(base::FilePath(),
1150 base::Version());
1151
1152 test_sync_service_->SetTransportState(
1153 syncer::SyncService::TransportState::ACTIVE);
1154 test_sync_service_->FireStateChanged();
1155
1156 EXPECT_TRUE(first_floc_computation_triggered());
1157
1158 task_environment_.RunUntilIdle();
1159
1160 // Expect a computation. The floc should be equal to 12345.
1161 EXPECT_EQ(1u, floc_id_provider_->compute_floc_completed_count());
1162 EXPECT_EQ(1u, floc_id_provider_->log_event_count());
1163 EXPECT_EQ(FlocId(12345, kTime, kTime, 99), floc_id());
1164
1165 // Configure the |sorting_lsh_service_| to block |sim_hash|.
1166 sorting_lsh_service_->ConfigureSortingLsh({{sim_hash, base::nullopt}},
1167 base::Version("3.4.5"));
1168
1169 task_environment_.FastForwardBy(base::TimeDelta::FromDays(1));
1170
1171 // Expect one more computation, where the result contains a valid sim_hash and
1172 // an invalid floc_id, as it was blocked. The internal floc is set to the
1173 // invalid one.
1174 EXPECT_EQ(2u, floc_id_provider_->compute_floc_completed_count());
1175 EXPECT_EQ(2u, floc_id_provider_->log_event_count());
1176 EXPECT_TRUE(floc_id_provider_->last_log_event_result().sim_hash_computed);
1177 EXPECT_EQ(floc_id_provider_->last_log_event_result().sim_hash, sim_hash);
1178 EXPECT_FALSE(floc_id_provider_->last_log_event_result().floc_id.IsValid());
1179 EXPECT_FALSE(floc_id().IsValid());
1180
1181 // In the event when the sim_hash is valid and floc_id is invalid, we'll
1182 // still log it.
1183 EXPECT_EQ(2u, fake_user_event_service_->GetRecordedUserEvents().size());
1184 const sync_pb::UserEventSpecifics& specifics =
1185 fake_user_event_service_->GetRecordedUserEvents()[1];
1186 EXPECT_EQ(specifics.event_time_usec(),
1187 base::Time::Now().ToDeltaSinceWindowsEpoch().InMicroseconds());
1188
1189 EXPECT_EQ(sync_pb::UserEventSpecifics::kFlocIdComputedEvent,
1190 specifics.event_case());
1191
1192 const sync_pb::UserEventSpecifics_FlocIdComputed& event =
1193 specifics.floc_id_computed_event();
1194 EXPECT_EQ(sync_pb::UserEventSpecifics::FlocIdComputed::REFRESHED,
1195 event.event_trigger());
1196 EXPECT_EQ(sim_hash, event.floc_id());
1197
1198 // Configure the |sorting_lsh_service_| to map |sim_hash| to 6789.
1199 sorting_lsh_service_->ConfigureSortingLsh({{sim_hash, 6789}},
1200 base::Version("999.0"));
1201
1202 task_environment_.FastForwardBy(base::TimeDelta::FromDays(1));
1203
1204 // Expect one more computation. The floc should be equal to 6789.
1205 EXPECT_EQ(3u, floc_id_provider_->compute_floc_completed_count());
1206 EXPECT_EQ(3u, floc_id_provider_->log_event_count());
1207 EXPECT_EQ(FlocId(6789, kTime, kTime, 999), floc_id());
1208 }
1209
1210 } // namespace federated_learning
1211