blob: 83e29074c846e31e6f03faad09dbcaa8bf8b4e63 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/attribution_reporting/destination_throttler.h"
#include "base/strings/strcat.h"
#include "base/time/time.h"
#include "components/attribution_reporting/destination_set.h"
#include "content/public/test/browser_task_environment.h"
#include "net/base/schemeful_site.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
namespace {
attribution_reporting::DestinationSet Destinations(
std::initializer_list<std::string> hosts) {
attribution_reporting::DestinationSet::Destinations dests;
for (const std::string& host : hosts) {
dests.insert(
net::SchemefulSite::Deserialize(base::StrCat({"https://", host})));
}
return attribution_reporting::DestinationSet::Create(dests).value();
}
net::SchemefulSite Site(const std::string& host) {
return net::SchemefulSite::Deserialize(base::StrCat({"https://", host}));
}
class DestinationThrottlerTest : public testing::Test {
public:
DestinationThrottlerTest() = default;
~DestinationThrottlerTest() override = default;
protected:
BrowserTaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
};
using Result = DestinationThrottler::Result;
TEST_F(DestinationThrottlerTest, SourceLimits) {
DestinationThrottler::Policy policy{
.max_total = 2,
.max_per_reporting_site = 100, // irrelevant
.rate_limit_window = base::Minutes(1)};
DestinationThrottler throttler(policy);
// First source site:
EXPECT_EQ(Result::kAllowed, throttler.UpdateAndGetResult(
Destinations({"foo1.test"}),
Site("source1.test"), Site("report.test")));
EXPECT_EQ(Result::kAllowed, throttler.UpdateAndGetResult(
Destinations({"foo2.test"}),
Site("source1.test"), Site("report.test")));
EXPECT_EQ(
Result::kHitGlobalLimit,
throttler.UpdateAndGetResult(Destinations({"foo3.test"}),
Site("source1.test"), Site("report.test")));
// Second source site should be independent:
EXPECT_EQ(Result::kAllowed, throttler.UpdateAndGetResult(
Destinations({"foo1.test"}),
Site("source2.test"), Site("report.test")));
EXPECT_EQ(Result::kAllowed, throttler.UpdateAndGetResult(
Destinations({"foo2.test"}),
Site("source2.test"), Site("report.test")));
EXPECT_EQ(
Result::kHitGlobalLimit,
throttler.UpdateAndGetResult(Destinations({"foo3.test"}),
Site("source2.test"), Site("report.test")));
}
TEST_F(DestinationThrottlerTest, ReportingSitesLimits) {
DestinationThrottler::Policy policy{.max_total = 100, // irrelevant
.max_per_reporting_site = 2,
.rate_limit_window = base::Minutes(1)};
DestinationThrottler throttler(policy);
// First reporting site:
EXPECT_EQ(Result::kAllowed, throttler.UpdateAndGetResult(
Destinations({"foo1.test"}),
Site("source.test"), Site("report1.test")));
EXPECT_EQ(Result::kAllowed, throttler.UpdateAndGetResult(
Destinations({"foo2.test"}),
Site("source.test"), Site("report1.test")));
EXPECT_EQ(
Result::kHitReportingLimit,
throttler.UpdateAndGetResult(Destinations({"foo3.test"}),
Site("source.test"), Site("report1.test")));
// Second reporting site should be independent:
EXPECT_EQ(Result::kAllowed, throttler.UpdateAndGetResult(
Destinations({"foo1.test"}),
Site("source.test"), Site("report2.test")));
EXPECT_EQ(Result::kAllowed, throttler.UpdateAndGetResult(
Destinations({"foo2.test"}),
Site("source.test"), Site("report2.test")));
EXPECT_EQ(
Result::kHitReportingLimit,
throttler.UpdateAndGetResult(Destinations({"foo3.test"}),
Site("source.test"), Site("report2.test")));
}
TEST_F(DestinationThrottlerTest, MultipleOverLimit) {
DestinationThrottler::Policy policy{
.max_total = 2,
.max_per_reporting_site = 100, // irrelevant
.rate_limit_window = base::Minutes(1)};
DestinationThrottler throttler(policy);
EXPECT_EQ(Result::kHitGlobalLimit,
throttler.UpdateAndGetResult(
Destinations({"foo1.test", "foo2.test", "foo3.test"}),
Site("source.test"), Site("report.test")));
EXPECT_EQ(Result::kAllowed, throttler.UpdateAndGetResult(
Destinations({"foo1.test"}),
Site("source.test"), Site("report.test")));
EXPECT_EQ(Result::kAllowed, throttler.UpdateAndGetResult(
Destinations({"foo2.test"}),
Site("source.test"), Site("report.test")));
EXPECT_EQ(
Result::kHitGlobalLimit,
throttler.UpdateAndGetResult(Destinations({"foo3.test"}),
Site("source.test"), Site("report.test")));
}
TEST_F(DestinationThrottlerTest, RollingWindow) {
DestinationThrottler::Policy policy{
.max_total = 2,
.max_per_reporting_site = 100, // irrelevant
.rate_limit_window = base::Minutes(1)};
DestinationThrottler throttler(policy);
// Time 0s
EXPECT_EQ(Result::kAllowed, throttler.UpdateAndGetResult(
Destinations({"foo1.test"}),
Site("source.test"), Site("report.test")));
task_environment_.FastForwardBy(base::Seconds(30));
// Time 30s
EXPECT_EQ(Result::kAllowed, throttler.UpdateAndGetResult(
Destinations({"foo2.test"}),
Site("source.test"), Site("report.test")));
task_environment_.FastForwardBy(base::Seconds(31));
// Time 1:01. foo1.test should be evicted.
EXPECT_EQ(Result::kAllowed, throttler.UpdateAndGetResult(
Destinations({"foo3.test"}),
Site("source.test"), Site("report.test")));
EXPECT_EQ(
Result::kHitGlobalLimit,
throttler.UpdateAndGetResult(Destinations({"foo4.test"}),
Site("source.test"), Site("report.test")));
task_environment_.FastForwardBy(base::Seconds(30));
// Time 1:31. foo2.test should be evicted.
EXPECT_EQ(Result::kAllowed, throttler.UpdateAndGetResult(
Destinations({"foo5.test"}),
Site("source.test"), Site("report.test")));
EXPECT_EQ(
Result::kHitGlobalLimit,
throttler.UpdateAndGetResult(Destinations({"foo6.test"}),
Site("source.test"), Site("report.test")));
}
// Insert a new element once all previous elements are too old.
TEST_F(DestinationThrottlerTest, CleanUpOldEntries) {
DestinationThrottler::Policy policy{.max_total = 100, // irrelevant
.max_per_reporting_site = 2,
.rate_limit_window = base::Minutes(1)};
DestinationThrottler throttler(policy);
// Time 0s
EXPECT_EQ(Result::kAllowed, throttler.UpdateAndGetResult(
Destinations({"foo1.test"}),
Site("source.test"), Site("report.test")));
EXPECT_EQ(Result::kAllowed, throttler.UpdateAndGetResult(
Destinations({"foo2.test"}),
Site("source.test"), Site("report.test")));
EXPECT_EQ(
Result::kHitReportingLimit,
throttler.UpdateAndGetResult(Destinations({"foo3.test"}),
Site("source.test"), Site("report.test")));
task_environment_.FastForwardBy(base::Seconds(61));
EXPECT_EQ(Result::kAllowed, throttler.UpdateAndGetResult(
Destinations({"foo4.test"}),
Site("source.test"), Site("report.test")));
EXPECT_EQ(Result::kAllowed, throttler.UpdateAndGetResult(
Destinations({"foo5.test"}),
Site("source.test"), Site("report.test")));
EXPECT_EQ(
Result::kHitReportingLimit,
throttler.UpdateAndGetResult(Destinations({"foo6.test"}),
Site("source.test"), Site("report.test")));
}
// Exercises EvictEntriesOlderThan with multiple deleted entries.
TEST_F(DestinationThrottlerTest, EvictMultiple) {
DestinationThrottler::Policy policy{
.max_total = 3,
.max_per_reporting_site = 100, // irrelevant
.rate_limit_window = base::Minutes(1)};
DestinationThrottler throttler(policy);
EXPECT_EQ(Result::kAllowed, throttler.UpdateAndGetResult(
Destinations({"foo1.test", "foo2.test"}),
Site("source.test"), Site("report.test")));
task_environment_.FastForwardBy(base::Seconds(31));
EXPECT_EQ(Result::kAllowed, throttler.UpdateAndGetResult(
Destinations({"foo3.test"}),
Site("source.test"), Site("report.test")));
task_environment_.FastForwardBy(base::Seconds(30));
// Should evict the first two destinations.
EXPECT_EQ(Result::kAllowed, throttler.UpdateAndGetResult(
Destinations({"foo4.test", "foo5.test"}),
Site("source.test"), Site("report.test")));
EXPECT_EQ(
Result::kHitGlobalLimit,
throttler.UpdateAndGetResult(Destinations({"foo6.test"}),
Site("source.test"), Site("report.test")));
}
TEST_F(DestinationThrottlerTest, HitBothLimits) {
DestinationThrottler::Policy policy{.max_total = 1,
.max_per_reporting_site = 1,
.rate_limit_window = base::Minutes(1)};
DestinationThrottler throttler(policy);
EXPECT_EQ(Result::kAllowed, throttler.UpdateAndGetResult(
Destinations({"foo1.test"}),
Site("source.test"), Site("report.test")));
EXPECT_EQ(Result::kAllowed, throttler.UpdateAndGetResult(
Destinations({"foo1.test"}),
Site("source.test"), Site("report.test")));
EXPECT_EQ(
Result::kHitBothLimits,
throttler.UpdateAndGetResult(Destinations({"foo2.test"}),
Site("source.test"), Site("report.test")));
}
} // namespace
} // namespace content