blob: 4252c9065fec444820e02fa4239ccaaf006ca459 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/federated_learning/floc_id_provider_impl.h"
#include "base/files/scoped_temp_dir.h"
#include "base/strings/strcat.h"
#include "base/test/bind_test_util.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/federated_learning/floc_remote_permission_service.h"
#include "chrome/common/chrome_features.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "components/content_settings/core/browser/cookie_settings.h"
#include "components/content_settings/core/common/pref_names.h"
#include "components/history/core/browser/history_database_params.h"
#include "components/history/core/browser/history_service.h"
#include "components/history/core/test/test_history_database.h"
#include "components/sync/driver/test_sync_service.h"
#include "components/sync_user_events/fake_user_event_service.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace federated_learning {
namespace {
class FakeFlocRemotePermissionService : public FlocRemotePermissionService {
public:
using FlocRemotePermissionService::FlocRemotePermissionService;
void QueryFlocPermission(QueryFlocPermissionCallback callback,
const net::PartialNetworkTrafficAnnotationTag&
partial_traffic_annotation) override {
std::move(callback).Run(swaa_nac_account_enabled_);
}
void set_swaa_nac_account_enabled(bool enabled) {
swaa_nac_account_enabled_ = enabled;
}
private:
bool swaa_nac_account_enabled_ = true;
};
class MockFlocIdProvider : public FlocIdProviderImpl {
public:
using FlocIdProviderImpl::FlocIdProviderImpl;
void NotifyFlocUpdated(
FlocIdProviderImpl::ComputeFlocTrigger trigger) override {
++floc_update_notification_count_;
FlocIdProviderImpl::NotifyFlocUpdated(trigger);
}
bool AreThirdPartyCookiesAllowed() override {
return third_party_cookies_allowed_;
}
size_t floc_update_notification_count() const {
return floc_update_notification_count_;
}
void set_third_party_cookies_allowed(bool allowed) {
third_party_cookies_allowed_ = allowed;
}
private:
size_t floc_update_notification_count_ = 0u;
bool third_party_cookies_allowed_ = true;
};
} // namespace
class FlocIdProviderUnitTest : public testing::Test {
public:
FlocIdProviderUnitTest()
: task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
~FlocIdProviderUnitTest() override = default;
void SetUp() override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
TestingBrowserProcess::GetGlobal()->SetFlocBlocklistService(
std::make_unique<FlocBlocklistService>());
history_service_ = std::make_unique<history::HistoryService>();
history_service_->Init(
history::TestHistoryDatabaseParamsForPath(temp_dir_.GetPath()));
test_sync_service_ = std::make_unique<syncer::TestSyncService>();
test_sync_service_->SetTransportState(
syncer::SyncService::TransportState::DISABLED);
fake_user_event_service_ = std::make_unique<syncer::FakeUserEventService>();
fake_floc_remote_permission_service_ =
std::make_unique<FakeFlocRemotePermissionService>(
/*url_loader_factory=*/nullptr);
floc_id_provider_ = std::make_unique<MockFlocIdProvider>(
test_sync_service_.get(), /*cookie_settings=*/nullptr,
fake_floc_remote_permission_service_.get(), history_service_.get(),
fake_user_event_service_.get());
task_environment_.RunUntilIdle();
}
void TearDown() override {
history_service_->RemoveObserver(floc_id_provider_.get());
}
void CheckCanComputeFloc(
FlocIdProviderImpl::CanComputeFlocCallback callback) {
floc_id_provider_->CheckCanComputeFloc(std::move(callback));
}
void IsSwaaNacAccountEnabled(
FlocIdProviderImpl::CanComputeFlocCallback callback) {
floc_id_provider_->IsSwaaNacAccountEnabled(std::move(callback));
}
void OnURLsDeleted(history::HistoryService* history_service,
const history::DeletionInfo& deletion_info) {
floc_id_provider_->OnURLsDeleted(history_service, deletion_info);
}
void OnGetRecentlyVisitedURLsCompleted(
FlocIdProviderImpl::ComputeFlocTrigger trigger,
history::QueryResults results) {
auto compute_floc_completed_callback =
base::BindOnce(&FlocIdProviderImpl::OnComputeFlocCompleted,
base::Unretained(floc_id_provider_.get()), trigger);
floc_id_provider_->OnGetRecentlyVisitedURLsCompleted(
std::move(compute_floc_completed_callback), std::move(results));
}
void ExpireHistoryBefore(base::Time end_time) {
base::RunLoop run_loop;
base::CancelableTaskTracker tracker;
history_service_->ExpireHistoryBeforeForTesting(
end_time, base::BindLambdaForTesting([&]() { run_loop.Quit(); }),
&tracker);
run_loop.Run();
}
FlocId floc_id() const { return floc_id_provider_->floc_id_; }
void set_floc_id(const FlocId& floc_id) const {
floc_id_provider_->floc_id_ = floc_id;
}
bool floc_computation_in_progress() const {
return floc_id_provider_->floc_computation_in_progress_;
}
void set_floc_computation_in_progress(bool floc_computation_in_progress) {
floc_id_provider_->floc_computation_in_progress_ =
floc_computation_in_progress;
}
bool first_floc_computation_triggered() const {
return floc_id_provider_->first_floc_computation_triggered_;
}
void set_first_floc_computation_triggered(bool triggered) {
floc_id_provider_->first_floc_computation_triggered_ = triggered;
}
void SetRemoteSwaaNacAccountEnabled(bool enabled) {
fake_floc_remote_permission_service_->set_swaa_nac_account_enabled(enabled);
}
void OnBlocklistLoaded(const std::unordered_set<uint64_t>& blocklist) {
g_browser_process->floc_blocklist_service()->OnBlocklistLoadResult(
blocklist);
}
protected:
content::BrowserTaskEnvironment task_environment_;
std::unique_ptr<history::HistoryService> history_service_;
std::unique_ptr<syncer::TestSyncService> test_sync_service_;
std::unique_ptr<syncer::FakeUserEventService> fake_user_event_service_;
std::unique_ptr<FakeFlocRemotePermissionService>
fake_floc_remote_permission_service_;
std::unique_ptr<MockFlocIdProvider> floc_id_provider_;
base::ScopedTempDir temp_dir_;
DISALLOW_COPY_AND_ASSIGN(FlocIdProviderUnitTest);
};
TEST_F(FlocIdProviderUnitTest, QualifiedInitialHistory) {
// Add a history entry with a timestamp exactly 7 days back from now.
std::string domain = "foo.com";
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.publicly_routable = true;
history_service_->AddPage(add_page_args);
task_environment_.RunUntilIdle();
// Expect that the floc computation hasn't started, as the floc_id_provider
// hasn't been notified about state of the sync_service.
ASSERT_EQ(0u, floc_id_provider_->floc_update_notification_count());
ASSERT_FALSE(floc_id().IsValid());
ASSERT_FALSE(first_floc_computation_triggered());
// Trigger the 1st floc computation.
test_sync_service_->SetTransportState(
syncer::SyncService::TransportState::ACTIVE);
test_sync_service_->FireStateChanged();
task_environment_.RunUntilIdle();
// Expect a floc id update notification.
ASSERT_EQ(1u, floc_id_provider_->floc_update_notification_count());
ASSERT_TRUE(floc_id().IsValid());
ASSERT_EQ(FlocId::CreateFromHistory({domain}).ToDebugHeaderValue(),
floc_id().ToDebugHeaderValue());
ASSERT_TRUE(first_floc_computation_triggered());
// Advance the clock by 1 day. Expect a floc id update notification, as
// there's no history in the last 7 days so the id has been reset to empty.
task_environment_.FastForwardBy(base::TimeDelta::FromDays(1));
ASSERT_EQ(2u, floc_id_provider_->floc_update_notification_count());
ASSERT_FALSE(floc_id().IsValid());
}
TEST_F(FlocIdProviderUnitTest, UnqualifiedInitialHistory) {
std::string domain = "foo.com";
// Add a history entry with a timestamp 8 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(8);
add_page_args.publicly_routable = true;
history_service_->AddPage(add_page_args);
task_environment_.RunUntilIdle();
// Expect that the floc computation hasn't started, as the floc_id_provider
// hasn't been notified about state of the sync_service.
ASSERT_EQ(0u, floc_id_provider_->floc_update_notification_count());
ASSERT_FALSE(floc_id().IsValid());
ASSERT_FALSE(first_floc_computation_triggered());
// Trigger the 1st floc computation.
test_sync_service_->SetTransportState(
syncer::SyncService::TransportState::ACTIVE);
test_sync_service_->FireStateChanged();
task_environment_.RunUntilIdle();
// Expect no floc id update notification, as there is no qualified history
// entry. However, the 1st computation should already have started.
ASSERT_EQ(0u, floc_id_provider_->floc_update_notification_count());
ASSERT_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);
history_service_->AddPage(add_page_args);
// Advance the clock by 23 hours. Expect no floc id update notification,
// as the id refresh interval is 24 hours.
task_environment_.FastForwardBy(base::TimeDelta::FromHours(23));
ASSERT_EQ(0u, floc_id_provider_->floc_update_notification_count());
// Advance the clock by 1 hour. Expect a floc id update notification, as the
// refresh time is reached and there's a valid history entry in the last 7
// days.
task_environment_.FastForwardBy(base::TimeDelta::FromHours(1));
ASSERT_EQ(1u, floc_id_provider_->floc_update_notification_count());
ASSERT_TRUE(floc_id().IsValid());
ASSERT_EQ(FlocId::CreateFromHistory({domain}).ToDebugHeaderValue(),
floc_id().ToDebugHeaderValue());
}
TEST_F(FlocIdProviderUnitTest, HistoryDeleteAndScheduledUpdate) {
std::string domain1 = "foo.com";
std::string domain2 = "bar.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);
// 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);
history_service_->AddPage(add_page_args);
task_environment_.RunUntilIdle();
// Trigger the 1st floc computation.
test_sync_service_->SetTransportState(
syncer::SyncService::TransportState::ACTIVE);
test_sync_service_->FireStateChanged();
task_environment_.RunUntilIdle();
// Expect a floc id update notification.
ASSERT_EQ(1u, floc_id_provider_->floc_update_notification_count());
ASSERT_TRUE(floc_id().IsValid());
ASSERT_EQ(FlocId::CreateFromHistory({domain1, domain2}).ToDebugHeaderValue(),
floc_id().ToDebugHeaderValue());
// Advance the clock by 12 hours. Expect no floc id update notification.
task_environment_.FastForwardBy(base::TimeDelta::FromHours(12));
ASSERT_EQ(1u, floc_id_provider_->floc_update_notification_count());
// Expire the oldest history entry.
ExpireHistoryBefore(base::Time::Now() - base::TimeDelta::FromDays(7));
task_environment_.RunUntilIdle();
// Expect a floc id update notification as it was just recomputed due to the
// history deletion.
ASSERT_EQ(2u, floc_id_provider_->floc_update_notification_count());
ASSERT_TRUE(floc_id().IsValid());
ASSERT_EQ(FlocId::CreateFromHistory({domain2}).ToDebugHeaderValue(),
floc_id().ToDebugHeaderValue());
// Advance the clock by 23 hours. Expect no floc id update notification as the
// timer has been reset due to the recomputation from history deletion.
task_environment_.FastForwardBy(base::TimeDelta::FromHours(23));
ASSERT_EQ(2u, floc_id_provider_->floc_update_notification_count());
// Advance the clock by 1 hour. Expect an floc id update notification 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));
ASSERT_EQ(3u, floc_id_provider_->floc_update_notification_count());
ASSERT_FALSE(floc_id().IsValid());
}
TEST_F(FlocIdProviderUnitTest, ScheduledUpdateSameFloc_NoNotification) {
std::string domain = "foo.com";
// 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.publicly_routable = true;
history_service_->AddPage(add_page_args);
task_environment_.RunUntilIdle();
// Trigger the 1st floc computation.
test_sync_service_->SetTransportState(
syncer::SyncService::TransportState::ACTIVE);
test_sync_service_->FireStateChanged();
task_environment_.RunUntilIdle();
// Expect a floc id update notification.
ASSERT_EQ(1u, floc_id_provider_->floc_update_notification_count());
// Advance the clock by 1 day. Expect no additional floc id update
// notification, as the floc didn't change.
task_environment_.FastForwardBy(base::TimeDelta::FromDays(1));
ASSERT_EQ(1u, floc_id_provider_->floc_update_notification_count());
}
TEST_F(FlocIdProviderUnitTest, CheckCanComputeFloc_Success) {
test_sync_service_->SetTransportState(
syncer::SyncService::TransportState::ACTIVE);
base::OnceCallback<void(bool)> cb = base::BindOnce(
[](bool can_compute_floc) { ASSERT_TRUE(can_compute_floc); });
CheckCanComputeFloc(std::move(cb));
task_environment_.RunUntilIdle();
}
TEST_F(FlocIdProviderUnitTest, CheckCanComputeFloc_Failure_SyncDisabled) {
base::OnceCallback<void(bool)> cb = base::BindOnce(
[](bool can_compute_floc) { ASSERT_FALSE(can_compute_floc); });
CheckCanComputeFloc(std::move(cb));
task_environment_.RunUntilIdle();
}
TEST_F(FlocIdProviderUnitTest,
CheckCanComputeFloc_Failure_BlockThirdPartyCookies) {
test_sync_service_->SetTransportState(
syncer::SyncService::TransportState::ACTIVE);
floc_id_provider_->set_third_party_cookies_allowed(false);
base::OnceCallback<void(bool)> cb = base::BindOnce(
[](bool can_compute_floc) { ASSERT_FALSE(can_compute_floc); });
CheckCanComputeFloc(std::move(cb));
task_environment_.RunUntilIdle();
}
TEST_F(FlocIdProviderUnitTest,
CheckCanComputeFloc_Failure_SwaaNacAccountDisabled) {
test_sync_service_->SetTransportState(
syncer::SyncService::TransportState::ACTIVE);
SetRemoteSwaaNacAccountEnabled(false);
base::OnceCallback<void(bool)> cb = base::BindOnce(
[](bool can_compute_floc) { ASSERT_FALSE(can_compute_floc); });
CheckCanComputeFloc(std::move(cb));
task_environment_.RunUntilIdle();
}
TEST_F(FlocIdProviderUnitTest, SwaaNacAccountEnabledUseCacheStatus) {
base::OnceCallback<void(bool)> assert_enabled_callback_1 = base::BindOnce(
[](bool can_compute_floc) { ASSERT_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) { ASSERT_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) { ASSERT_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) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(features::kFlocIdComputedEventLogging);
set_floc_id(FlocId(12345ULL));
floc_id_provider_->NotifyFlocUpdated(
FlocIdProviderImpl::ComputeFlocTrigger::kBrowserStart);
ASSERT_EQ(1u, fake_user_event_service_->GetRecordedUserEvents().size());
const sync_pb::UserEventSpecifics& specifics1 =
fake_user_event_service_->GetRecordedUserEvents()[0];
EXPECT_EQ(specifics1.event_time_usec(),
base::Time::Now().ToDeltaSinceWindowsEpoch().InMicroseconds());
EXPECT_EQ(sync_pb::UserEventSpecifics::kFlocIdComputedEvent,
specifics1.event_case());
const sync_pb::UserEventSpecifics_FlocIdComputed& event1 =
specifics1.floc_id_computed_event();
EXPECT_EQ(sync_pb::UserEventSpecifics::FlocIdComputed::NEW,
event1.event_trigger());
EXPECT_EQ(12345ULL, event1.floc_id());
task_environment_.FastForwardBy(base::TimeDelta::FromDays(3));
set_floc_id(FlocId(999ULL));
floc_id_provider_->NotifyFlocUpdated(
FlocIdProviderImpl::ComputeFlocTrigger::kScheduledUpdate);
ASSERT_EQ(2u, fake_user_event_service_->GetRecordedUserEvents().size());
const sync_pb::UserEventSpecifics& specifics2 =
fake_user_event_service_->GetRecordedUserEvents()[1];
EXPECT_EQ(specifics2.event_time_usec(),
base::Time::Now().ToDeltaSinceWindowsEpoch().InMicroseconds());
EXPECT_EQ(sync_pb::UserEventSpecifics::kFlocIdComputedEvent,
specifics2.event_case());
const sync_pb::UserEventSpecifics_FlocIdComputed& event2 =
specifics2.floc_id_computed_event();
EXPECT_EQ(sync_pb::UserEventSpecifics::FlocIdComputed::REFRESHED,
event2.event_trigger());
EXPECT_EQ(999ULL, event2.floc_id());
set_floc_id(FlocId());
floc_id_provider_->NotifyFlocUpdated(
FlocIdProviderImpl::ComputeFlocTrigger::kScheduledUpdate);
ASSERT_EQ(3u, fake_user_event_service_->GetRecordedUserEvents().size());
const sync_pb::UserEventSpecifics& specifics3 =
fake_user_event_service_->GetRecordedUserEvents()[2];
EXPECT_EQ(specifics3.event_time_usec(),
base::Time::Now().ToDeltaSinceWindowsEpoch().InMicroseconds());
EXPECT_EQ(sync_pb::UserEventSpecifics::kFlocIdComputedEvent,
specifics3.event_case());
const sync_pb::UserEventSpecifics_FlocIdComputed& event3 =
specifics3.floc_id_computed_event();
EXPECT_EQ(sync_pb::UserEventSpecifics::FlocIdComputed::REFRESHED,
event3.event_trigger());
EXPECT_FALSE(event3.has_floc_id());
set_floc_id(FlocId(555));
floc_id_provider_->NotifyFlocUpdated(
FlocIdProviderImpl::ComputeFlocTrigger::kHistoryDelete);
ASSERT_EQ(4u, fake_user_event_service_->GetRecordedUserEvents().size());
const sync_pb::UserEventSpecifics& specifics4 =
fake_user_event_service_->GetRecordedUserEvents()[3];
EXPECT_EQ(specifics4.event_time_usec(),
base::Time::Now().ToDeltaSinceWindowsEpoch().InMicroseconds());
EXPECT_EQ(sync_pb::UserEventSpecifics::kFlocIdComputedEvent,
specifics4.event_case());
const sync_pb::UserEventSpecifics_FlocIdComputed& event4 =
specifics4.floc_id_computed_event();
EXPECT_EQ(sync_pb::UserEventSpecifics::FlocIdComputed::HISTORY_DELETE,
event4.event_trigger());
EXPECT_EQ(555ULL, event4.floc_id());
}
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});
set_first_floc_computation_triggered(true);
set_floc_computation_in_progress(true);
OnGetRecentlyVisitedURLsCompleted(
FlocIdProviderImpl::ComputeFlocTrigger::kBrowserStart,
std::move(query_results));
ASSERT_TRUE(floc_id().IsValid());
OnURLsDeleted(history_service_.get(), history::DeletionInfo::ForAllHistory());
ASSERT_FALSE(floc_id().IsValid());
}
TEST_F(FlocIdProviderUnitTest, HistoryDelete_InvalidTimeRange) {
base::Time time = base::Time::Now() - base::TimeDelta::FromDays(9);
GURL url_a = GURL("https://a.test");
history::URLResult url_result(url_a, time);
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);
OnGetRecentlyVisitedURLsCompleted(
FlocIdProviderImpl::ComputeFlocTrigger::kBrowserStart,
std::move(query_results));
ASSERT_TRUE(floc_id().IsValid());
OnURLsDeleted(history_service_.get(),
history::DeletionInfo::ForUrls(
{history::URLResult(url_a, base::Time())}, {}));
task_environment_.RunUntilIdle();
ASSERT_FALSE(floc_id().IsValid());
}
TEST_F(FlocIdProviderUnitTest, HistoryDelete_TimeRange) {
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});
set_first_floc_computation_triggered(true);
set_floc_computation_in_progress(true);
OnGetRecentlyVisitedURLsCompleted(
FlocIdProviderImpl::ComputeFlocTrigger::kBrowserStart,
std::move(query_results));
ASSERT_TRUE(floc_id().IsValid());
history::DeletionInfo deletion_info(history::DeletionTimeRange(time, time),
false, {}, {},
base::Optional<std::set<GURL>>());
OnURLsDeleted(history_service_.get(), deletion_info);
task_environment_.RunUntilIdle();
ASSERT_FALSE(floc_id().IsValid());
}
TEST_F(FlocIdProviderUnitTest, HistoryEntriesWithPrivateIP) {
history::QueryResults query_results;
query_results.SetURLResults(
{history::URLResult(GURL("https://a.test"),
base::Time::Now() - base::TimeDelta::FromDays(1))});
set_first_floc_computation_triggered(true);
set_floc_computation_in_progress(true);
OnGetRecentlyVisitedURLsCompleted(
FlocIdProviderImpl::ComputeFlocTrigger::kBrowserStart,
std::move(query_results));
ASSERT_FALSE(floc_id().IsValid());
}
TEST_F(FlocIdProviderUnitTest, MultipleHistoryEntries) {
base::Time time = base::Time::Now() - base::TimeDelta::FromDays(1);
history::URLResult url_result_a(GURL("https://a.test"), time);
url_result_a.set_publicly_routable(true);
history::URLResult url_result_b(GURL("https://b.test"), time);
url_result_b.set_publicly_routable(true);
history::URLResult url_result_c(GURL("https://c.test"), time);
std::vector<history::URLResult> url_results{url_result_a, url_result_b,
url_result_c};
history::QueryResults query_results;
query_results.SetURLResults(std::move(url_results));
set_first_floc_computation_triggered(true);
set_floc_computation_in_progress(true);
OnGetRecentlyVisitedURLsCompleted(
FlocIdProviderImpl::ComputeFlocTrigger::kBrowserStart,
std::move(query_results));
ASSERT_EQ(
FlocId::CreateFromHistory({"a.test", "b.test"}).ToDebugHeaderValue(),
floc_id().ToDebugHeaderValue());
}
TEST_F(FlocIdProviderUnitTest,
BlocklistFilteringEnabled_SyncHistoryEnabledFollowedByBlocklistLoaded) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(features::kFlocIdBlocklistFiltering);
// Turn on sync & sync-history. The 1st floc computation should not be
// triggered as the blocklist hasn't been loaded yet.
test_sync_service_->SetTransportState(
syncer::SyncService::TransportState::ACTIVE);
test_sync_service_->FireStateChanged();
EXPECT_FALSE(first_floc_computation_triggered());
// Load the blocklist. The 1st floc computation should be triggered now as
// sync & sync-history are enabled the blocklist is loaded.
OnBlocklistLoaded({});
EXPECT_TRUE(first_floc_computation_triggered());
}
TEST_F(FlocIdProviderUnitTest,
BlocklistFilteringEnabled_BlocklistLoadedFollowedBySyncHistoryEnabled) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(features::kFlocIdBlocklistFiltering);
// Load the blocklist. The 1st floc computation should not be
// triggered as sync & sync-history are not enabled yet.
OnBlocklistLoaded({});
EXPECT_FALSE(first_floc_computation_triggered());
// Turn on sync & sync-history. The 1st floc computation should be triggered
// now as sync & sync-history are enabled the blocklist is loaded.
test_sync_service_->SetTransportState(
syncer::SyncService::TransportState::ACTIVE);
test_sync_service_->FireStateChanged();
EXPECT_TRUE(first_floc_computation_triggered());
}
TEST_F(FlocIdProviderUnitTest, BlocklistFilteringEnabled_BlockedFloc) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(features::kFlocIdBlocklistFiltering);
std::string domain = "foo.com";
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.publicly_routable = true;
history_service_->AddPage(add_page_args);
task_environment_.RunUntilIdle();
// Load the blocklist and turn on sync & sync-history to trigger the 1st floc
// computation.
std::unordered_set<uint64_t> blocklist;
OnBlocklistLoaded(blocklist);
test_sync_service_->SetTransportState(
syncer::SyncService::TransportState::ACTIVE);
test_sync_service_->FireStateChanged();
EXPECT_TRUE(first_floc_computation_triggered());
task_environment_.RunUntilIdle();
// Expect a floc id update notification. The floc should be equal to the
// sim-hash of the history.
ASSERT_EQ(1u, floc_id_provider_->floc_update_notification_count());
ASSERT_EQ(FlocId::CreateFromHistory({domain}).ToDebugHeaderValue(),
floc_id().ToDebugHeaderValue());
// Insert the current floc to blocklist and reload it.
blocklist.insert(FlocId::CreateFromHistory({domain}).ToUint64());
OnBlocklistLoaded(blocklist);
task_environment_.FastForwardBy(base::TimeDelta::FromDays(1));
// Expect a floc id update notification, with an invalid floc because was
// blocked.
ASSERT_EQ(2u, floc_id_provider_->floc_update_notification_count());
ASSERT_EQ(FlocId().ToDebugHeaderValue(), floc_id().ToDebugHeaderValue());
// Reset and reload the blocklist.
blocklist.clear();
OnBlocklistLoaded(blocklist);
task_environment_.FastForwardBy(base::TimeDelta::FromDays(1));
// Expect a floc id update notification. The floc should be equal to the
// sim-hash of the history.
ASSERT_EQ(3u, floc_id_provider_->floc_update_notification_count());
ASSERT_EQ(FlocId::CreateFromHistory({domain}).ToDebugHeaderValue(),
floc_id().ToDebugHeaderValue());
}
TEST_F(FlocIdProviderUnitTest, TurnSyncOffAndOn) {
std::string domain = "foo.com";
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.publicly_routable = true;
history_service_->AddPage(add_page_args);
task_environment_.RunUntilIdle();
// Trigger the 1st floc computation.
test_sync_service_->SetTransportState(
syncer::SyncService::TransportState::ACTIVE);
test_sync_service_->FireStateChanged();
task_environment_.RunUntilIdle();
// Expect a floc id update notification.
ASSERT_EQ(1u, floc_id_provider_->floc_update_notification_count());
ASSERT_EQ(FlocId::CreateFromHistory({domain}).ToDebugHeaderValue(),
floc_id().ToDebugHeaderValue());
// Turn off sync.
test_sync_service_->SetTransportState(
syncer::SyncService::TransportState::DISABLED);
// Advance the clock by 1 day. Expect a floc id update notification, as
// the sync was turned off so the id has been reset to empty.
task_environment_.FastForwardBy(base::TimeDelta::FromDays(1));
ASSERT_EQ(2u, floc_id_provider_->floc_update_notification_count());
ASSERT_FALSE(floc_id().IsValid());
// Turn on sync.
test_sync_service_->SetTransportState(
syncer::SyncService::TransportState::ACTIVE);
// Advance the clock by 1 day. Expect a floc id update notification and a
// valid floc id.
task_environment_.FastForwardBy(base::TimeDelta::FromDays(1));
ASSERT_EQ(3u, floc_id_provider_->floc_update_notification_count());
ASSERT_EQ(FlocId::CreateFromHistory({domain}).ToDebugHeaderValue(),
floc_id().ToDebugHeaderValue());
}
} // namespace federated_learning