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

Switch to new blocklist format. Avoid keeping it in memory all the time.

Switched to do ReadVarint64 from CodedInputStream. Each blocklist check
would involve a file scan on the background.

Bug: 1062736
Change-Id: I6b0153e8a8e39025a4bf9485a837897767b3abde
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2440305
Commit-Queue: Yao Xiao <yaoxia@chromium.org>
Reviewed-by: default avatarJosh Karlin <jkarlin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#812875}
parent fa57522d
......@@ -7,6 +7,7 @@
#include "base/strings/strcat.h"
#include "base/test/bind_test_util.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_content_browser_client.h"
#include "chrome/browser/content_settings/cookie_settings_factory.h"
......@@ -36,9 +37,31 @@
#include "net/test/embedded_test_server/http_response.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/protobuf/src/google/protobuf/io/coded_stream.h"
#include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.h"
namespace federated_learning {
class CopyingFileOutputStream
: public google::protobuf::io::CopyingOutputStream {
public:
explicit CopyingFileOutputStream(base::File file) : file_(std::move(file)) {}
CopyingFileOutputStream(const CopyingFileOutputStream&) = delete;
CopyingFileOutputStream& operator=(const CopyingFileOutputStream&) = delete;
~CopyingFileOutputStream() override = default;
// google::protobuf::io::CopyingOutputStream:
bool Write(const void* buffer, int size) override {
return file_.WriteAtCurrentPos(static_cast<const char*>(buffer), size) ==
size;
}
private:
base::File file_;
};
class FlocIdProviderBrowserTest : public InProcessBrowserTest {
public:
void SetUpOnMainThread() override {
......@@ -214,6 +237,16 @@ class FlocIdProviderWithCustomizedServicesBrowserTest
run_loop.Run();
}
void FinishOutstandingBlocklistQueries() {
base::RunLoop run_loop;
FlocId dummy_unfiltered_floc = FlocId(0u);
g_browser_process->floc_blocklist_service()->FilterByBlocklist(
dummy_unfiltered_floc,
base::BindLambdaForTesting(
[&](FlocId filtered_floc) { run_loop.Quit(); }));
run_loop.Run();
}
void ExpireHistoryBefore(base::Time end_time) {
base::RunLoop run_loop;
base::CancelableTaskTracker tracker;
......@@ -225,17 +258,53 @@ class FlocIdProviderWithCustomizedServicesBrowserTest
run_loop.Run();
}
// Turn on sync-history and load the blocklist. Finish outstanding remote
// permission queries and history queries.
void InitializeBlocklist(const std::unordered_set<uint64_t>& blocklist) {
sync_service()->SetActiveDataTypes(syncer::ModelTypeSet::All());
sync_service()->FireStateChanged();
base::FilePath GetUniqueTemporaryPath() {
CHECK(scoped_temp_dir_.IsValid() || scoped_temp_dir_.CreateUniqueTempDir());
return scoped_temp_dir_.GetPath().AppendASCII(
base::NumberToString(next_unique_file_suffix_++));
}
base::FilePath CreateBlocklistFile(
const std::vector<uint64_t>& blocklist_entries) {
base::ScopedAllowBlockingForTesting allow_blocking;
base::FilePath file_path = GetUniqueTemporaryPath();
base::File file(file_path, base::File::FLAG_CREATE | base::File::FLAG_READ |
base::File::FLAG_WRITE);
CHECK(file.IsValid());
CopyingFileOutputStream copying_stream(std::move(file));
google::protobuf::io::CopyingOutputStreamAdaptor zero_copy_stream_adaptor(
&copying_stream);
g_browser_process->floc_blocklist_service()->OnBlocklistLoadResult(
blocklist);
google::protobuf::io::CodedOutputStream output_stream(
&zero_copy_stream_adaptor);
for (uint64_t next : blocklist_entries)
output_stream.WriteVarint64(next);
CHECK(!output_stream.HadError());
return file_path;
}
// Finish outstanding async queries for a full floc compute cycle to finish.
void FinishOutstandingAsyncQueries() {
FinishOutstandingRemotePermissionQueries();
FinishOutstandingHistoryQueries();
FinishOutstandingBlocklistQueries();
}
// Turn on sync-history, set up the blocklist file, and trigger the blocklist
// file-ready event.
void InitializeBlocklist(const std::vector<uint64_t>& blocklist_entries) {
sync_service()->SetActiveDataTypes(syncer::ModelTypeSet::All());
sync_service()->FireStateChanged();
g_browser_process->floc_blocklist_service()->OnBlocklistFileReady(
CreateBlocklistFile(blocklist_entries));
FinishOutstandingAsyncQueries();
}
history::HistoryService* history_service() {
......@@ -320,6 +389,9 @@ class FlocIdProviderWithCustomizedServicesBrowserTest
base::test::ScopedFeatureList scoped_feature_list_;
base::ScopedTempDir scoped_temp_dir_;
int next_unique_file_suffix_ = 1;
std::unique_ptr<
BrowserContextDependencyManager::CreateServicesCallbackList::Subscription>
subscription_;
......@@ -397,8 +469,7 @@ IN_PROC_BROWSER_TEST_F(FlocIdProviderWithCustomizedServicesBrowserTest,
ASSERT_EQ(1u, user_event_service()->GetRecordedUserEvents().size());
ExpireHistoryBefore(base::Time::Now());
FinishOutstandingRemotePermissionQueries();
FinishOutstandingHistoryQueries();
FinishOutstandingAsyncQueries();
// Expect that the 2nd FlocIdComputed event should be due to history deletion.
ASSERT_EQ(2u, user_event_service()->GetRecordedUserEvents().size());
......
......@@ -52,8 +52,8 @@ FlocIdProviderImpl::FlocIdProviderImpl(
OnStateChanged(sync_service);
if (g_browser_process->floc_blocklist_service()->BlocklistLoaded())
OnBlocklistLoaded();
if (g_browser_process->floc_blocklist_service()->IsBlocklistFileReady())
OnBlocklistFileReady();
}
FlocIdProviderImpl::~FlocIdProviderImpl() = default;
......@@ -179,11 +179,11 @@ void FlocIdProviderImpl::OnURLsDeleted(
ComputeFloc(ComputeFlocTrigger::kHistoryDelete);
}
void FlocIdProviderImpl::OnBlocklistLoaded() {
if (first_blocklist_loaded_seen_)
void FlocIdProviderImpl::OnBlocklistFileReady() {
if (first_blocklist_file_ready_seen_)
return;
first_blocklist_loaded_seen_ = true;
first_blocklist_file_ready_seen_ = true;
MaybeTriggerFirstFlocComputation();
}
......@@ -206,7 +206,7 @@ void FlocIdProviderImpl::MaybeTriggerFirstFlocComputation() {
if (!first_sync_history_enabled_seen_ ||
(base::FeatureList::IsEnabled(features::kFlocIdBlocklistFiltering) &&
!first_blocklist_loaded_seen_)) {
!first_blocklist_file_ready_seen_)) {
return;
}
......@@ -369,14 +369,23 @@ void FlocIdProviderImpl::ApplyAdditionalFiltering(
const FlocId& sim_hash) {
DCHECK(sim_hash.IsValid());
if (base::FeatureList::IsEnabled(features::kFlocIdBlocklistFiltering) &&
g_browser_process->floc_blocklist_service()->ShouldBlockFloc(
sim_hash.ToUint64())) {
std::move(callback).Run(ComputeFlocResult(sim_hash, FlocId()));
if (!base::FeatureList::IsEnabled(features::kFlocIdBlocklistFiltering)) {
std::move(callback).Run(ComputeFlocResult(sim_hash, sim_hash));
return;
}
std::move(callback).Run(ComputeFlocResult(sim_hash, sim_hash));
g_browser_process->floc_blocklist_service()->FilterByBlocklist(
sim_hash, base::BindOnce(&FlocIdProviderImpl::DidApplyAdditionalFiltering,
weak_ptr_factory_.GetWeakPtr(),
std::move(callback), sim_hash));
}
void FlocIdProviderImpl::DidApplyAdditionalFiltering(
ComputeFlocCompletedCallback callback,
FlocId sim_hash,
FlocId final_hash) {
std::move(callback).Run(
ComputeFlocResult(std::move(sim_hash), std::move(final_hash)));
}
} // namespace federated_learning
......@@ -113,7 +113,7 @@ class FlocIdProviderImpl : public FlocIdProvider,
const history::DeletionInfo& deletion_info) override;
// FlocBlocklistService::Observer
void OnBlocklistLoaded() override;
void OnBlocklistFileReady() override;
// syncer::SyncServiceObserver:
void OnStateChanged(syncer::SyncService* sync_service) override;
......@@ -143,6 +143,9 @@ class FlocIdProviderImpl : public FlocIdProvider,
// history. For example, invalidate it if it's in the blocklist.
void ApplyAdditionalFiltering(ComputeFlocCompletedCallback callback,
const FlocId& sim_hash);
void DidApplyAdditionalFiltering(ComputeFlocCompletedCallback callback,
FlocId sim_hash,
FlocId final_hash);
// The id to be exposed to the JS API.
FlocId floc_id_;
......@@ -155,7 +158,7 @@ class FlocIdProviderImpl : public FlocIdProvider,
// loggings, updates, etc.), and compute again.
base::Optional<ComputeFlocTrigger> pending_recompute_event_;
bool first_blocklist_loaded_seen_ = false;
bool first_blocklist_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
......
......@@ -33,6 +33,27 @@ namespace {
using ComputeFlocTrigger = FlocIdProviderImpl::ComputeFlocTrigger;
using ComputeFlocResult = FlocIdProviderImpl::ComputeFlocResult;
class MockFlocBlocklistService : public FlocBlocklistService {
public:
using FlocBlocklistService::FlocBlocklistService;
void ConfigureFlocToBlock(const FlocId& floc_to_block) {
floc_to_block_ = floc_to_block;
}
void FilterByBlocklist(const FlocId& unfiltered_floc,
FilterByBlocklistCallback callback) override {
if (floc_to_block_ == unfiltered_floc) {
std::move(callback).Run(FlocId());
return;
}
std::move(callback).Run(unfiltered_floc);
}
private:
FlocId floc_to_block_;
};
class FakeFlocRemotePermissionService : public FlocRemotePermissionService {
public:
using FlocRemotePermissionService::FlocRemotePermissionService;
......@@ -182,8 +203,10 @@ class FlocIdProviderUnitTest : public testing::Test {
&prefs_, /*is_off_the_record=*/false, /*store_last_modified=*/false,
/*restore_session=*/false);
auto blocklist_service = std::make_unique<MockFlocBlocklistService>();
blocklist_service_ = blocklist_service.get();
TestingBrowserProcess::GetGlobal()->SetFlocBlocklistService(
std::make_unique<FlocBlocklistService>());
std::move(blocklist_service));
history_service_ = std::make_unique<history::HistoryService>();
history_service_->Init(
......@@ -287,11 +310,6 @@ class FlocIdProviderUnitTest : public testing::Test {
floc_id_provider_->OnComputeFlocScheduledUpdate();
}
void OnBlocklistLoaded(const std::unordered_set<uint64_t>& blocklist) {
g_browser_process->floc_blocklist_service()->OnBlocklistLoadResult(
blocklist);
}
protected:
content::BrowserTaskEnvironment task_environment_;
......@@ -306,6 +324,8 @@ class FlocIdProviderUnitTest : public testing::Test {
scoped_refptr<FakeCookieSettings> fake_cookie_settings_;
std::unique_ptr<MockFlocIdProvider> floc_id_provider_;
MockFlocBlocklistService* blocklist_service_;
base::ScopedTempDir temp_dir_;
DISALLOW_COPY_AND_ASSIGN(FlocIdProviderUnitTest);
......@@ -811,9 +831,9 @@ TEST_F(FlocIdProviderUnitTest,
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({});
// Trigger the blocklist ready event. The 1st floc computation should be
// triggered now as sync & sync-history are enabled the blocklist is ready.
blocklist_service_->OnBlocklistFileReady(base::FilePath());
EXPECT_TRUE(first_floc_computation_triggered());
}
......@@ -823,9 +843,9 @@ TEST_F(FlocIdProviderUnitTest,
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(features::kFlocIdBlocklistFiltering);
// Load the blocklist. The 1st floc computation should not be
// Trigger the blocklist ready event. The 1st floc computation should not be
// triggered as sync & sync-history are not enabled yet.
OnBlocklistLoaded({});
blocklist_service_->OnBlocklistFileReady(base::FilePath());
EXPECT_FALSE(first_floc_computation_triggered());
......@@ -854,10 +874,9 @@ TEST_F(FlocIdProviderUnitTest, BlocklistFilteringEnabled_BlockedFloc) {
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);
// Trigger the blocklist ready event, and turn on sync & sync-history to
// trigger the 1st floc computation.
blocklist_service_->OnBlocklistFileReady(base::FilePath());
test_sync_service_->SetTransportState(
syncer::SyncService::TransportState::ACTIVE);
......@@ -875,9 +894,8 @@ TEST_F(FlocIdProviderUnitTest, BlocklistFilteringEnabled_BlockedFloc) {
EXPECT_EQ(1u, floc_id_provider_->log_event_count());
EXPECT_EQ(floc_from_history, floc_id());
// Insert the current floc to blocklist and reload it.
blocklist.insert(floc_from_history.ToUint64());
OnBlocklistLoaded(blocklist);
// Set the blocklist to block |floc_from_history|.
blocklist_service_->ConfigureFlocToBlock(floc_from_history);
task_environment_.FastForwardBy(base::TimeDelta::FromDays(1));
......@@ -908,9 +926,8 @@ TEST_F(FlocIdProviderUnitTest, BlocklistFilteringEnabled_BlockedFloc) {
event.event_trigger());
EXPECT_EQ(floc_from_history.ToUint64(), event.floc_id());
// Reset and reload the blocklist.
blocklist.clear();
OnBlocklistLoaded(blocklist);
// Reset the blocklist to block nothing.
blocklist_service_->ConfigureFlocToBlock(FlocId());
task_environment_.FastForwardBy(base::TimeDelta::FromDays(1));
......
......@@ -16,10 +16,7 @@ static_library("federated_learning") {
"sim_hash.h",
]
public_deps = [
"//components/federated_learning/proto:federated_learning",
"//third_party/protobuf:protobuf_lite",
]
public_deps = [ "//third_party/protobuf:protobuf_lite" ]
deps = [ "//base" ]
}
......
......@@ -11,7 +11,7 @@
#include "base/task/post_task.h"
#include "base/task_runner_util.h"
#include "components/federated_learning/floc_constants.h"
#include "components/federated_learning/proto/blocklist.pb.h"
#include "third_party/protobuf/src/google/protobuf/io/coded_stream.h"
#include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.h"
namespace federated_learning {
......@@ -36,26 +36,39 @@ class CopyingFileInputStream : public google::protobuf::io::CopyingInputStream {
base::File file_;
};
FlocBlocklistService::LoadedBlocklist LoadBlockListOnBackgroundThread(
const base::FilePath& file_path) {
FlocId FilterByBlocklistOnBackgroundThread(const FlocId& unfiltered_floc,
const base::FilePath& file_path) {
DCHECK(unfiltered_floc.IsValid());
base::File blocklist_file(file_path,
base::File::FLAG_OPEN | base::File::FLAG_READ);
if (!blocklist_file.IsValid())
return FlocBlocklistService::LoadedBlocklist();
return FlocId();
CopyingFileInputStream copying_stream(std::move(blocklist_file));
google::protobuf::io::CopyingInputStreamAdaptor zero_copy_stream_adaptor(
&copying_stream);
proto::Blocklist blocklist_proto;
if (!blocklist_proto.ParseFromZeroCopyStream(&zero_copy_stream_adaptor))
return FlocBlocklistService::LoadedBlocklist();
google::protobuf::io::CodedInputStream input_stream(
&zero_copy_stream_adaptor);
const uint64_t kMaxPossibleFloc = (1ULL << kMaxNumberOfBitsInFloc) - 1;
std::unordered_set<uint64_t> blocklist;
for (uint64_t entry : blocklist_proto.entries())
blocklist.insert(entry);
uint64_t next;
while (input_stream.ReadVarint64(&next)) {
if (next > kMaxPossibleFloc) {
// A sign of a corrupted file. Block the floc to be safe.
// TODO: add metrics
return FlocId();
}
return blocklist;
if (unfiltered_floc.ToUint64() == next) {
// The floc should be blocked.
return FlocId();
}
}
// The floc should be not blocked.
return unfiltered_floc;
}
} // namespace
......@@ -63,7 +76,8 @@ FlocBlocklistService::LoadedBlocklist LoadBlockListOnBackgroundThread(
FlocBlocklistService::FlocBlocklistService()
: background_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})) {}
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})),
weak_ptr_factory_(this) {}
FlocBlocklistService::~FlocBlocklistService() = default;
......@@ -75,13 +89,28 @@ void FlocBlocklistService::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
bool FlocBlocklistService::IsBlocklistFileReady() const {
return blocklist_file_path_.has_value();
}
void FlocBlocklistService::OnBlocklistFileReady(
const base::FilePath& file_path) {
blocklist_file_path_ = file_path;
for (auto& observer : observers_)
observer.OnBlocklistFileReady();
}
void FlocBlocklistService::FilterByBlocklist(
const FlocId& unfiltered_floc,
FilterByBlocklistCallback callback) {
DCHECK(unfiltered_floc.IsValid());
DCHECK(blocklist_file_path_.has_value());
base::PostTaskAndReplyWithResult(
background_task_runner_.get(), FROM_HERE,
base::BindOnce(&LoadBlockListOnBackgroundThread, file_path),
base::BindOnce(&FlocBlocklistService::OnBlocklistLoadResult,
AsWeakPtr()));
base::BindOnce(&FilterByBlocklistOnBackgroundThread, unfiltered_floc,
blocklist_file_path_.value()),
std::move(callback));
}
void FlocBlocklistService::SetBackgroundTaskRunnerForTesting(
......@@ -89,26 +118,4 @@ void FlocBlocklistService::SetBackgroundTaskRunnerForTesting(
background_task_runner_ = background_task_runner;
}
bool FlocBlocklistService::BlocklistLoaded() const {
return loaded_blocklist_.has_value();
}
bool FlocBlocklistService::ShouldBlockFloc(uint64_t floc_id) const {
// If the blocklist hasn't been loaded or if there was a load failure, we
// block all flocs.
if (!loaded_blocklist_)
return true;
return loaded_blocklist_->find(floc_id) != loaded_blocklist_->end();
}
void FlocBlocklistService::OnBlocklistLoadResult(LoadedBlocklist blocklist) {
loaded_blocklist_ = std::move(blocklist);
if (loaded_blocklist_) {
for (auto& observer : observers_)
observer.OnBlocklistLoaded();
}
}
} // namespace federated_learning
......@@ -7,15 +7,15 @@
#include <stdint.h>
#include <unordered_set>
#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/optional.h"
#include "components/federated_learning/floc_id.h"
namespace base {
class FilePath;
class SequencedTaskRunner;
} // namespace base
......@@ -25,16 +25,15 @@ namespace federated_learning {
// the component updater.
//
// File reading and parsing is posted to |background_task_runner_|.
class FlocBlocklistService
: public base::SupportsWeakPtr<FlocBlocklistService> {
class FlocBlocklistService {
public:
using FilterByBlocklistCallback = base::OnceCallback<void(FlocId)>;
class Observer {
public:
virtual void OnBlocklistLoaded() = 0;
virtual void OnBlocklistFileReady() = 0;
};
using LoadedBlocklist = base::Optional<std::unordered_set<uint64_t>>;
FlocBlocklistService();
virtual ~FlocBlocklistService();
......@@ -45,21 +44,20 @@ class FlocBlocklistService
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
bool IsBlocklistFileReady() const;
// Virtual for testing.
virtual void OnBlocklistFileReady(const base::FilePath& file_path);
// Virtual for testing.
virtual void FilterByBlocklist(const FlocId& unfiltered_floc,
FilterByBlocklistCallback callback);
void SetBackgroundTaskRunnerForTesting(
scoped_refptr<base::SequencedTaskRunner> background_task_runner);
bool BlocklistLoaded() const;
bool ShouldBlockFloc(uint64_t floc_id) const;
protected:
// Virtual for testing.
virtual void OnBlocklistLoadResult(LoadedBlocklist blocklist);
private:
friend class MockFlocBlocklistService;
friend class FlocBlocklistServiceTest;
friend class FlocIdProviderUnitTest;
friend class FlocIdProviderWithCustomizedServicesBrowserTest;
......@@ -68,7 +66,9 @@ class FlocBlocklistService
base::ObserverList<Observer>::Unchecked observers_;
LoadedBlocklist loaded_blocklist_;
base::Optional<base::FilePath> blocklist_file_path_;
base::WeakPtrFactory<FlocBlocklistService> weak_ptr_factory_;
};
} // namespace federated_learning
......
......@@ -11,43 +11,34 @@
#include "base/files/scoped_temp_dir.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/bind_test_util.h"
#include "base/test/task_environment.h"
#include "base/test/test_simple_task_runner.h"
#include "components/federated_learning/proto/blocklist.pb.h"
#include "components/federated_learning/floc_constants.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/protobuf/src/google/protobuf/io/coded_stream.h"
#include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.h"
namespace federated_learning {
// The purpose of this class is to expose the loaded_blocklist_ member and to
// allow monitoring the OnBlocklistLoadResult method calls.
class MockFlocBlocklistService : public FlocBlocklistService {
class CopyingFileOutputStream
: public google::protobuf::io::CopyingOutputStream {
public:
using FlocBlocklistService::FlocBlocklistService;
explicit CopyingFileOutputStream(base::File file) : file_(std::move(file)) {}
void OnBlocklistLoadResult(LoadedBlocklist blocklist) override {
FlocBlocklistService::OnBlocklistLoadResult(std::move(blocklist));
CopyingFileOutputStream(const CopyingFileOutputStream&) = delete;
CopyingFileOutputStream& operator=(const CopyingFileOutputStream&) = delete;
++load_result_count_;
~CopyingFileOutputStream() override = default;
if (load_result_count_ == expected_load_result_count_)
run_loop_.Quit();
}
const LoadedBlocklist& loaded_blocklist() { return loaded_blocklist_; }
void WaitForExpectedLoadResultCount(size_t expected_load_result_count) {
DCHECK(!run_loop_.running());
if (expected_load_result_count_ >= expected_load_result_count)
return;
expected_load_result_count_ = expected_load_result_count;
run_loop_.Run();
// google::protobuf::io::CopyingOutputStream:
bool Write(const void* buffer, int size) override {
return file_.WriteAtCurrentPos(static_cast<const char*>(buffer), size) ==
size;
}
private:
size_t load_result_count_ = 0;
size_t expected_load_result_count_ = 0;
base::RunLoop run_loop_;
base::File file_;
};
class FlocBlocklistServiceTest : public ::testing::Test {
......@@ -61,7 +52,7 @@ class FlocBlocklistServiceTest : public ::testing::Test {
protected:
void SetUp() override {
service_ = std::make_unique<MockFlocBlocklistService>();
service_ = std::make_unique<FlocBlocklistService>();
service_->SetBackgroundTaskRunnerForTesting(background_task_runner_);
}
......@@ -71,33 +62,58 @@ class FlocBlocklistServiceTest : public ::testing::Test {
base::NumberToString(next_unique_file_suffix_++));
}
base::FilePath CreateTestBlocklistProtoFile(
const std::vector<uint64_t>& blocklist) {
base::FilePath CreateBlocklistFile(const std::vector<uint64_t>& blocklist) {
base::FilePath file_path = GetUniqueTemporaryPath();
base::File file(file_path, base::File::FLAG_CREATE | base::File::FLAG_READ |
base::File::FLAG_WRITE);
CHECK(file.IsValid());
federated_learning::proto::Blocklist blocklist_proto;
for (uint64_t n : blocklist)
blocklist_proto.add_entries(n);
CopyingFileOutputStream copying_stream(std::move(file));
google::protobuf::io::CopyingOutputStreamAdaptor zero_copy_stream_adaptor(
&copying_stream);
std::string contents;
CHECK(blocklist_proto.SerializeToString(&contents));
google::protobuf::io::CodedOutputStream output_stream(
&zero_copy_stream_adaptor);
for (uint64_t next : blocklist)
output_stream.WriteVarint64(next);
CHECK(!output_stream.HadError());
CHECK_EQ(static_cast<int>(contents.size()),
base::WriteFile(file_path, contents.data(),
static_cast<int>(contents.size())));
return file_path;
}
base::FilePath CreateCorruptedTestBlocklistProtoFile() {
base::FilePath file_path = GetUniqueTemporaryPath();
std::string contents = "1234\n5678\n";
CHECK_EQ(static_cast<int>(contents.size()),
base::WriteFile(file_path, contents.data(),
static_cast<int>(contents.size())));
base::FilePath InitializeBlocklistFile(
const std::vector<uint64_t>& blocklist) {
base::FilePath file_path = CreateBlocklistFile(blocklist);
service()->OnBlocklistFileReady(file_path);
EXPECT_TRUE(blocklist_file_path().has_value());
return file_path;
}
MockFlocBlocklistService* service() { return service_.get(); }
FlocId MaxFlocId() { return FlocId((1ULL << kMaxNumberOfBitsInFloc) - 1); }
FlocBlocklistService* service() { return service_.get(); }
const base::Optional<base::FilePath>& blocklist_file_path() {
return service()->blocklist_file_path_;
}
FlocId FilterByBlocklist(const FlocId& unfiltered_floc) {
FlocId result;
base::RunLoop run_loop;
auto cb = base::BindLambdaForTesting([&](FlocId filtered_floc) {
result = filtered_floc;
run_loop.Quit();
});
service()->FilterByBlocklist(unfiltered_floc, std::move(cb));
background_task_runner_->RunPendingTasks();
run_loop.Run();
return result;
}
protected:
base::test::TaskEnvironment task_environment_;
......@@ -106,73 +122,60 @@ class FlocBlocklistServiceTest : public ::testing::Test {
scoped_refptr<base::TestSimpleTaskRunner> background_task_runner_;
std::unique_ptr<MockFlocBlocklistService> service_;
std::unique_ptr<FlocBlocklistService> service_;
};
TEST_F(FlocBlocklistServiceTest, Startup_NoBlocklistNotNotified) {
EXPECT_FALSE(service()->loaded_blocklist().has_value());
TEST_F(FlocBlocklistServiceTest, NoFilePath) {
EXPECT_FALSE(blocklist_file_path().has_value());
}
TEST_F(FlocBlocklistServiceTest, NewEmptyBlocklist_Loaded) {
base::FilePath file_path = CreateTestBlocklistProtoFile({});
service()->OnBlocklistFileReady(file_path);
background_task_runner_->RunPendingTasks();
service()->WaitForExpectedLoadResultCount(1u);
EXPECT_TRUE(service()->loaded_blocklist().has_value());
EXPECT_EQ(service()->loaded_blocklist()->size(), 0u);
TEST_F(FlocBlocklistServiceTest, EmptyList) {
InitializeBlocklistFile({});
EXPECT_EQ(FlocId(0), FilterByBlocklist(FlocId(0)));
EXPECT_EQ(FlocId(1), FilterByBlocklist(FlocId(1)));
EXPECT_EQ(MaxFlocId(), FilterByBlocklist(MaxFlocId()));
}
TEST_F(FlocBlocklistServiceTest, NewNonEmptyBlocklist_Loaded) {
base::FilePath file_path = CreateTestBlocklistProtoFile({1, 2, 3, 0});
service()->OnBlocklistFileReady(file_path);
background_task_runner_->RunPendingTasks();
service()->WaitForExpectedLoadResultCount(1u);
EXPECT_TRUE(service()->loaded_blocklist().has_value());
EXPECT_EQ(service()->loaded_blocklist()->size(), 4u);
EXPECT_TRUE(service()->loaded_blocklist()->count(0));
EXPECT_TRUE(service()->loaded_blocklist()->count(1));
EXPECT_TRUE(service()->loaded_blocklist()->count(2));
EXPECT_TRUE(service()->loaded_blocklist()->count(3));
TEST_F(FlocBlocklistServiceTest, List_0) {
InitializeBlocklistFile({0});
EXPECT_EQ(FlocId(), FilterByBlocklist(FlocId(0)));
EXPECT_EQ(FlocId(1), FilterByBlocklist(FlocId(1)));
EXPECT_EQ(MaxFlocId(), FilterByBlocklist(MaxFlocId()));
}
TEST_F(FlocBlocklistServiceTest, NonExistentBlocklist_NotLoaded) {
base::FilePath file_path = GetUniqueTemporaryPath();
service()->OnBlocklistFileReady(file_path);
TEST_F(FlocBlocklistServiceTest, List_0_2) {
InitializeBlocklistFile({0, 2});
EXPECT_EQ(FlocId(), FilterByBlocklist(FlocId(0)));
EXPECT_EQ(FlocId(1), FilterByBlocklist(FlocId(1)));
EXPECT_EQ(FlocId(), FilterByBlocklist(FlocId(2)));
EXPECT_EQ(FlocId(3), FilterByBlocklist(FlocId(3)));
EXPECT_EQ(MaxFlocId(), FilterByBlocklist(MaxFlocId()));
}
background_task_runner_->RunPendingTasks();
service()->WaitForExpectedLoadResultCount(1u);
TEST_F(FlocBlocklistServiceTest, List_1_Max) {
InitializeBlocklistFile({1, MaxFlocId().ToUint64()});
EXPECT_EQ(FlocId(0), FilterByBlocklist(FlocId(0)));
EXPECT_EQ(FlocId(), FilterByBlocklist(FlocId(1)));
EXPECT_EQ(FlocId(), FilterByBlocklist(MaxFlocId()));
}
EXPECT_FALSE(service()->loaded_blocklist().has_value());
TEST_F(FlocBlocklistServiceTest, List_MaxFlocPlus1) {
InitializeBlocklistFile({(1ULL << kMaxNumberOfBitsInFloc)});
EXPECT_EQ(FlocId(), FilterByBlocklist(FlocId(0)));
EXPECT_EQ(FlocId(), FilterByBlocklist(FlocId(1)));
}
TEST_F(FlocBlocklistServiceTest, CorruptedBlocklist_NotLoaded) {
base::FilePath file_path = CreateCorruptedTestBlocklistProtoFile();
TEST_F(FlocBlocklistServiceTest, NonExistentBlocklist_Blocked) {
base::FilePath file_path = GetUniqueTemporaryPath();
service()->OnBlocklistFileReady(file_path);
background_task_runner_->RunPendingTasks();
service()->WaitForExpectedLoadResultCount(1u);
EXPECT_FALSE(service()->loaded_blocklist().has_value());
EXPECT_EQ(FlocId(), FilterByBlocklist(FlocId(3)));
}
TEST_F(FlocBlocklistServiceTest, MultipleUpdate_LatestOneLoaded) {
base::FilePath file_path1 = CreateTestBlocklistProtoFile({1, 2, 3, 0});
base::FilePath file_path2 = CreateTestBlocklistProtoFile({4});
service()->OnBlocklistFileReady(file_path1);
service()->OnBlocklistFileReady(file_path2);
EXPECT_FALSE(service()->loaded_blocklist().has_value());
background_task_runner_->RunPendingTasks();
service()->WaitForExpectedLoadResultCount(2u);
EXPECT_TRUE(service()->loaded_blocklist().has_value());
EXPECT_EQ(service()->loaded_blocklist()->size(), 1u);
EXPECT_TRUE(service()->loaded_blocklist()->count(4));
InitializeBlocklistFile({500});
InitializeBlocklistFile({600});
EXPECT_EQ(FlocId(500), FilterByBlocklist(FlocId(500)));
EXPECT_EQ(FlocId(), FilterByBlocklist(FlocId(600)));
}
} // namespace federated_learning
\ No newline at end of file
......@@ -16,7 +16,7 @@ static_assert(kMaxNumberOfBitsInFloc > 0 &&
const char kManifestFlocComponentFormatKey[] = "floc_component_format";
const int kCurrentFlocComponentFormatVersion = 1;
const int kCurrentFlocComponentFormatVersion = 2;
const base::FilePath::CharType kTopLevelDirectoryName[] =
FILE_PATH_LITERAL("Floc");
......
# 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.
import("//third_party/protobuf/proto_library.gni")
proto_library("federated_learning") {
sources = [ "blocklist.proto" ]
}
// 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.
syntax = "proto2";
option optimize_for = LITE_RUNTIME;
package federated_learning.proto;
option java_package = "org.chromium.components.federated_learning.proto";
message Blocklist {
repeated int64 entries = 1 [packed = true];
}
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