Commit a927c079 authored by Yao Xiao's avatar Yao Xiao Committed by Commit Bot

Avoid recalculating floc on history-delete. Will either invalidate or no-op.

Why: we don't want floc to change more often than the intended
scheduled update cadence.

What:
- For each history-delete notification, if it's all-history or
the time-range overlaps with the time range of the history used to
compute the floc, we invalidate the floc. Otherwise, we keep using the
current floc.
- Remove the mechanism caching the swaa_nac_account_enabled status.
It's no longer needed as we don't need to query more often than the
scheduled update rate (% rare race condition).

How: Store history_begin_time_/history_end_time_ fields to FlocId.
Compare these fields with the history delete info.

Bug: 1143597
Change-Id: I685fd1a10cbc8044e799d7a644dfa2b7bb82ddd4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2507454
Commit-Queue: Yao Xiao <yaoxia@chromium.org>
Reviewed-by: default avatarJosh Karlin <jkarlin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#826680}
parent 1a5301a5
......@@ -214,6 +214,38 @@ class FlocIdProviderWithCustomizedServicesBrowserTest
return enumerator.urls();
}
std::pair<base::Time, base::Time> GetHistoryTimeRange() {
history::QueryOptions options;
options.duplicate_policy = history::QueryOptions::KEEP_ALL_DUPLICATES;
base::Time history_begin_time = base::Time::Max();
base::Time history_end_time = base::Time::Min();
base::RunLoop run_loop;
base::CancelableTaskTracker tracker;
HistoryServiceFactory::GetForProfile(browser()->profile(),
ServiceAccessType::EXPLICIT_ACCESS)
->QueryHistory(
base::string16(), options,
base::BindLambdaForTesting([&](history::QueryResults results) {
for (const history::URLResult& url_result : results) {
if (!url_result.publicly_routable())
continue;
if (url_result.visit_time() < history_begin_time)
history_begin_time = url_result.visit_time();
if (url_result.visit_time() > history_end_time)
history_end_time = url_result.visit_time();
}
run_loop.Quit();
}),
&tracker);
run_loop.Run();
return {history_begin_time, history_end_time};
}
void FinishOutstandingRemotePermissionQueries() {
base::RunLoop run_loop;
FlocRemotePermissionServiceFactory::GetForProfile(browser()->profile())
......@@ -241,7 +273,8 @@ class FlocIdProviderWithCustomizedServicesBrowserTest
const uint64_t dummy_sim_hash = 0u;
g_browser_process->floc_sorting_lsh_clusters_service()->ApplySortingLsh(
dummy_sim_hash,
base::BindLambdaForTesting([&](FlocId floc) { run_loop.Quit(); }));
base::BindLambdaForTesting(
[&](base::Optional<uint64_t>, base::Version) { run_loop.Quit(); }));
run_loop.Run();
}
......@@ -250,9 +283,10 @@ class FlocIdProviderWithCustomizedServicesBrowserTest
base::CancelableTaskTracker tracker;
HistoryServiceFactory::GetForProfile(browser()->profile(),
ServiceAccessType::EXPLICIT_ACCESS)
->ExpireHistoryBeforeForTesting(
end_time, base::BindLambdaForTesting([&]() { run_loop.Quit(); }),
&tracker);
->ExpireHistoryBetween(
/*restrict_urls=*/{}, /*begin_time=*/base::Time(), end_time,
/*user_initiated=*/true,
base::BindLambdaForTesting([&]() { run_loop.Quit(); }), &tracker);
run_loop.Run();
}
......@@ -528,8 +562,10 @@ IN_PROC_BROWSER_TEST_F(FlocIdProviderWithCustomizedServicesBrowserTest,
InitializeHistorySync();
// Promise resolved with the expected floc value.
EXPECT_EQ(FlocId(FlocId::SimHashHistory({test_host()}), 0).ToString(),
// Promise resolved with the expected string.
EXPECT_EQ(FlocId(FlocId::SimHashHistory({test_host()}), base::Time(),
base::Time(), 0)
.ToStringForJsApi(),
InvokeInterestCohortJsApi(web_contents()));
}
......@@ -555,8 +591,10 @@ IN_PROC_BROWSER_TEST_F(FlocIdProviderWithCustomizedServicesBrowserTest,
content::RenderFrameHost* child =
content::ChildFrameAt(web_contents()->GetMainFrame(), 0);
// Promise resolved with the expected floc value.
EXPECT_EQ(FlocId(FlocId::SimHashHistory({test_host()}), 0).ToString(),
// Promise resolved with the expected string.
EXPECT_EQ(FlocId(FlocId::SimHashHistory({test_host()}), base::Time(),
base::Time(), 0)
.ToStringForJsApi(),
InvokeInterestCohortJsApi(child));
}
......@@ -582,8 +620,10 @@ IN_PROC_BROWSER_TEST_F(FlocIdProviderWithCustomizedServicesBrowserTest,
content::RenderFrameHost* child =
content::ChildFrameAt(web_contents()->GetMainFrame(), 0);
// Promise resolved with the expected floc value.
EXPECT_EQ(FlocId(FlocId::SimHashHistory({test_host()}), 0).ToString(),
// Promise resolved with the expected string.
EXPECT_EQ(FlocId(FlocId::SimHashHistory({test_host()}), base::Time(),
base::Time(), 0)
.ToStringForJsApi(),
InvokeInterestCohortJsApi(child));
}
......@@ -618,8 +658,10 @@ IN_PROC_BROWSER_TEST_F(FlocIdProviderWithCustomizedServicesBrowserTest,
// Promise rejected as the cookies permission disallows the child's host.
EXPECT_EQ("rejected", InvokeInterestCohortJsApi(child));
// Promise resolved with the expected floc value.
EXPECT_EQ(FlocId(FlocId::SimHashHistory({test_host()}), 0).ToString(),
// Promise resolved with the expected string.
EXPECT_EQ(FlocId(FlocId::SimHashHistory({test_host()}), base::Time(),
base::Time(), 0)
.ToStringForJsApi(),
InvokeInterestCohortJsApi(web_contents()));
}
......@@ -646,6 +688,9 @@ IN_PROC_BROWSER_TEST_F(FlocIdProviderSortingLshEnabledBrowserTest,
browser(), https_server_.GetURL(test_host(), cookies_to_set));
EXPECT_EQ(1u, GetHistoryUrls().size());
auto p = GetHistoryTimeRange();
base::Time history_begin_time = p.first;
base::Time history_end_time = p.second;
EXPECT_FALSE(GetFlocId().IsValid());
......@@ -659,7 +704,7 @@ IN_PROC_BROWSER_TEST_F(FlocIdProviderSortingLshEnabledBrowserTest,
EXPECT_NE(0u, FlocId::SimHashHistory({test_host()}));
// Expect that the final id is 0 because the sorting-lsh was applied.
EXPECT_EQ(FlocId(0, 9), GetFlocId());
EXPECT_EQ(FlocId(0, history_begin_time, history_end_time, 9), GetFlocId());
}
IN_PROC_BROWSER_TEST_F(FlocIdProviderSortingLshEnabledBrowserTest,
......
......@@ -29,8 +29,6 @@ constexpr size_t kMinHistoryDomainSizeToReportFlocId = 1;
constexpr base::TimeDelta kFlocScheduledUpdateInterval =
base::TimeDelta::FromDays(1);
constexpr int kQueryHistoryWindowInDays = 7;
constexpr base::TimeDelta kSwaaNacAccountEnabledCachePeriod =
base::TimeDelta::FromHours(12);
// The placeholder sorting-lsh version when the sorting-lsh feature is disabled.
constexpr uint32_t kSortingLshVersionPlaceholder = 0;
......@@ -81,7 +79,7 @@ std::string FlocIdProviderImpl::GetInterestCohortForJsApi(
if (!floc_id_.IsValid())
return std::string();
return floc_id_.ToString();
return floc_id_.ToStringForJsApi();
}
void FlocIdProviderImpl::OnComputeFlocCompleted(ComputeFlocTrigger trigger,
......@@ -89,12 +87,11 @@ void FlocIdProviderImpl::OnComputeFlocCompleted(ComputeFlocTrigger trigger,
DCHECK(floc_computation_in_progress_);
floc_computation_in_progress_ = false;
// Some recompute event came in when this computation was in progress. Ignore
// this computation completely. Handle the pending one.
if (pending_recompute_event_) {
ComputeFlocTrigger recompute_trigger = pending_recompute_event_.value();
pending_recompute_event_.reset();
ComputeFloc(recompute_trigger);
// History-delete event came in when this computation was in progress. Ignore
// this computation completely and recompute.
if (need_recompute_) {
need_recompute_ = false;
ComputeFloc(trigger);
return;
}
......@@ -167,18 +164,34 @@ void FlocIdProviderImpl::Shutdown() {
void FlocIdProviderImpl::OnURLsDeleted(
history::HistoryService* history_service,
const history::DeletionInfo& deletion_info) {
// Set a pending event or override the existing one, that will get run when
// the in-progress computation finishes.
// Set the |need_recompute_| flag so that we will recompute the floc
// immediately after the in-progress one finishes, so as to avoid potential
// data races.
if (floc_computation_in_progress_) {
DCHECK(first_floc_computation_triggered_);
pending_recompute_event_ = ComputeFlocTrigger::kHistoryDelete;
need_recompute_ = true;
return;
}
if (!first_floc_computation_triggered_ || !floc_id_.IsValid())
if (!floc_id_.IsValid())
return;
// Only invalidate the floc if it's delete-all or if the time range overlaps
// with the time range of the history used to compute the current floc.
if (!deletion_info.IsAllHistory() && !deletion_info.time_range().IsValid()) {
return;
}
if (deletion_info.time_range().begin() > floc_id_.history_end_time() ||
deletion_info.time_range().end() < floc_id_.history_begin_time()) {
return;
}
ComputeFloc(ComputeFlocTrigger::kHistoryDelete);
// We log the invalidation event although it's technically not a recompute.
// It'd give us a better idea how often the floc is invalidated due to
// history-delete.
LogFlocComputedEvent(ComputeFlocTrigger::kHistoryDelete, ComputeFlocResult());
floc_id_ = FlocId();
}
void FlocIdProviderImpl::OnSortingLshClustersFileReady() {
......@@ -218,21 +231,15 @@ void FlocIdProviderImpl::MaybeTriggerFirstFlocComputation() {
}
void FlocIdProviderImpl::OnComputeFlocScheduledUpdate() {
// It's fine to skip the scheduled update as long as there's one in progress.
// We won't be losing the recomputing frequency, as the in-progress one only
// occurs sooner and when it finishes a new compute-floc task will be
// scheduled.
if (floc_computation_in_progress_)
return;
DCHECK(!pending_recompute_event_);
DCHECK(!floc_computation_in_progress_);
ComputeFloc(ComputeFlocTrigger::kScheduledUpdate);
}
void FlocIdProviderImpl::ComputeFloc(ComputeFlocTrigger trigger) {
DCHECK_NE(trigger == ComputeFlocTrigger::kBrowserStart,
first_floc_computation_triggered_);
DCHECK(trigger == ComputeFlocTrigger::kBrowserStart ||
(trigger == ComputeFlocTrigger::kScheduledUpdate &&
first_floc_computation_triggered_));
DCHECK(!floc_computation_in_progress_);
floc_computation_in_progress_ = true;
......@@ -285,14 +292,6 @@ bool FlocIdProviderImpl::AreThirdPartyCookiesAllowed() const {
void FlocIdProviderImpl::IsSwaaNacAccountEnabled(
CanComputeFlocCallback callback) {
if (!last_swaa_nac_account_enabled_query_time_.is_null() &&
last_swaa_nac_account_enabled_query_time_ +
kSwaaNacAccountEnabledCachePeriod >
base::TimeTicks::Now()) {
std::move(callback).Run(cached_swaa_nac_account_enabled_);
return;
}
net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation =
net::DefinePartialNetworkTrafficAnnotation(
"floc_id_provider_impl", "floc_remote_permission_service",
......@@ -323,17 +322,7 @@ void FlocIdProviderImpl::IsSwaaNacAccountEnabled(
})");
floc_remote_permission_service_->QueryFlocPermission(
base::BindOnce(&FlocIdProviderImpl::OnCheckSwaaNacAccountEnabledCompleted,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)),
partial_traffic_annotation);
}
void FlocIdProviderImpl::OnCheckSwaaNacAccountEnabledCompleted(
CanComputeFlocCallback callback,
bool enabled) {
cached_swaa_nac_account_enabled_ = enabled;
last_swaa_nac_account_enabled_query_time_ = base::TimeTicks::Now();
std::move(callback).Run(enabled);
std::move(callback), partial_traffic_annotation);
}
void FlocIdProviderImpl::GetRecentlyVisitedURLs(
......@@ -350,10 +339,20 @@ void FlocIdProviderImpl::OnGetRecentlyVisitedURLsCompleted(
ComputeFlocCompletedCallback callback,
history::QueryResults results) {
std::unordered_set<std::string> domains;
base::Time history_begin_time = base::Time::Max();
base::Time history_end_time = base::Time::Min();
for (const history::URLResult& url_result : results) {
if (!url_result.publicly_routable())
continue;
if (url_result.visit_time() < history_begin_time)
history_begin_time = url_result.visit_time();
if (url_result.visit_time() > history_end_time)
history_end_time = url_result.visit_time();
domains.insert(net::registry_controlled_domains::GetDomainAndRegistry(
url_result.url(),
net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES));
......@@ -365,16 +364,20 @@ void FlocIdProviderImpl::OnGetRecentlyVisitedURLsCompleted(
}
ApplySortingLshPostProcessing(std::move(callback),
FlocId::SimHashHistory(domains));
FlocId::SimHashHistory(domains),
history_begin_time, history_end_time);
}
void FlocIdProviderImpl::ApplySortingLshPostProcessing(
ComputeFlocCompletedCallback callback,
uint64_t sim_hash) {
uint64_t sim_hash,
base::Time history_begin_time,
base::Time history_end_time) {
if (!base::FeatureList::IsEnabled(
features::kFlocIdSortingLshBasedComputation)) {
std::move(callback).Run(ComputeFlocResult(
sim_hash, FlocId(sim_hash, kSortingLshVersionPlaceholder)));
sim_hash, FlocId(sim_hash, history_begin_time, history_end_time,
kSortingLshVersionPlaceholder)));
return;
}
......@@ -382,14 +385,24 @@ void FlocIdProviderImpl::ApplySortingLshPostProcessing(
sim_hash,
base::BindOnce(&FlocIdProviderImpl::DidApplySortingLshPostProcessing,
weak_ptr_factory_.GetWeakPtr(), std::move(callback),
sim_hash));
sim_hash, history_begin_time, history_end_time));
}
void FlocIdProviderImpl::DidApplySortingLshPostProcessing(
ComputeFlocCompletedCallback callback,
uint64_t sim_hash,
FlocId floc_id) {
std::move(callback).Run(ComputeFlocResult(sim_hash, std::move(floc_id)));
base::Time history_begin_time,
base::Time history_end_time,
base::Optional<uint64_t> final_hash,
base::Version version) {
if (!final_hash) {
std::move(callback).Run(ComputeFlocResult(sim_hash, FlocId()));
return;
}
std::move(callback).Run(ComputeFlocResult(
sim_hash, FlocId(final_hash.value(), history_begin_time, history_end_time,
version.components().front())));
}
} // namespace federated_learning
......@@ -42,8 +42,9 @@ class FlocRemotePermissionService;
//
// The floc will be first computed after sync & sync-history are enabled. After
// each computation, another computation will be scheduled 24 hours later. In
// the event of history deletion, the floc will be recomputed immediately and
// reset the timer of any currently scheduled computation to be 24 hours later.
// the event of history deletion, the floc will be invalidated immediately
// if the time range of the deletion overlaps with the time range used to
// compute the existing floc.
class FlocIdProviderImpl : public FlocIdProvider,
public FlocSortingLshClustersService::Observer,
public history::HistoryServiceObserver,
......@@ -134,8 +135,6 @@ class FlocIdProviderImpl : public FlocIdProvider,
bool AreThirdPartyCookiesAllowed() const;
void IsSwaaNacAccountEnabled(CanComputeFlocCallback callback);
void OnCheckSwaaNacAccountEnabledCompleted(CanComputeFlocCallback callback,
bool enabled);
void GetRecentlyVisitedURLs(GetRecentlyVisitedURLsCallback callback);
void OnGetRecentlyVisitedURLsCompleted(ComputeFlocCompletedCallback callback,
......@@ -145,10 +144,15 @@ class FlocIdProviderImpl : public FlocIdProvider,
// The final floc may be invalid if the file is corrupted or the floc end up
// being blocked.
void ApplySortingLshPostProcessing(ComputeFlocCompletedCallback callback,
uint64_t sim_hash);
uint64_t sim_hash,
base::Time history_begin_time,
base::Time history_end_time);
void DidApplySortingLshPostProcessing(ComputeFlocCompletedCallback callback,
uint64_t sim_hash,
FlocId floc_id);
base::Time history_begin_time,
base::Time history_end_time,
base::Optional<uint64_t> final_hash,
base::Version version);
// The id to be exposed to the JS API.
FlocId floc_id_;
......@@ -156,19 +160,17 @@ class FlocIdProviderImpl : public FlocIdProvider,
bool floc_computation_in_progress_ = false;
bool first_floc_computation_triggered_ = false;
// We store a pending event if it arrives during an in-progress computation.
// When the in-progress one finishes, we would disregard the result (no
// loggings, updates, etc.), and compute again.
base::Optional<ComputeFlocTrigger> pending_recompute_event_;
// True if history-delete occurs during an in-progress computation. When the
// in-progress one finishes, we would disregard the result (i.e. no loggings
// or floc update), and compute again. Potentially we could maintain extra
// states to tell if the history-delete would have impact on the in-progress
// result, but since this would only happen in rare race situations, we just
// always recompute to keep things simple.
bool need_recompute_ = false;
bool first_sorting_lsh_file_ready_seen_ = false;
bool first_sync_history_enabled_seen_ = false;
// For the swaa/nac/account_type permission, we will use a cached status to
// avoid querying too often.
bool cached_swaa_nac_account_enabled_ = false;
base::TimeTicks last_swaa_nac_account_enabled_query_time_;
syncer::SyncService* sync_service_;
scoped_refptr<content_settings::CookieSettings> cookie_settings_;
FlocRemotePermissionService* floc_remote_permission_service_;
......
......@@ -37,29 +37,31 @@ using ComputeFlocCompletedCallback =
FlocIdProviderImpl::ComputeFlocCompletedCallback;
using CanComputeFlocCallback = FlocIdProviderImpl::CanComputeFlocCallback;
const uint32_t kDummySortingLshVersion = 1;
class MockFlocSortingLshService : public FlocSortingLshClustersService {
public:
using FlocSortingLshClustersService::FlocSortingLshClustersService;
void ConfigureSortingLsh(
const std::unordered_map<uint64_t, FlocId>& sorting_lsh_map) {
const std::unordered_map<uint64_t, base::Optional<uint64_t>>&
sorting_lsh_map,
base::Version version) {
sorting_lsh_map_ = sorting_lsh_map;
version_ = version;
}
void ApplySortingLsh(uint64_t sim_hash,
ApplySortingLshCallback callback) override {
if (sorting_lsh_map_.count(sim_hash)) {
std::move(callback).Run(sorting_lsh_map_.at(sim_hash));
std::move(callback).Run(sorting_lsh_map_.at(sim_hash), version_);
return;
}
std::move(callback).Run(FlocId());
std::move(callback).Run(base::nullopt, version_);
}
private:
std::unordered_map<uint64_t, FlocId> sorting_lsh_map_;
std::unordered_map<uint64_t, base::Optional<uint64_t>> sorting_lsh_map_;
base::Version version_;
};
class FakeFlocRemotePermissionService : public FlocRemotePermissionService {
......@@ -247,9 +249,11 @@ class FlocIdProviderUnitTest : public testing::Test {
}
void ApplySortingLshPostProcessing(ComputeFlocCompletedCallback callback,
uint64_t sim_hash) {
floc_id_provider_->ApplySortingLshPostProcessing(std::move(callback),
sim_hash);
uint64_t sim_hash,
base::Time history_begin_time,
base::Time history_end_time) {
floc_id_provider_->ApplySortingLshPostProcessing(
std::move(callback), sim_hash, history_begin_time, history_end_time);
}
void CheckCanComputeFloc(CanComputeFlocCallback callback) {
......@@ -275,11 +279,12 @@ class FlocIdProviderUnitTest : public testing::Test {
std::move(compute_floc_completed_callback), std::move(results));
}
void ExpireHistoryBefore(base::Time end_time) {
void ExpireHistoryBeforeUninclusive(base::Time end_time) {
base::CancelableTaskTracker tracker;
base::RunLoop run_loop;
history_service_->ExpireHistoryBeforeForTesting(
end_time, run_loop.QuitClosure(), &tracker);
history_service_->ExpireHistoryBetween(
/*restrict_urls=*/{}, /*begin_time=*/base::Time(), end_time,
/*user_initiated=*/true, run_loop.QuitClosure(), &tracker);
run_loop.Run();
}
......@@ -310,9 +315,7 @@ class FlocIdProviderUnitTest : public testing::Test {
floc_id_provider_->floc_id_ = floc_id;
}
base::Optional<ComputeFlocTrigger> pending_recompute_event() {
return floc_id_provider_->pending_recompute_event_;
}
bool need_recompute() { return floc_id_provider_->need_recompute_; }
void SetRemoteSwaaNacAccountEnabled(bool enabled) {
fake_floc_remote_permission_service_->set_swaa_nac_account_enabled(enabled);
......@@ -348,10 +351,11 @@ class FlocIdProviderUnitTest : public testing::Test {
TEST_F(FlocIdProviderUnitTest, QualifiedInitialHistory) {
// Add a history entry with a timestamp exactly 7 days back from now.
std::string domain = "foo.com";
const base::Time kTime = base::Time::Now() - base::TimeDelta::FromDays(7);
history::HistoryAddPageArgs add_page_args;
add_page_args.url = GURL(base::StrCat({"https://www.", domain}));
add_page_args.time = base::Time::Now() - base::TimeDelta::FromDays(7);
add_page_args.time = kTime;
add_page_args.publicly_routable = true;
history_service_->AddPage(add_page_args);
......@@ -374,8 +378,8 @@ TEST_F(FlocIdProviderUnitTest, QualifiedInitialHistory) {
// Expect that the 1st computation has completed.
EXPECT_EQ(1u, floc_id_provider_->compute_floc_completed_count());
EXPECT_EQ(1u, floc_id_provider_->log_event_count());
EXPECT_TRUE(floc_id().IsValid());
EXPECT_EQ(FlocId::SimHashHistory({domain}), floc_id().ToUint64());
EXPECT_EQ(FlocId(FlocId::SimHashHistory({domain}), kTime, kTime, 0),
floc_id());
EXPECT_TRUE(first_floc_computation_triggered());
// Advance the clock by 1 day. Expect a computation, as there's no history in
......@@ -419,7 +423,8 @@ TEST_F(FlocIdProviderUnitTest, UnqualifiedInitialHistory) {
EXPECT_TRUE(first_floc_computation_triggered());
// Add a history entry with a timestamp 6 days back from now.
add_page_args.time = base::Time::Now() - base::TimeDelta::FromDays(6);
const base::Time kTime = base::Time::Now() - base::TimeDelta::FromDays(6);
add_page_args.time = kTime;
history_service_->AddPage(add_page_args);
// Advance the clock by 23 hours. Expect no more computation, as the id
......@@ -435,8 +440,9 @@ TEST_F(FlocIdProviderUnitTest, UnqualifiedInitialHistory) {
EXPECT_EQ(2u, floc_id_provider_->compute_floc_completed_count());
EXPECT_EQ(2u, floc_id_provider_->log_event_count());
EXPECT_TRUE(floc_id().IsValid());
EXPECT_EQ(FlocId::SimHashHistory({domain}), floc_id().ToUint64());
EXPECT_EQ(FlocId(FlocId::SimHashHistory({domain}), kTime, kTime, 0),
floc_id());
}
TEST_F(FlocIdProviderUnitTest, HistoryDeleteAndScheduledUpdate) {
......@@ -445,14 +451,16 @@ TEST_F(FlocIdProviderUnitTest, HistoryDeleteAndScheduledUpdate) {
// Add a history entry with a timestamp exactly 7 days back from now.
history::HistoryAddPageArgs add_page_args;
const base::Time kTime1 = base::Time::Now() - base::TimeDelta::FromDays(7);
add_page_args.url = GURL(base::StrCat({"https://www.", domain1}));
add_page_args.time = base::Time::Now() - base::TimeDelta::FromDays(7);
add_page_args.time = kTime1;
add_page_args.publicly_routable = true;
history_service_->AddPage(add_page_args);
// Add a history entry with a timestamp exactly 6 days back from now.
add_page_args.url = GURL(base::StrCat({"https://www.", domain2}));
add_page_args.time = base::Time::Now() - base::TimeDelta::FromDays(6);
const base::Time kTime2 = base::Time::Now() - base::TimeDelta::FromDays(6);
add_page_args.time = kTime2;
history_service_->AddPage(add_page_args);
task_environment_.RunUntilIdle();
......@@ -467,8 +475,9 @@ TEST_F(FlocIdProviderUnitTest, HistoryDeleteAndScheduledUpdate) {
// Expect that the 1st computation has completed.
EXPECT_EQ(1u, floc_id_provider_->compute_floc_completed_count());
EXPECT_EQ(1u, floc_id_provider_->log_event_count());
EXPECT_TRUE(floc_id().IsValid());
EXPECT_EQ(FlocId::SimHashHistory({domain1, domain2}), floc_id().ToUint64());
EXPECT_EQ(
FlocId(FlocId::SimHashHistory({domain1, domain2}), kTime1, kTime2, 0),
floc_id());
// Advance the clock by 12 hours. Expect no more computation.
task_environment_.FastForwardBy(base::TimeDelta::FromHours(12));
......@@ -476,37 +485,33 @@ TEST_F(FlocIdProviderUnitTest, HistoryDeleteAndScheduledUpdate) {
EXPECT_EQ(1u, floc_id_provider_->log_event_count());
// Expire the oldest history entry.
ExpireHistoryBefore(base::Time::Now() - base::TimeDelta::FromDays(7));
ExpireHistoryBeforeUninclusive(kTime2);
task_environment_.RunUntilIdle();
// Expect one more computation due to the history deletion.
EXPECT_EQ(2u, floc_id_provider_->compute_floc_completed_count());
// Expect that the floc has been invalidated. Expect no more floc computation,
// but one more logging.
EXPECT_EQ(1u, floc_id_provider_->compute_floc_completed_count());
EXPECT_EQ(2u, floc_id_provider_->log_event_count());
EXPECT_TRUE(floc_id().IsValid());
EXPECT_EQ(FlocId::SimHashHistory({domain2}), floc_id().ToUint64());
EXPECT_FALSE(floc_id().IsValid());
// Advance the clock by 23 hours. Expect no more computation, as the timer has
// been reset due to the recomputation from history deletion.
task_environment_.FastForwardBy(base::TimeDelta::FromHours(23));
// Advance the clock by 12 hours. Expect one more computation, which implies
// the timer didn't get reset due to the history invalidation. Expect that
// the floc is derived from domain2.
task_environment_.FastForwardBy(base::TimeDelta::FromHours(12));
EXPECT_EQ(2u, floc_id_provider_->compute_floc_completed_count());
EXPECT_EQ(2u, floc_id_provider_->log_event_count());
// Advance the clock by 1 hour. Expect one more computation, as the scheduled
// time is reached. Expect an invalid floc id as there is no history in the
// past 7 days.
task_environment_.FastForwardBy(base::TimeDelta::FromHours(1));
EXPECT_EQ(3u, floc_id_provider_->compute_floc_completed_count());
EXPECT_EQ(3u, floc_id_provider_->log_event_count());
EXPECT_FALSE(floc_id().IsValid());
EXPECT_EQ(FlocId(FlocId::SimHashHistory({domain2}), kTime2, kTime2, 0),
floc_id());
}
TEST_F(FlocIdProviderUnitTest, ScheduledUpdateSameFloc_NoNotification) {
TEST_F(FlocIdProviderUnitTest, ScheduledUpdateSameFloc) {
std::string domain = "foo.com";
const base::Time kTime = base::Time::Now() - base::TimeDelta::FromDays(2);
// Add a history entry with a timestamp 2 days back from now.
history::HistoryAddPageArgs add_page_args;
add_page_args.url = GURL(base::StrCat({"https://www.", domain}));
add_page_args.time = base::Time::Now() - base::TimeDelta::FromDays(2);
add_page_args.time = kTime;
add_page_args.publicly_routable = true;
history_service_->AddPage(add_page_args);
......@@ -522,7 +527,8 @@ TEST_F(FlocIdProviderUnitTest, ScheduledUpdateSameFloc_NoNotification) {
// Expect that the 1st computation has completed.
EXPECT_EQ(1u, floc_id_provider_->compute_floc_completed_count());
EXPECT_EQ(1u, floc_id_provider_->log_event_count());
EXPECT_EQ(FlocId::SimHashHistory({domain}), floc_id().ToUint64());
EXPECT_EQ(FlocId(FlocId::SimHashHistory({domain}), kTime, kTime, 0),
floc_id());
// Advance the clock by 1 day. Expect one more computation, but the floc
// didn't change.
......@@ -530,7 +536,8 @@ TEST_F(FlocIdProviderUnitTest, ScheduledUpdateSameFloc_NoNotification) {
EXPECT_EQ(2u, floc_id_provider_->compute_floc_completed_count());
EXPECT_EQ(2u, floc_id_provider_->log_event_count());
EXPECT_EQ(FlocId::SimHashHistory({domain}), floc_id().ToUint64());
EXPECT_EQ(FlocId(FlocId::SimHashHistory({domain}), kTime, kTime, 0),
floc_id());
}
TEST_F(FlocIdProviderUnitTest, CheckCanComputeFloc_Success) {
......@@ -580,46 +587,14 @@ TEST_F(FlocIdProviderUnitTest,
task_environment_.RunUntilIdle();
}
TEST_F(FlocIdProviderUnitTest, SwaaNacAccountEnabledUseCacheStatus) {
base::OnceCallback<void(bool)> assert_enabled_callback_1 = base::BindOnce(
[](bool can_compute_floc) { EXPECT_TRUE(can_compute_floc); });
// The permission status in the fake_floc_remote_premission_service_ is by
// default enabled.
IsSwaaNacAccountEnabled(std::move(assert_enabled_callback_1));
task_environment_.RunUntilIdle();
// Turn off the permission in the fake_floc_remote_premission_service_.
SetRemoteSwaaNacAccountEnabled(false);
base::OnceCallback<void(bool)> assert_enabled_callback_2 = base::BindOnce(
[](bool can_compute_floc) { EXPECT_TRUE(can_compute_floc); });
// Fast forward by 11 hours. The cache is still valid.
task_environment_.FastForwardBy(base::TimeDelta::FromHours(11));
// The permission status is still enabled because it was obtained from the
// cache.
IsSwaaNacAccountEnabled(std::move(assert_enabled_callback_2));
task_environment_.RunUntilIdle();
// Fast forward by 1 hour so the cache becomes invalid.
task_environment_.FastForwardBy(base::TimeDelta::FromHours(1));
base::OnceCallback<void(bool)> assert_disabled_callback = base::BindOnce(
[](bool can_compute_floc) { EXPECT_FALSE(can_compute_floc); });
// The permission status should be obtained from the server again, and it's
// now disabled.
IsSwaaNacAccountEnabled(std::move(assert_disabled_callback));
task_environment_.RunUntilIdle();
}
TEST_F(FlocIdProviderUnitTest, EventLogging) {
const base::Time kTime1 = base::Time::FromTimeT(1);
const base::Time kTime2 = base::Time::FromTimeT(2);
// Event logging for browser start.
floc_id_provider_->LogFlocComputedEvent(
ComputeFlocTrigger::kBrowserStart,
ComputeFlocResult(12345ULL, FlocId(123ULL, kDummySortingLshVersion)));
ComputeFlocResult(12345ULL, FlocId(123ULL, kTime1, kTime1, 999)));
EXPECT_EQ(1u, fake_user_event_service_->GetRecordedUserEvents().size());
const sync_pb::UserEventSpecifics& specifics1 =
......@@ -641,7 +616,7 @@ TEST_F(FlocIdProviderUnitTest, EventLogging) {
// Event logging for scheduled update.
floc_id_provider_->LogFlocComputedEvent(
ComputeFlocTrigger::kScheduledUpdate,
ComputeFlocResult(999ULL, FlocId(777ULL, kDummySortingLshVersion)));
ComputeFlocResult(999ULL, FlocId(777ULL, kTime1, kTime2, 888)));
EXPECT_EQ(2u, fake_user_event_service_->GetRecordedUserEvents().size());
const sync_pb::UserEventSpecifics& specifics2 =
......@@ -675,10 +650,9 @@ TEST_F(FlocIdProviderUnitTest, EventLogging) {
event3.event_trigger());
EXPECT_FALSE(event3.has_floc_id());
// Event logging for history delete.
floc_id_provider_->LogFlocComputedEvent(
ComputeFlocTrigger::kHistoryDelete,
ComputeFlocResult(555, FlocId(444, kDummySortingLshVersion)));
// Event logging for history-delete invalidation.
floc_id_provider_->LogFlocComputedEvent(ComputeFlocTrigger::kHistoryDelete,
ComputeFlocResult());
EXPECT_EQ(4u, fake_user_event_service_->GetRecordedUserEvents().size());
const sync_pb::UserEventSpecifics& specifics4 =
......@@ -692,7 +666,7 @@ TEST_F(FlocIdProviderUnitTest, EventLogging) {
specifics4.floc_id_computed_event();
EXPECT_EQ(sync_pb::UserEventSpecifics::FlocIdComputed::HISTORY_DELETE,
event4.event_trigger());
EXPECT_EQ(555ULL, event4.floc_id());
EXPECT_FALSE(event4.has_floc_id());
// Event logging for blocked floc.
floc_id_provider_->LogFlocComputedEvent(ComputeFlocTrigger::kScheduledUpdate,
......@@ -714,75 +688,99 @@ TEST_F(FlocIdProviderUnitTest, EventLogging) {
}
TEST_F(FlocIdProviderUnitTest, HistoryDelete_AllHistory) {
base::Time time = base::Time::Now() - base::TimeDelta::FromDays(9);
history::URLResult url_result(GURL("https://a.test"), time);
url_result.set_publicly_routable(true);
history::QueryResults query_results;
query_results.SetURLResults({url_result});
const base::Time kTime1 = base::Time::FromTimeT(1);
const base::Time kTime2 = base::Time::FromTimeT(2);
set_floc_id(FlocId(123, kTime1, kTime2, 0));
set_first_floc_computation_triggered(true);
set_floc_computation_in_progress(true);
OnGetRecentlyVisitedURLsCompleted(ComputeFlocTrigger::kBrowserStart,
std::move(query_results));
EXPECT_FALSE(floc_computation_in_progress());
EXPECT_TRUE(floc_id().IsValid());
OnURLsDeleted(history_service_.get(), history::DeletionInfo::ForAllHistory());
EXPECT_FALSE(floc_id().IsValid());
}
TEST_F(FlocIdProviderUnitTest, HistoryDelete_InvalidTimeRange) {
base::Time time = base::Time::Now() - base::TimeDelta::FromDays(9);
const base::Time kTime1 = base::Time::FromTimeT(1);
const base::Time kTime2 = base::Time::FromTimeT(2);
GURL url_a = GURL("https://a.test");
history::URLResult url_result(url_a, time);
history::URLResult url_result(url_a, kTime1);
url_result.set_publicly_routable(true);
history::QueryResults query_results;
query_results.SetURLResults({url_result});
set_first_floc_computation_triggered(true);
set_floc_computation_in_progress(true);
const FlocId expected_floc =
FlocId(FlocId::SimHashHistory({"a.test"}), kTime1, kTime2, 0);
OnGetRecentlyVisitedURLsCompleted(ComputeFlocTrigger::kBrowserStart,
std::move(query_results));
EXPECT_FALSE(floc_computation_in_progress());
EXPECT_TRUE(floc_id().IsValid());
set_floc_id(expected_floc);
set_first_floc_computation_triggered(true);
OnURLsDeleted(history_service_.get(),
history::DeletionInfo::ForUrls(
{history::URLResult(url_a, base::Time())}, {}));
task_environment_.RunUntilIdle();
EXPECT_FALSE(floc_id().IsValid());
{history::URLResult(url_a, kTime1)}, /*favicon_urls=*/{}));
EXPECT_EQ(expected_floc, floc_id());
}
TEST_F(FlocIdProviderUnitTest, HistoryDelete_TimeRange) {
base::Time time = base::Time::Now() - base::TimeDelta::FromDays(9);
TEST_F(FlocIdProviderUnitTest, HistoryDelete_TimeRangeNoOverlap) {
const base::Time kTime1 = base::Time::FromTimeT(1);
const base::Time kTime2 = base::Time::FromTimeT(2);
const base::Time kTime3 = base::Time::FromTimeT(3);
const base::Time kTime4 = base::Time::FromTimeT(4);
history::URLResult url_result(GURL("https://a.test"), time);
url_result.set_publicly_routable(true);
const FlocId expected_floc =
FlocId(FlocId::SimHashHistory({"a.test"}), kTime1, kTime2, 0);
history::QueryResults query_results;
query_results.SetURLResults({url_result});
set_floc_id(expected_floc);
set_first_floc_computation_triggered(true);
history::DeletionInfo deletion_info(
history::DeletionTimeRange(kTime3, kTime4),
/*is_from_expiration=*/false, /*deleted_rows=*/{}, /*favicon_urls=*/{},
/*restrict_urls=*/base::nullopt);
OnURLsDeleted(history_service_.get(), deletion_info);
EXPECT_EQ(expected_floc, floc_id());
}
TEST_F(FlocIdProviderUnitTest, HistoryDelete_TimeRangePartialOverlap) {
const base::Time kTime1 = base::Time::FromTimeT(1);
const base::Time kTime2 = base::Time::FromTimeT(2);
const base::Time kTime3 = base::Time::FromTimeT(3);
const FlocId expected_floc =
FlocId(FlocId::SimHashHistory({"a.test"}), kTime1, kTime2, 0);
set_floc_id(expected_floc);
set_first_floc_computation_triggered(true);
set_floc_computation_in_progress(true);
OnGetRecentlyVisitedURLsCompleted(ComputeFlocTrigger::kBrowserStart,
std::move(query_results));
EXPECT_FALSE(floc_computation_in_progress());
EXPECT_TRUE(floc_id().IsValid());
history::DeletionInfo deletion_info(
history::DeletionTimeRange(kTime2, kTime3),
/*is_from_expiration=*/false, /*deleted_rows=*/{}, /*favicon_urls=*/{},
/*restrict_urls=*/base::nullopt);
OnURLsDeleted(history_service_.get(), deletion_info);
EXPECT_FALSE(floc_id().IsValid());
}
TEST_F(FlocIdProviderUnitTest, HistoryDelete_TimeRangeFullOverlap) {
const base::Time kTime1 = base::Time::FromTimeT(1);
const base::Time kTime2 = base::Time::FromTimeT(2);
history::DeletionInfo deletion_info(history::DeletionTimeRange(time, time),
false, {}, {},
base::Optional<std::set<GURL>>());
const FlocId expected_floc =
FlocId(FlocId::SimHashHistory({"a.test"}), kTime1, kTime2, 0);
set_floc_id(expected_floc);
set_first_floc_computation_triggered(true);
history::DeletionInfo deletion_info(
history::DeletionTimeRange(kTime1, kTime2),
/*is_from_expiration=*/false, /*deleted_rows=*/{}, /*favicon_urls=*/{},
/*restrict_urls=*/base::nullopt);
OnURLsDeleted(history_service_.get(), deletion_info);
task_environment_.RunUntilIdle();
EXPECT_FALSE(floc_id().IsValid());
}
......@@ -802,15 +800,17 @@ TEST_F(FlocIdProviderUnitTest, HistoryEntriesWithPrivateIP) {
}
TEST_F(FlocIdProviderUnitTest, MultipleHistoryEntries) {
base::Time time = base::Time::Now() - base::TimeDelta::FromDays(1);
const base::Time kTime1 = base::Time::FromTimeT(1);
const base::Time kTime2 = base::Time::FromTimeT(2);
const base::Time kTime3 = base::Time::FromTimeT(3);
history::URLResult url_result_a(GURL("https://a.test"), time);
history::URLResult url_result_a(GURL("https://a.test"), kTime1);
url_result_a.set_publicly_routable(true);
history::URLResult url_result_b(GURL("https://b.test"), time);
history::URLResult url_result_b(GURL("https://b.test"), kTime2);
url_result_b.set_publicly_routable(true);
history::URLResult url_result_c(GURL("https://c.test"), time);
history::URLResult url_result_c(GURL("https://c.test"), kTime3);
std::vector<history::URLResult> url_results{url_result_a, url_result_b,
url_result_c};
......@@ -824,15 +824,18 @@ TEST_F(FlocIdProviderUnitTest, MultipleHistoryEntries) {
OnGetRecentlyVisitedURLsCompleted(ComputeFlocTrigger::kBrowserStart,
std::move(query_results));
EXPECT_EQ(FlocId::SimHashHistory({"a.test", "b.test"}), floc_id().ToUint64());
EXPECT_EQ(
FlocId(FlocId::SimHashHistory({"a.test", "b.test"}), kTime1, kTime2, 0),
floc_id());
}
TEST_F(FlocIdProviderUnitTest, TurnSyncOffAndOn) {
std::string domain = "foo.com";
const base::Time kTime = base::Time::Now() - base::TimeDelta::FromDays(1);
history::HistoryAddPageArgs add_page_args;
add_page_args.url = GURL(base::StrCat({"https://www.", domain}));
add_page_args.time = base::Time::Now() - base::TimeDelta::FromDays(1);
add_page_args.time = kTime;
add_page_args.publicly_routable = true;
history_service_->AddPage(add_page_args);
......@@ -848,7 +851,8 @@ TEST_F(FlocIdProviderUnitTest, TurnSyncOffAndOn) {
// Expect that the 1st computation has completed.
EXPECT_EQ(1u, floc_id_provider_->compute_floc_completed_count());
EXPECT_EQ(1u, floc_id_provider_->log_event_count());
EXPECT_EQ(FlocId::SimHashHistory({domain}), floc_id().ToUint64());
EXPECT_EQ(FlocId(FlocId::SimHashHistory({domain}), kTime, kTime, 0),
floc_id());
// Turn off sync.
test_sync_service_->SetTransportState(
......@@ -871,22 +875,29 @@ TEST_F(FlocIdProviderUnitTest, TurnSyncOffAndOn) {
EXPECT_EQ(3u, floc_id_provider_->compute_floc_completed_count());
EXPECT_EQ(3u, floc_id_provider_->log_event_count());
EXPECT_EQ(FlocId::SimHashHistory({domain}), floc_id().ToUint64());
EXPECT_EQ(FlocId(FlocId::SimHashHistory({domain}), kTime, kTime, 0),
floc_id());
}
TEST_F(FlocIdProviderUnitTest, GetInterestCohortForJsApiMethod) {
test_sync_service_->SetTransportState(
syncer::SyncService::TransportState::ACTIVE);
set_floc_id(FlocId(123, kDummySortingLshVersion));
const base::Time kTime = base::Time::Now() - base::TimeDelta::FromDays(1);
const FlocId expected_floc = FlocId(123, kTime, kTime, 999);
EXPECT_EQ(FlocId(123, kDummySortingLshVersion).ToString(),
set_floc_id(expected_floc);
EXPECT_EQ(expected_floc.ToStringForJsApi(),
floc_id_provider_->GetInterestCohortForJsApi(
/*requesting_origin=*/{}, /*site_for_cookies=*/{}));
}
TEST_F(FlocIdProviderUnitTest,
GetInterestCohortForJsApiMethod_SyncHistoryDisabled) {
set_floc_id(FlocId(123, kDummySortingLshVersion));
const base::Time kTime = base::Time::Now() - base::TimeDelta::FromDays(1);
set_floc_id(FlocId(123, kTime, kTime, 888));
EXPECT_EQ(std::string(),
floc_id_provider_->GetInterestCohortForJsApi(
/*requesting_origin=*/{}, /*site_for_cookies=*/{}));
......@@ -896,10 +907,12 @@ TEST_F(FlocIdProviderUnitTest,
GetInterestCohortForJsApiMethod_ThirdPartyCookiesDisabled) {
test_sync_service_->SetTransportState(
syncer::SyncService::TransportState::ACTIVE);
set_floc_id(FlocId(123, kDummySortingLshVersion));
fake_cookie_settings_->set_should_block_third_party_cookies(true);
const base::Time kTime = base::Time::Now() - base::TimeDelta::FromDays(1);
set_floc_id(FlocId(123, kTime, kTime, 999));
EXPECT_EQ(std::string(),
floc_id_provider_->GetInterestCohortForJsApi(
/*requesting_origin=*/{}, /*site_for_cookies=*/{}));
......@@ -909,10 +922,12 @@ TEST_F(FlocIdProviderUnitTest,
GetInterestCohortForJsApiMethod_CookiesContentSettingsDisallowed) {
test_sync_service_->SetTransportState(
syncer::SyncService::TransportState::ACTIVE);
set_floc_id(FlocId(123, kDummySortingLshVersion));
fake_cookie_settings_->set_allow_cookies_internal(false);
const base::Time kTime = base::Time::Now() - base::TimeDelta::FromDays(1);
set_floc_id(FlocId(123, kTime, kTime, 999));
EXPECT_EQ(std::string(),
floc_id_provider_->GetInterestCohortForJsApi(
/*requesting_origin=*/{}, /*site_for_cookies=*/{}));
......@@ -929,31 +944,49 @@ TEST_F(FlocIdProviderUnitTest,
}
TEST_F(FlocIdProviderUnitTest, HistoryDeleteDuringInProgressComputation) {
base::test::ScopedFeatureList feature_list;
feature_list.InitWithFeatures({features::kFlocIdSortingLshBasedComputation},
{});
std::string domain1 = "foo.com";
std::string domain2 = "bar.com";
std::string domain3 = "baz.com";
const base::Time kTime1 = base::Time::Now() - base::TimeDelta::FromDays(7);
const base::Time kTime2 = base::Time::Now() - base::TimeDelta::FromDays(6);
const base::Time kTime3 = base::Time::Now() - base::TimeDelta::FromDays(5);
// Add a history entry with a timestamp exactly 7 days back from now.
history::HistoryAddPageArgs add_page_args;
add_page_args.url = GURL(base::StrCat({"https://www.", domain1}));
add_page_args.time = base::Time::Now() - base::TimeDelta::FromDays(7);
add_page_args.time = kTime1;
add_page_args.publicly_routable = true;
history_service_->AddPage(add_page_args);
// Add a history entry with a timestamp exactly 6 days back from now.
add_page_args.url = GURL(base::StrCat({"https://www.", domain2}));
add_page_args.time = base::Time::Now() - base::TimeDelta::FromDays(6);
add_page_args.time = kTime2;
history_service_->AddPage(add_page_args);
// Add a history entry with a timestamp exactly 5 days back from now.
add_page_args.url = GURL(base::StrCat({"https://www.", domain3}));
add_page_args.time = base::Time::Now() - base::TimeDelta::FromDays(5);
add_page_args.time = kTime3;
history_service_->AddPage(add_page_args);
// Map SimHashHistory({domain1, domain2, domain3}) to 123.
// Map SimHashHistory({domain2, domain3}) to 456.
// Map SimHashHistory({domain3}) to 789.
sorting_lsh_service_->ConfigureSortingLsh(
{{FlocId::SimHashHistory({domain1, domain2, domain3}), 123},
{FlocId::SimHashHistory({domain2, domain3}), 456},
{FlocId::SimHashHistory({domain3}), 789}},
base::Version("999.0.0"));
// Trigger the 1st floc computation.
test_sync_service_->SetTransportState(
syncer::SyncService::TransportState::ACTIVE);
test_sync_service_->FireStateChanged();
sorting_lsh_service_->OnSortingLshClustersFileReady(base::FilePath(),
base::Version());
task_environment_.RunUntilIdle();
......@@ -961,8 +994,7 @@ TEST_F(FlocIdProviderUnitTest, HistoryDeleteDuringInProgressComputation) {
EXPECT_EQ(1u, floc_id_provider_->compute_floc_completed_count());
EXPECT_EQ(1u, floc_id_provider_->log_event_count());
EXPECT_TRUE(floc_id().IsValid());
EXPECT_EQ(FlocId::SimHashHistory({domain1, domain2, domain3}),
floc_id().ToUint64());
EXPECT_EQ(FlocId(123, kTime1, kTime3, 999), floc_id());
// Advance the clock by 1 day. The "domain1" should expire. However, we pause
// before the computation completes.
......@@ -970,23 +1002,19 @@ TEST_F(FlocIdProviderUnitTest, HistoryDeleteDuringInProgressComputation) {
task_environment_.FastForwardBy(base::TimeDelta::FromDays(1));
EXPECT_TRUE(floc_computation_in_progress());
EXPECT_FALSE(pending_recompute_event().has_value());
EXPECT_EQ(FlocId::SimHashHistory({domain1, domain2, domain3}),
floc_id().ToUint64());
EXPECT_EQ(FlocId::SimHashHistory({domain2, domain3}),
floc_id_provider_->paused_result().floc_id.ToUint64());
EXPECT_FALSE(need_recompute());
EXPECT_EQ(FlocId(123, kTime1, kTime3, 999), floc_id());
EXPECT_EQ(FlocId(456, kTime2, kTime3, 999),
floc_id_provider_->paused_result().floc_id);
EXPECT_EQ(ComputeFlocTrigger::kScheduledUpdate,
floc_id_provider_->paused_trigger());
// Expire the "domain2" history entry right before the floc computation
// completes. Since the computation is still considered to be in-progress, a
// new recompute event due to this delete will be scheduled to happen right
// after this computation completes.
ExpireHistoryBefore(base::Time::Now() - base::TimeDelta::FromDays(7));
// completes. Since the computation is still considered to be in-progress, we
// will recompute right after this computation completes.
ExpireHistoryBeforeUninclusive(kTime3);
EXPECT_TRUE(pending_recompute_event().has_value());
EXPECT_EQ(ComputeFlocTrigger::kHistoryDelete,
pending_recompute_event().value());
EXPECT_TRUE(need_recompute());
floc_id_provider_->set_should_pause_before_compute_floc_completed(false);
floc_id_provider_->ContinueLastOnComputeFlocCompleted();
......@@ -994,49 +1022,17 @@ TEST_F(FlocIdProviderUnitTest, HistoryDeleteDuringInProgressComputation) {
// Expect 2 more compute completion events and 1 more log event. This is
// because we won't send log event if there's a recompute event scheduled.
// The compute trigger should be the original trigger (i.e. kScheduledUpdate),
// rather than kHistoryDelete.
EXPECT_EQ(3u, floc_id_provider_->compute_floc_completed_count());
EXPECT_EQ(2u, floc_id_provider_->log_event_count());
EXPECT_EQ(ComputeFlocTrigger::kHistoryDelete,
EXPECT_EQ(ComputeFlocTrigger::kScheduledUpdate,
floc_id_provider_->last_log_event_trigger());
EXPECT_FALSE(pending_recompute_event().has_value());
EXPECT_FALSE(need_recompute());
// The final floc should be derived from "domain3".
EXPECT_TRUE(floc_id().IsValid());
EXPECT_EQ(FlocId::SimHashHistory({domain3}), floc_id().ToUint64());
}
TEST_F(FlocIdProviderUnitTest, ScheduledUpdateDuringInProgressComputation) {
std::string domain1 = "foo.com";
std::string domain2 = "bar.com";
std::string domain3 = "baz.com";
// Add a history entry with a timestamp exactly 7 days back from now.
history::HistoryAddPageArgs add_page_args;
add_page_args.url = GURL(base::StrCat({"https://www.", domain1}));
add_page_args.time = base::Time::Now() - base::TimeDelta::FromDays(7);
add_page_args.publicly_routable = true;
history_service_->AddPage(add_page_args);
// Trigger the 1st floc computation.
test_sync_service_->SetTransportState(
syncer::SyncService::TransportState::ACTIVE);
test_sync_service_->FireStateChanged();
EXPECT_TRUE(floc_computation_in_progress());
EXPECT_FALSE(pending_recompute_event().has_value());
// Scheduled update during an in-progress computation won't set the pending
// event.
ForceScheduledUpdate();
EXPECT_FALSE(pending_recompute_event().has_value());
task_environment_.RunUntilIdle();
// Expect that the 1st computation has completed.
EXPECT_EQ(1u, floc_id_provider_->compute_floc_completed_count());
EXPECT_EQ(1u, floc_id_provider_->log_event_count());
EXPECT_TRUE(floc_id().IsValid());
EXPECT_EQ(FlocId::SimHashHistory({domain1}), floc_id().ToUint64());
EXPECT_EQ(FlocId(789, kTime3, kTime3, 999), floc_id());
}
class FlocIdProviderUnitTestSortingLshEnabled : public FlocIdProviderUnitTest {
......@@ -1085,26 +1081,33 @@ TEST_F(FlocIdProviderUnitTestSortingLshEnabled,
TEST_F(FlocIdProviderUnitTestSortingLshEnabled,
ApplyAdditionalFiltering_SortingLsh) {
const base::Time kTime1 = base::Time::FromTimeT(1);
const base::Time kTime2 = base::Time::FromTimeT(2);
bool callback_called = false;
auto callback = base::BindLambdaForTesting([&](ComputeFlocResult result) {
EXPECT_FALSE(callback_called);
EXPECT_EQ(result.sim_hash, 3u);
EXPECT_EQ(result.floc_id, FlocId(2, 99));
EXPECT_EQ(result.floc_id, FlocId(2, kTime1, kTime2, 99));
callback_called = true;
});
// Map 3 to 2
sorting_lsh_service_->OnSortingLshClustersFileReady(base::FilePath(),
base::Version());
sorting_lsh_service_->ConfigureSortingLsh({{3, FlocId(2, 99)}});
sorting_lsh_service_->ConfigureSortingLsh({{3, 2}}, base::Version("99.0"));
ApplySortingLshPostProcessing(std::move(callback), /*sim_hash=*/3);
ApplySortingLshPostProcessing(std::move(callback), /*sim_hash=*/3, kTime1,
kTime2);
task_environment_.RunUntilIdle();
EXPECT_TRUE(callback_called);
}
TEST_F(FlocIdProviderUnitTestSortingLshEnabled,
ApplySortingLshPostProcessing_FileCorrupted) {
const base::Time kTime1 = base::Time::FromTimeT(1);
const base::Time kTime2 = base::Time::FromTimeT(2);
bool callback_called = false;
auto callback = base::BindLambdaForTesting([&](ComputeFlocResult result) {
EXPECT_FALSE(callback_called);
......@@ -1115,19 +1118,21 @@ TEST_F(FlocIdProviderUnitTestSortingLshEnabled,
sorting_lsh_service_->OnSortingLshClustersFileReady(base::FilePath(),
base::Version());
sorting_lsh_service_->ConfigureSortingLsh({});
sorting_lsh_service_->ConfigureSortingLsh({}, base::Version("3.4.5"));
ApplySortingLshPostProcessing(std::move(callback), /*sim_hash=*/3);
ApplySortingLshPostProcessing(std::move(callback), /*sim_hash=*/3, kTime1,
kTime2);
task_environment_.RunUntilIdle();
EXPECT_TRUE(callback_called);
}
TEST_F(FlocIdProviderUnitTestSortingLshEnabled, SortingLshPostProcessing) {
std::string domain = "foo.com";
const base::Time kTime = base::Time::Now() - base::TimeDelta::FromDays(1);
history::HistoryAddPageArgs add_page_args;
add_page_args.url = GURL(base::StrCat({"https://www.", domain}));
add_page_args.time = base::Time::Now() - base::TimeDelta::FromDays(1);
add_page_args.time = kTime;
add_page_args.publicly_routable = true;
history_service_->AddPage(add_page_args);
......@@ -1136,7 +1141,8 @@ TEST_F(FlocIdProviderUnitTestSortingLshEnabled, SortingLshPostProcessing) {
uint64_t sim_hash = FlocId::SimHashHistory({domain});
// Configure the |sorting_lsh_service_| to map |sim_hash| to 12345.
sorting_lsh_service_->ConfigureSortingLsh({{sim_hash, FlocId(12345, 99)}});
sorting_lsh_service_->ConfigureSortingLsh({{sim_hash, 12345}},
base::Version("99.0"));
// Trigger the sorting-lsh ready event, and turn on sync & sync-history to
// trigger the 1st floc computation.
......@@ -1154,10 +1160,11 @@ TEST_F(FlocIdProviderUnitTestSortingLshEnabled, SortingLshPostProcessing) {
// Expect a computation. The floc should be equal to 12345.
EXPECT_EQ(1u, floc_id_provider_->compute_floc_completed_count());
EXPECT_EQ(1u, floc_id_provider_->log_event_count());
EXPECT_EQ(FlocId(12345, 99), floc_id());
EXPECT_EQ(FlocId(12345, kTime, kTime, 99), floc_id());
// Configure the |sorting_lsh_service_| to block |sim_hash|.
sorting_lsh_service_->ConfigureSortingLsh({{sim_hash, FlocId()}});
sorting_lsh_service_->ConfigureSortingLsh({{sim_hash, base::nullopt}},
base::Version("3.4.5"));
task_environment_.FastForwardBy(base::TimeDelta::FromDays(1));
......@@ -1189,15 +1196,15 @@ TEST_F(FlocIdProviderUnitTestSortingLshEnabled, SortingLshPostProcessing) {
EXPECT_EQ(sim_hash, event.floc_id());
// Configure the |sorting_lsh_service_| to map |sim_hash| to 6789.
sorting_lsh_service_->ConfigureSortingLsh(
{{sim_hash, FlocId(6789, kDummySortingLshVersion)}});
sorting_lsh_service_->ConfigureSortingLsh({{sim_hash, 6789}},
base::Version("999.0"));
task_environment_.FastForwardBy(base::TimeDelta::FromDays(1));
// Expect one more computation. The floc should be equal to 6789.
EXPECT_EQ(3u, floc_id_provider_->compute_floc_completed_count());
EXPECT_EQ(3u, floc_id_provider_->log_event_count());
EXPECT_EQ(FlocId(6789, kDummySortingLshVersion), floc_id());
EXPECT_EQ(FlocId(6789, kTime, kTime, 999), floc_id());
}
} // namespace federated_learning
......@@ -26,8 +26,14 @@ uint64_t FlocId::SimHashHistory(
FlocId::FlocId() = default;
FlocId::FlocId(uint64_t id, uint32_t sorting_lsh_version)
: id_(id), sorting_lsh_version_(sorting_lsh_version) {}
FlocId::FlocId(uint64_t id,
base::Time history_begin_time,
base::Time history_end_time,
uint32_t sorting_lsh_version)
: id_(id),
history_begin_time_(history_begin_time),
history_end_time_(history_end_time),
sorting_lsh_version_(sorting_lsh_version) {}
FlocId::FlocId(const FlocId& id) = default;
......@@ -42,19 +48,16 @@ bool FlocId::IsValid() const {
}
bool FlocId::operator==(const FlocId& other) const {
return id_ == other.id_ && sorting_lsh_version_ == other.sorting_lsh_version_;
return id_ == other.id_ && history_begin_time_ == other.history_begin_time_ &&
history_end_time_ == other.history_end_time_ &&
sorting_lsh_version_ == other.sorting_lsh_version_;
}
bool FlocId::operator!=(const FlocId& other) const {
return !(*this == other);
}
uint64_t FlocId::ToUint64() const {
DCHECK(id_.has_value());
return id_.value();
}
std::string FlocId::ToString() const {
std::string FlocId::ToStringForJsApi() const {
DCHECK(id_.has_value());
return base::StrCat({base::NumberToString(id_.value()), ".",
......
......@@ -6,6 +6,7 @@
#define COMPONENTS_FEDERATED_LEARNING_FLOC_ID_H_
#include "base/optional.h"
#include "base/time/time.h"
#include "base/version.h"
#include <stdint.h>
......@@ -24,9 +25,13 @@ class FlocId {
const std::unordered_set<std::string>& domains);
FlocId();
explicit FlocId(uint64_t id, uint32_t sorting_lsh_version);
FlocId(const FlocId& id);
explicit FlocId(uint64_t id,
base::Time history_begin_time,
base::Time history_end_time,
uint32_t sorting_lsh_version);
FlocId(const FlocId& id);
~FlocId();
FlocId& operator=(const FlocId& id);
FlocId& operator=(FlocId&& id);
......@@ -35,16 +40,24 @@ class FlocId {
bool operator!=(const FlocId& other) const;
bool IsValid() const;
uint64_t ToUint64() const;
// The id, followed by the chrome floc version, followed by the async floc
// component versions (i.e. model and sorting-lsh). This is the format to be
// exposed to the JS API. Precondition: |id_| must be valid.
std::string ToString() const;
std::string ToStringForJsApi() const;
base::Time history_begin_time() const { return history_begin_time_; }
base::Time history_end_time() const { return history_end_time_; }
private:
base::Optional<uint64_t> id_;
// The time range of the actual history used to compute the floc. This should
// always be within the time range of each history query.
base::Time history_begin_time_;
base::Time history_end_time_;
// The main version (i.e. 1st int) of the sorting lsh component version.
uint32_t sorting_lsh_version_ = 0;
};
......
......@@ -8,32 +8,33 @@
namespace federated_learning {
const base::Time kTime0 = base::Time();
const base::Time kTime1 = base::Time::FromTimeT(1);
const base::Time kTime2 = base::Time::FromTimeT(2);
TEST(FlocIdTest, IsValid) {
EXPECT_FALSE(FlocId().IsValid());
EXPECT_TRUE(FlocId(0, 0).IsValid());
EXPECT_TRUE(FlocId(0, 1).IsValid());
}
TEST(FlocIdTest, ToUint64) {
EXPECT_EQ(0u, FlocId(0, 0).ToUint64());
EXPECT_EQ(1u, FlocId(1, 0).ToUint64());
EXPECT_EQ(1u, FlocId(1, 1).ToUint64());
EXPECT_TRUE(FlocId(0, kTime0, kTime0, 0).IsValid());
EXPECT_TRUE(FlocId(0, kTime1, kTime2, 1).IsValid());
}
TEST(FlocIdTest, Comparison) {
EXPECT_EQ(FlocId(), FlocId());
EXPECT_EQ(FlocId(0, 0), FlocId(0, 0));
EXPECT_EQ(FlocId(0, 1), FlocId(0, 1));
EXPECT_NE(FlocId(), FlocId(0, 0));
EXPECT_NE(FlocId(0, 0), FlocId(1, 0));
EXPECT_NE(FlocId(0, 0), FlocId(0, 1));
EXPECT_EQ(FlocId(0, kTime0, kTime0, 0), FlocId(0, kTime0, kTime0, 0));
EXPECT_EQ(FlocId(0, kTime1, kTime1, 1), FlocId(0, kTime1, kTime1, 1));
EXPECT_EQ(FlocId(0, kTime1, kTime2, 1), FlocId(0, kTime1, kTime2, 1));
EXPECT_NE(FlocId(), FlocId(0, kTime0, kTime0, 0));
EXPECT_NE(FlocId(0, kTime0, kTime0, 0), FlocId(1, kTime0, kTime0, 0));
EXPECT_NE(FlocId(0, kTime0, kTime1, 0), FlocId(0, kTime1, kTime1, 0));
EXPECT_NE(FlocId(0, kTime0, kTime0, 0), FlocId(0, kTime0, kTime0, 1));
}
TEST(FlocIdTest, ToString) {
EXPECT_EQ("0.1.0", FlocId(0, 0).ToString());
EXPECT_EQ("12345.1.0", FlocId(12345, 0).ToString());
EXPECT_EQ("12345.1.2", FlocId(12345, 2).ToString());
TEST(FlocIdTest, ToStringForJsApi) {
EXPECT_EQ("0.1.0", FlocId(0, kTime0, kTime0, 0).ToStringForJsApi());
EXPECT_EQ("12345.1.0", FlocId(12345, kTime0, kTime0, 0).ToStringForJsApi());
EXPECT_EQ("12345.1.2", FlocId(12345, kTime1, kTime1, 2).ToStringForJsApi());
}
} // namespace federated_learning
......@@ -36,13 +36,13 @@ class CopyingFileInputStream : public google::protobuf::io::CopyingInputStream {
base::File file_;
};
FlocId ApplySortingLshOnBackgroundThread(uint64_t sim_hash,
const base::FilePath& file_path,
const base::Version& version) {
base::Optional<uint64_t> ApplySortingLshOnBackgroundThread(
uint64_t sim_hash,
const base::FilePath& file_path) {
base::File sorting_lsh_clusters_file(
file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
if (!sorting_lsh_clusters_file.IsValid())
return FlocId();
return base::nullopt;
CopyingFileInputStream copying_stream(std::move(sorting_lsh_clusters_file));
google::protobuf::io::CopyingInputStreamAdaptor zero_copy_stream_adaptor(
......@@ -88,33 +88,33 @@ FlocId ApplySortingLshOnBackgroundThread(uint64_t sim_hash,
for (uint64_t index = 0; input_stream.ReadVarint32(&next_combined); ++index) {
// Sanitizing error: the entry used more than |kSortingLshMaxBits| bits.
if ((next_combined >> kSortingLshMaxBits) > 0)
return FlocId();
return base::nullopt;
bool is_blocked = next_combined & kSortingLshBlockedMask;
uint32_t next = next_combined & kSortingLshSizeMask;
// Sanitizing error
if (next > kMaxNumberOfBitsInFloc)
return FlocId();
return base::nullopt;
cumulative_sum += (1ULL << next);
// Sanitizing error
if (cumulative_sum > kExpectedFinalCumulativeSum)
return FlocId();
return base::nullopt;
// Found the sim-hash upper bound. Use the index as the new floc.
if (cumulative_sum > sim_hash) {
if (is_blocked)
return FlocId();
return base::nullopt;
return FlocId(index, version.components().front());
return index;
}
}
// Sanitizing error: we didn't find a sim-hash upper bound, but we expect to
// always find it after finish iterating through the list.
return FlocId();
return base::nullopt;
}
} // namespace
......@@ -158,9 +158,10 @@ void FlocSortingLshClustersService::ApplySortingLsh(
base::PostTaskAndReplyWithResult(
background_task_runner_.get(), FROM_HERE,
base::BindOnce(&ApplySortingLshOnBackgroundThread, sim_hash,
sorting_lsh_clusters_file_path_,
sorting_lsh_clusters_version_),
std::move(callback));
sorting_lsh_clusters_file_path_),
base::BindOnce(&FlocSortingLshClustersService::DidApplySortingLsh,
weak_ptr_factory_.GetWeakPtr(), std::move(callback),
sorting_lsh_clusters_version_));
}
void FlocSortingLshClustersService::SetBackgroundTaskRunnerForTesting(
......@@ -168,4 +169,11 @@ void FlocSortingLshClustersService::SetBackgroundTaskRunnerForTesting(
background_task_runner_ = background_task_runner;
}
void FlocSortingLshClustersService::DidApplySortingLsh(
ApplySortingLshCallback callback,
base::Version version,
base::Optional<uint64_t> final_hash) {
std::move(callback).Run(std::move(final_hash), std::move(version));
}
} // namespace federated_learning
......@@ -29,7 +29,8 @@ namespace federated_learning {
// File reading and parsing is posted to |background_task_runner_|.
class FlocSortingLshClustersService {
public:
using ApplySortingLshCallback = base::OnceCallback<void(FlocId)>;
using ApplySortingLshCallback =
base::OnceCallback<void(base::Optional<uint64_t>, base::Version)>;
class Observer {
public:
......@@ -63,6 +64,10 @@ class FlocSortingLshClustersService {
private:
friend class FlocSortingLshClustersServiceTest;
void DidApplySortingLsh(ApplySortingLshCallback callback,
base::Version version,
base::Optional<uint64_t> final_hash);
// Runner for tasks that do not influence user experience.
scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
......
......@@ -45,6 +45,11 @@ class CopyingFileOutputStream
base::File file_;
};
struct ApplySortingLshResult {
base::Optional<uint64_t> final_hash;
base::Version version;
};
} // namespace
class FlocSortingLshClustersServiceTest : public ::testing::Test {
......@@ -117,12 +122,14 @@ class FlocSortingLshClustersServiceTest : public ::testing::Test {
return service()->sorting_lsh_clusters_file_path_;
}
FlocId ApplySortingLsh(uint64_t sim_hash) {
FlocId result;
ApplySortingLshResult ApplySortingLsh(uint64_t sim_hash) {
ApplySortingLshResult result;
base::RunLoop run_loop;
auto cb = base::BindLambdaForTesting([&](FlocId floc_id) {
result = floc_id;
auto cb = base::BindLambdaForTesting(
[&](base::Optional<uint64_t> final_hash, base::Version version) {
result.final_hash = final_hash;
result.version = version;
run_loop.Quit();
});
......@@ -149,83 +156,86 @@ TEST_F(FlocSortingLshClustersServiceTest, NoFilePath) {
TEST_F(FlocSortingLshClustersServiceTest, EmptyList) {
InitializeSortingLshClustersFile({}, base::Version("2.3.4"));
EXPECT_EQ(FlocId(), ApplySortingLsh(0));
EXPECT_EQ(FlocId(), ApplySortingLsh(1));
EXPECT_EQ(FlocId(), ApplySortingLsh(kMaxSimHash));
EXPECT_EQ(base::nullopt, ApplySortingLsh(0).final_hash);
EXPECT_EQ(base::nullopt, ApplySortingLsh(1).final_hash);
EXPECT_EQ(base::nullopt, ApplySortingLsh(kMaxSimHash).final_hash);
}
TEST_F(FlocSortingLshClustersServiceTest, List_0) {
InitializeSortingLshClustersFile({{0, false}}, base::Version("2.3.4"));
EXPECT_EQ(FlocId(0, 2), ApplySortingLsh(0));
EXPECT_EQ(FlocId(), ApplySortingLsh(1));
EXPECT_EQ(FlocId(), ApplySortingLsh(kMaxSimHash));
EXPECT_EQ(0u, ApplySortingLsh(0).final_hash.value());
EXPECT_EQ(base::Version("2.3.4"), ApplySortingLsh(0).version);
EXPECT_EQ(base::nullopt, ApplySortingLsh(1).final_hash);
EXPECT_EQ(base::nullopt, ApplySortingLsh(kMaxSimHash).final_hash);
}
TEST_F(FlocSortingLshClustersServiceTest, List_0_Blocked) {
InitializeSortingLshClustersFile({{0, true}}, base::Version("2.3.4"));
EXPECT_EQ(FlocId(), ApplySortingLsh(0));
EXPECT_EQ(FlocId(), ApplySortingLsh(1));
EXPECT_EQ(FlocId(), ApplySortingLsh(kMaxSimHash));
EXPECT_EQ(base::nullopt, ApplySortingLsh(0).final_hash);
EXPECT_EQ(base::nullopt, ApplySortingLsh(1).final_hash);
EXPECT_EQ(base::nullopt, ApplySortingLsh(kMaxSimHash).final_hash);
}
TEST_F(FlocSortingLshClustersServiceTest, List_UnexpectedNumber) {
InitializeSortingLshClustersFile({{1 << 8, false}}, base::Version("2.3.4"));
EXPECT_EQ(FlocId(), ApplySortingLsh(0));
EXPECT_EQ(FlocId(), ApplySortingLsh(1));
EXPECT_EQ(FlocId(), ApplySortingLsh(kMaxSimHash));
EXPECT_EQ(base::nullopt, ApplySortingLsh(0).final_hash);
EXPECT_EQ(base::nullopt, ApplySortingLsh(1).final_hash);
EXPECT_EQ(base::nullopt, ApplySortingLsh(kMaxSimHash).final_hash);
}
TEST_F(FlocSortingLshClustersServiceTest, List_1) {
InitializeSortingLshClustersFile({{1, false}}, base::Version("2.3.4"));
EXPECT_EQ(FlocId(0, 2), ApplySortingLsh(0));
EXPECT_EQ(FlocId(0, 2), ApplySortingLsh(1));
EXPECT_EQ(FlocId(), ApplySortingLsh(2));
EXPECT_EQ(FlocId(), ApplySortingLsh(kMaxSimHash));
EXPECT_EQ(0u, ApplySortingLsh(0).final_hash.value());
EXPECT_EQ(0u, ApplySortingLsh(1).final_hash.value());
EXPECT_EQ(base::nullopt, ApplySortingLsh(2).final_hash);
EXPECT_EQ(base::nullopt, ApplySortingLsh(kMaxSimHash).final_hash);
}
TEST_F(FlocSortingLshClustersServiceTest, List_0_0) {
InitializeSortingLshClustersFile({{0, false}, {0, false}},
base::Version("2.3.4"));
EXPECT_EQ(FlocId(0, 2), ApplySortingLsh(0));
EXPECT_EQ(FlocId(1, 2), ApplySortingLsh(1));
EXPECT_EQ(FlocId(), ApplySortingLsh(2));
EXPECT_EQ(FlocId(), ApplySortingLsh(kMaxSimHash));
EXPECT_EQ(0u, ApplySortingLsh(0).final_hash.value());
EXPECT_EQ(1u, ApplySortingLsh(1).final_hash.value());
EXPECT_EQ(base::nullopt, ApplySortingLsh(2).final_hash);
EXPECT_EQ(base::nullopt, ApplySortingLsh(kMaxSimHash).final_hash);
}
TEST_F(FlocSortingLshClustersServiceTest, List_0_1) {
InitializeSortingLshClustersFile({{0, false}, {1, false}},
base::Version("2.3.4"));
EXPECT_EQ(FlocId(0, 2), ApplySortingLsh(0));
EXPECT_EQ(FlocId(1, 2), ApplySortingLsh(1));
EXPECT_EQ(FlocId(1, 2), ApplySortingLsh(2));
EXPECT_EQ(FlocId(), ApplySortingLsh(3));
EXPECT_EQ(FlocId(), ApplySortingLsh(kMaxSimHash));
EXPECT_EQ(0u, ApplySortingLsh(0).final_hash.value());
EXPECT_EQ(1u, ApplySortingLsh(1).final_hash.value());
EXPECT_EQ(1u, ApplySortingLsh(2).final_hash.value());
EXPECT_EQ(base::nullopt, ApplySortingLsh(3).final_hash);
EXPECT_EQ(base::nullopt, ApplySortingLsh(kMaxSimHash).final_hash);
}
TEST_F(FlocSortingLshClustersServiceTest, List_1_0) {
InitializeSortingLshClustersFile({{1, false}, {0, false}},
base::Version("2.3.4"));
EXPECT_EQ(FlocId(0, 2), ApplySortingLsh(0));
EXPECT_EQ(FlocId(0, 2), ApplySortingLsh(1));
EXPECT_EQ(FlocId(1, 2), ApplySortingLsh(2));
EXPECT_EQ(FlocId(), ApplySortingLsh(3));
EXPECT_EQ(FlocId(), ApplySortingLsh(kMaxSimHash));
EXPECT_EQ(0u, ApplySortingLsh(0).final_hash.value());
EXPECT_EQ(0u, ApplySortingLsh(1).final_hash.value());
EXPECT_EQ(1u, ApplySortingLsh(2).final_hash.value());
EXPECT_EQ(base::nullopt, ApplySortingLsh(3).final_hash);
EXPECT_EQ(base::nullopt, ApplySortingLsh(kMaxSimHash).final_hash);
}
TEST_F(FlocSortingLshClustersServiceTest, List_SingleCluster) {
InitializeSortingLshClustersFile({{kMaxNumberOfBitsInFloc, false}},
base::Version("2.3.4"));
EXPECT_EQ(FlocId(0, 2), ApplySortingLsh(0));
EXPECT_EQ(FlocId(0, 2), ApplySortingLsh(1));
EXPECT_EQ(FlocId(0, 2), ApplySortingLsh(12345));
EXPECT_EQ(FlocId(0, 2), ApplySortingLsh(kMaxSimHash));
EXPECT_EQ(0u, ApplySortingLsh(0).final_hash.value());
EXPECT_EQ(0u, ApplySortingLsh(1).final_hash.value());
EXPECT_EQ(0u, ApplySortingLsh(12345).final_hash.value());
EXPECT_EQ(0u, ApplySortingLsh(kMaxSimHash).final_hash.value());
}
TEST_F(FlocSortingLshClustersServiceTest, List_TwoClustersEqualSize) {
......@@ -234,12 +244,12 @@ TEST_F(FlocSortingLshClustersServiceTest, List_TwoClustersEqualSize) {
base::Version("2.3.4"));
uint64_t middle_value = (1ULL << (kMaxNumberOfBitsInFloc - 1));
EXPECT_EQ(FlocId(0, 2), ApplySortingLsh(0));
EXPECT_EQ(FlocId(0, 2), ApplySortingLsh(1));
EXPECT_EQ(FlocId(0, 2), ApplySortingLsh(middle_value - 1));
EXPECT_EQ(FlocId(1, 2), ApplySortingLsh(middle_value));
EXPECT_EQ(FlocId(1, 2), ApplySortingLsh(middle_value + 1));
EXPECT_EQ(FlocId(1, 2), ApplySortingLsh(kMaxSimHash));
EXPECT_EQ(0u, ApplySortingLsh(0).final_hash.value());
EXPECT_EQ(0u, ApplySortingLsh(1).final_hash.value());
EXPECT_EQ(0u, ApplySortingLsh(middle_value - 1).final_hash.value());
EXPECT_EQ(1u, ApplySortingLsh(middle_value).final_hash.value());
EXPECT_EQ(1u, ApplySortingLsh(middle_value + 1).final_hash.value());
EXPECT_EQ(1u, ApplySortingLsh(kMaxSimHash).final_hash.value());
}
TEST_F(FlocSortingLshClustersServiceTest,
......@@ -248,9 +258,11 @@ TEST_F(FlocSortingLshClustersServiceTest,
InitializeSortingLshClustersFile({{0, false}}, base::Version("2.3.4"));
base::RunLoop run_loop;
auto cb = base::BindLambdaForTesting([&](FlocId floc_id) {
// Since the file has been deleted, expect an invalid floc id.
EXPECT_EQ(FlocId(), floc_id);
auto cb = base::BindLambdaForTesting(
[&](base::Optional<uint64_t> final_hash, base::Version version) {
// Since the file has been deleted, expect an invalid final_hash.
EXPECT_EQ(base::nullopt, final_hash);
EXPECT_EQ(base::Version("2.3.4"), version);
run_loop.Quit();
});
......@@ -264,7 +276,9 @@ TEST_F(FlocSortingLshClustersServiceTest,
TEST_F(FlocSortingLshClustersServiceTest, MultipleUpdate_LatestOneUsed) {
InitializeSortingLshClustersFile({}, base::Version("2.3.4"));
InitializeSortingLshClustersFile({{0, false}}, base::Version("6.7.8.9"));
EXPECT_EQ(FlocId(0, 6), ApplySortingLsh(0));
EXPECT_EQ(0u, ApplySortingLsh(0).final_hash.value());
EXPECT_EQ(base::Version("6.7.8.9"), ApplySortingLsh(0).version);
}
} // namespace federated_learning
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment