blob: a15d3aa38e07a07e4d45f9989e7fc1fafd276727 [file] [log] [blame]
// Copyright 2021 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/privacy_budget/surface_set_with_valuation.h"
#include <cstddef>
#include "base/containers/contains.h"
#include "chrome/browser/privacy_budget/surface_set_equivalence.h"
#include "chrome/browser/privacy_budget/surface_set_valuation.h"
#include "chrome/common/privacy_budget/scoped_privacy_budget_config.h"
#include "chrome/common/privacy_budget/types.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/privacy_budget/identifiable_surface.h"
namespace {
constexpr auto kSurface1 = blink::IdentifiableSurface::FromTypeAndToken(
blink::IdentifiableSurface::Type::kCanvasRenderingContext,
3);
constexpr auto kSurface2 = blink::IdentifiableSurface::FromTypeAndToken(
blink::IdentifiableSurface::Type::kHTMLMediaElement_CanPlayType,
5);
// Usual budgets range from 20..40.
constexpr PrivacyBudgetCost kDefaultMediumBudget = 30.0;
struct ScopedValuation {
ScopedValuation()
: study_configuration(test::ScopedPrivacyBudgetConfig::kEnable),
valuation(equivalence) {}
explicit ScopedValuation(
test::ScopedPrivacyBudgetConfig::Parameters parameters)
: study_configuration(parameters), valuation(equivalence) {}
test::ScopedPrivacyBudgetConfig study_configuration;
SurfaceSetEquivalence equivalence;
SurfaceSetValuation valuation;
};
IdentifiableSurfaceSet CreateSurfaceSetWithSize(size_t count) {
IdentifiableSurfaceSet set;
set.reserve(count);
for (auto token = 0u; token < count; ++token) {
set.insert(blink::IdentifiableSurface::FromTypeAndToken(
blink::IdentifiableSurface::Type::kWebFeature, token));
}
return set;
}
} // namespace
TEST(SurfaceSetWithValuation, BasicGetters) {
ScopedValuation fixture;
SurfaceSetWithValuation set(fixture.valuation);
EXPECT_TRUE(set.Empty());
EXPECT_TRUE(set.TryAdd(kSurface1, kDefaultMediumBudget));
EXPECT_FALSE(set.Empty());
EXPECT_EQ(1u, set.Size());
EXPECT_TRUE(set.contains(kSurface1));
EXPECT_FALSE(set.contains(kSurface2));
set.Clear();
EXPECT_TRUE(set.Empty());
}
TEST(SurfaceSetWithValuation, Idempotence) {
ScopedValuation fixture;
SurfaceSetWithValuation set(fixture.valuation);
ASSERT_TRUE(set.TryAdd(kSurface1, kDefaultMediumBudget));
ASSERT_EQ(SurfaceSetValuation::kDefaultCost, set.Cost());
ASSERT_EQ(1u, set.Size());
// Nothing should change.
ASSERT_TRUE(set.TryAdd(kSurface1, kDefaultMediumBudget));
ASSERT_EQ(SurfaceSetValuation::kDefaultCost, set.Cost());
}
TEST(SurfaceSetWithValuation, Overflow) {
ScopedValuation fixture;
SurfaceSetWithValuation set(fixture.valuation);
int token = 10; // Different surfaces just need to have different token
// values. The values themselves don't matter.
// Adds surfaces until we hit the budget ceiling. The only expected outcome of
// this exercise is that when the loop is done `set` can't accept any more
// surfaces.
//
// By default, ScopedValuation sets things up so that each surface
// independently costs kDefaultCost.
for (PrivacyBudgetCost cost = 0.0; cost < kDefaultMediumBudget;
cost += SurfaceSetValuation::kDefaultCost) {
ASSERT_TRUE(
set.TryAdd(blink::IdentifiableSurface::FromTypeAndToken(
blink::IdentifiableSurface::Type::kWebFeature, token),
kDefaultMediumBudget));
++token;
}
// Overflows
EXPECT_FALSE(
set.TryAdd(blink::IdentifiableSurface::FromTypeAndToken(
blink::IdentifiableSurface::Type::kWebFeature, token),
kDefaultMediumBudget));
}
TEST(SurfaceSetWithValuation, Fill) {
ScopedValuation fixture;
SurfaceSetWithValuation set(fixture.valuation);
int token = 10; // As before, this value doesn't matter. All we care about is
// that each surface is different.
while (set.TryAdd(blink::IdentifiableSurface::FromTypeAndToken(
blink::IdentifiableSurface::Type::kWebFeature, ++token),
kDefaultMediumBudget)) {
}
EXPECT_LE(kDefaultMediumBudget, set.Cost());
}
TEST(SurfaceSetWithValuation, AssignWithBudget_Fits) {
ScopedValuation fixture;
SurfaceSetWithValuation set(fixture.valuation);
const auto kRawSurfaceSet = CreateSurfaceSetWithSize(
kDefaultMediumBudget / SurfaceSetValuation::kDefaultCost);
auto representative_surface_set =
fixture.equivalence.GetRepresentatives(kRawSurfaceSet);
ASSERT_EQ(kRawSurfaceSet.size(), representative_surface_set.size());
// All the surfaces in representative_surface_set should exist as-is in `set`.
set.AssignWithBudget(std::move(representative_surface_set),
kDefaultMediumBudget);
ASSERT_EQ(kRawSurfaceSet.size(), set.Size());
}
TEST(SurfaceSetWithValuation, AssignWithBudget_Overflows) {
ScopedValuation fixture;
SurfaceSetWithValuation set(fixture.valuation);
// Contains twice as many surfaces as is expected to fit.
const auto kRawSurfaceSet = CreateSurfaceSetWithSize(
2 * kDefaultMediumBudget / SurfaceSetValuation::kDefaultCost);
auto representative_surface_set =
fixture.equivalence.GetRepresentatives(kRawSurfaceSet);
ASSERT_EQ(kRawSurfaceSet.size(), representative_surface_set.size());
set.AssignWithBudget(std::move(representative_surface_set),
kDefaultMediumBudget);
EXPECT_GT(kRawSurfaceSet.size(), set.Size());
EXPECT_GE(kDefaultMediumBudget, set.Cost());
for (const auto& s : set)
EXPECT_TRUE(base::Contains(kRawSurfaceSet, s.value()));
}
TEST(SurfaceSetWithValuation, RepresentativeSurface) {
const auto kEquivalenceSet = CreateSurfaceSetWithSize(10);
const auto kEquivalenceList =
IdentifiableSurfaceList(kEquivalenceSet.begin(), kEquivalenceSet.end());
test::ScopedPrivacyBudgetConfig::Parameters pb_parameters;
pb_parameters.enabled = true;
pb_parameters.equivalence_classes.emplace_back(kEquivalenceList);
ScopedValuation fixture(pb_parameters);
SurfaceSetWithValuation set(fixture.valuation);
for (const auto& s : kEquivalenceSet) {
ASSERT_TRUE(set.TryAdd(s, kDefaultMediumBudget));
}
// Since all the surfaces are in the same equivalence class, `set` only
// retains a single representative surface.
EXPECT_EQ(1u, set.Size());
for (const auto& s : kEquivalenceSet) {
EXPECT_TRUE(set.contains(s));
}
const auto representative_surface =
fixture.equivalence.GetRepresentative(*kEquivalenceSet.begin());
EXPECT_TRUE(set.contains(representative_surface));
}