blob: f8acda012d1e61cb02f397a645e39edaeb8b1e9d [file] [log] [blame]
// Copyright 2025 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/private_aggregation/private_aggregation_pending_contributions.h"
#include <stddef.h>
#include <array>
#include <optional>
#include <string_view>
#include <utility>
#include <vector>
#include "base/containers/contains.h"
#include "base/strings/strcat.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features_generated.h"
#include "third_party/blink/public/mojom/aggregation_service/aggregatable_report.mojom.h"
namespace content {
constexpr size_t kExampleMaxNumContributions = 20;
std::vector<std::string_view> GetExampleHistogramSuffixes() {
constexpr std::string_view kExampleSuffix = ".ExampleSuffix";
constexpr std::string_view kAnotherSuffix = ".AnotherSuffix";
return {kExampleSuffix, kAnotherSuffix};
}
using PendingReportLimitResult =
PrivateAggregationPendingContributions::PendingReportLimitResult;
using NullReportBehavior =
PrivateAggregationPendingContributions::NullReportBehavior;
using PAErrorEvent = blink::mojom::PrivateAggregationErrorEvent;
class PrivateAggregationPendingContributionsTest : public testing::Test {
public:
std::vector<PrivateAggregationPendingContributions::BudgeterResult>
AllApprovalVector(size_t n) {
return std::vector<PrivateAggregationPendingContributions::BudgeterResult>(
n, PrivateAggregationPendingContributions::BudgeterResult::kApproved);
}
std::vector<PrivateAggregationPendingContributions::BudgeterResult>
AllDenialVector(size_t n) {
return std::vector<PrivateAggregationPendingContributions::BudgeterResult>(
n, PrivateAggregationPendingContributions::BudgeterResult::kDenied);
}
void MakeConditionalContributionForEachInternalError(
PrivateAggregationPendingContributions& pending_contributions) {
for (auto i = static_cast<int>(PAErrorEvent::kMinValue);
i <= static_cast<int>(PAErrorEvent::kMaxValue); i++) {
auto error_event = static_cast<PAErrorEvent>(i);
// Skip this external event as it will always be triggered.
if (error_event == PAErrorEvent::kAlreadyTriggeredExternalError) {
continue;
}
pending_contributions.AddConditionalContributions(
error_event, {blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/i, /*value=*/1, /*filtering_id=*/0)});
}
}
void ValidateHistograms(
std::vector<std::string_view> expected_suffixes,
base::HistogramBase::Count32 num_final_unmerged_contributions_sample,
base::HistogramBase::Count32 num_merge_keys_sent_or_truncated_sample,
PrivateAggregationPendingContributions::TruncationResult
truncation_result = PrivateAggregationPendingContributions::
TruncationResult::kNoTruncation,
base::HistogramTester* histogram_tester_override = nullptr) const {
const base::HistogramTester& histogram_tester =
histogram_tester_override ? *histogram_tester_override
: histogram_tester_;
constexpr std::string_view kFinalUnmergedContributionsBase =
"PrivacySandbox.PrivateAggregation.NumFinalUnmergedContributions";
constexpr std::string_view kMergeKeysHistogramBase =
"PrivacySandbox.PrivateAggregation.NumContributionMergeKeys";
constexpr std::string_view kTruncationHistogram =
"PrivacySandbox.PrivateAggregation.TruncationResult";
histogram_tester.ExpectUniqueSample(kFinalUnmergedContributionsBase,
num_final_unmerged_contributions_sample,
1);
histogram_tester.ExpectUniqueSample(
kMergeKeysHistogramBase, num_merge_keys_sent_or_truncated_sample, 1);
histogram_tester.ExpectUniqueSample(kTruncationHistogram, truncation_result,
1);
for (std::string_view expected_suffix : expected_suffixes) {
histogram_tester.ExpectUniqueSample(
base::StrCat({kFinalUnmergedContributionsBase, expected_suffix}),
num_final_unmerged_contributions_sample, 1);
histogram_tester.ExpectUniqueSample(
base::StrCat({kMergeKeysHistogramBase, expected_suffix}),
num_merge_keys_sent_or_truncated_sample, 1);
// We don't expect suffixes for the truncation histogram.
}
// Ensure that these suffixes are not hard coded.
constexpr std::array<std::string_view, 4> kUnexpectedSuffixes = {
".ProtectedAudience", ".SharedStorage", ".SharedStorage.FullDelay",
".SharedStorage.ReducedDelay"};
for (std::string_view unexpected_suffix : kUnexpectedSuffixes) {
if (base::Contains(expected_suffixes, unexpected_suffix)) {
// Handles the case where a test might use one of these suffixes.
continue;
}
histogram_tester.ExpectTotalCount(
base::StrCat({kFinalUnmergedContributionsBase, unexpected_suffix}),
0);
histogram_tester.ExpectTotalCount(
base::StrCat({kMergeKeysHistogramBase, unexpected_suffix}), 0);
}
}
protected:
base::test::ScopedFeatureList scoped_feature_list_{
blink::features::kPrivateAggregationApiErrorReporting};
base::HistogramTester histogram_tester_;
};
TEST_F(PrivateAggregationPendingContributionsTest, Empty) {
PrivateAggregationPendingContributions pending_contributions(
kExampleMaxNumContributions, GetExampleHistogramSuffixes());
pending_contributions.MarkContributionsFinalized(
PrivateAggregationPendingContributions::TimeoutOrDisconnect::kTimeout);
EXPECT_THAT(pending_contributions.CompileFinalUnmergedContributions(
{}, PendingReportLimitResult::kNotAtLimit,
NullReportBehavior::kDontSendReport),
testing::IsEmpty());
EXPECT_THAT(std::move(pending_contributions).TakeFinalContributions({}),
testing::IsEmpty());
ValidateHistograms(GetExampleHistogramSuffixes(),
/*num_final_unmerged_contributions_sample=*/0,
/*num_merge_keys_sent_or_truncated_sample=*/0);
}
TEST_F(PrivateAggregationPendingContributionsTest, AddUnconditional) {
const std::vector<blink::mojom::AggregatableReportHistogramContribution>
contributions_vector = {
blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/1, /*value=*/2, /*filtering_id=*/3),
blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/4, /*value=*/5, /*filtering_id=*/std::nullopt)};
PrivateAggregationPendingContributions pending_contributions(
kExampleMaxNumContributions, GetExampleHistogramSuffixes());
pending_contributions.AddUnconditionalContributions(contributions_vector);
pending_contributions.MarkContributionsFinalized(
PrivateAggregationPendingContributions::TimeoutOrDisconnect::kTimeout);
EXPECT_EQ(pending_contributions.CompileFinalUnmergedContributions(
AllApprovalVector(2), PendingReportLimitResult::kNotAtLimit,
NullReportBehavior::kDontSendReport),
contributions_vector);
EXPECT_EQ(std::move(pending_contributions)
.TakeFinalContributions(AllApprovalVector(2)),
contributions_vector);
ValidateHistograms(GetExampleHistogramSuffixes(),
/*num_final_unmerged_contributions_sample=*/2,
/*num_merge_keys_sent_or_truncated_sample=*/2);
}
TEST_F(PrivateAggregationPendingContributionsTest, AddUnconditionalEmpty) {
PrivateAggregationPendingContributions pending_contributions(
kExampleMaxNumContributions, GetExampleHistogramSuffixes());
pending_contributions.AddUnconditionalContributions({});
pending_contributions.MarkContributionsFinalized(
PrivateAggregationPendingContributions::TimeoutOrDisconnect::kDisconnect);
EXPECT_THAT(pending_contributions.CompileFinalUnmergedContributions(
{}, PendingReportLimitResult::kNotAtLimit,
NullReportBehavior::kDontSendReport),
testing::IsEmpty());
EXPECT_THAT(std::move(pending_contributions).TakeFinalContributions({}),
testing::IsEmpty());
ValidateHistograms(GetExampleHistogramSuffixes(),
/*num_final_unmerged_contributions_sample=*/0,
/*num_merge_keys_sent_or_truncated_sample=*/0);
}
TEST_F(PrivateAggregationPendingContributionsTest, AddConditionalTriggered) {
std::vector<blink::mojom::AggregatableReportHistogramContribution>
contributions_vector = {
blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/1, /*value=*/2, /*filtering_id=*/3),
blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/4, /*value=*/5, /*filtering_id=*/std::nullopt)};
PrivateAggregationPendingContributions pending_contributions(
kExampleMaxNumContributions, GetExampleHistogramSuffixes());
pending_contributions.AddConditionalContributions(
PAErrorEvent::kContributionTimeoutReached, contributions_vector);
pending_contributions.MarkContributionsFinalized(
PrivateAggregationPendingContributions::TimeoutOrDisconnect::kTimeout);
EXPECT_EQ(pending_contributions.CompileFinalUnmergedContributions(
{}, PendingReportLimitResult::kNotAtLimit,
NullReportBehavior::kDontSendReport),
contributions_vector);
EXPECT_EQ(std::move(pending_contributions)
.TakeFinalContributions(AllApprovalVector(2)),
contributions_vector);
ValidateHistograms(GetExampleHistogramSuffixes(),
/*num_final_unmerged_contributions_sample=*/2,
/*num_merge_keys_sent_or_truncated_sample=*/2);
}
TEST_F(PrivateAggregationPendingContributionsTest, AddConditionalNotTriggered) {
std::vector<blink::mojom::AggregatableReportHistogramContribution>
contributions_vector = {
blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/1, /*value=*/2, /*filtering_id=*/3),
blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/4, /*value=*/5, /*filtering_id=*/std::nullopt)};
PrivateAggregationPendingContributions pending_contributions(
kExampleMaxNumContributions, GetExampleHistogramSuffixes());
pending_contributions.AddConditionalContributions(
PAErrorEvent::kContributionTimeoutReached, contributions_vector);
pending_contributions.MarkContributionsFinalized(
PrivateAggregationPendingContributions::TimeoutOrDisconnect::kDisconnect);
EXPECT_THAT(pending_contributions.CompileFinalUnmergedContributions(
{}, PendingReportLimitResult::kNotAtLimit,
NullReportBehavior::kDontSendReport),
testing::IsEmpty());
EXPECT_THAT(std::move(pending_contributions).TakeFinalContributions({}),
testing::IsEmpty());
ValidateHistograms(GetExampleHistogramSuffixes(),
/*num_final_unmerged_contributions_sample=*/0,
/*num_merge_keys_sent_or_truncated_sample=*/0);
}
TEST_F(PrivateAggregationPendingContributionsTest, AddConditionalEmpty) {
PrivateAggregationPendingContributions pending_contributions(
kExampleMaxNumContributions, GetExampleHistogramSuffixes());
pending_contributions.AddConditionalContributions(
PAErrorEvent::kTooManyContributions, {});
pending_contributions.MarkContributionsFinalized(
PrivateAggregationPendingContributions::TimeoutOrDisconnect::kDisconnect);
EXPECT_THAT(pending_contributions.CompileFinalUnmergedContributions(
{}, PendingReportLimitResult::kNotAtLimit,
NullReportBehavior::kDontSendReport),
testing::IsEmpty());
EXPECT_THAT(std::move(pending_contributions).TakeFinalContributions({}),
testing::IsEmpty());
ValidateHistograms(GetExampleHistogramSuffixes(),
/*num_final_unmerged_contributions_sample=*/0,
/*num_merge_keys_sent_or_truncated_sample=*/0);
}
TEST_F(PrivateAggregationPendingContributionsTest,
UntriggeredContributionsSkipped) {
PrivateAggregationPendingContributions pending_contributions(
kExampleMaxNumContributions, GetExampleHistogramSuffixes());
pending_contributions.AddUnconditionalContributions(
{blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/1, /*value=*/2, /*filtering_id=*/3)});
pending_contributions.AddConditionalContributions(
PAErrorEvent::kContributionTimeoutReached,
{blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/4, /*value=*/5, /*filtering_id=*/std::nullopt)});
pending_contributions.AddConditionalContributions(
PAErrorEvent::kEmptyReportDropped,
{blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/6, /*value=*/7, /*filtering_id=*/8)});
pending_contributions.MarkContributionsFinalized(
PrivateAggregationPendingContributions::TimeoutOrDisconnect::kTimeout);
// Conditional contributions are placed before unconditional contributions.
const std::vector<blink::mojom::AggregatableReportHistogramContribution>
contributions_vector = {
blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/4, /*value=*/5, /*filtering_id=*/std::nullopt),
blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/1, /*value=*/2, /*filtering_id=*/3)};
EXPECT_EQ(pending_contributions.CompileFinalUnmergedContributions(
AllApprovalVector(1), PendingReportLimitResult::kNotAtLimit,
NullReportBehavior::kDontSendReport),
contributions_vector);
EXPECT_EQ(std::move(pending_contributions)
.TakeFinalContributions(AllApprovalVector(2)),
contributions_vector);
ValidateHistograms(GetExampleHistogramSuffixes(),
/*num_final_unmerged_contributions_sample=*/2,
/*num_merge_keys_sent_or_truncated_sample=*/2);
}
TEST_F(PrivateAggregationPendingContributionsTest, ContributionsMerged) {
PrivateAggregationPendingContributions pending_contributions(
kExampleMaxNumContributions, GetExampleHistogramSuffixes());
const std::vector<blink::mojom::AggregatableReportHistogramContribution>
unmerged_vector = {blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/1, /*value=*/2, /*filtering_id=*/3),
blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/4, /*value=*/5, /*filtering_id=*/6),
blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/1, /*value=*/2, /*filtering_id=*/3)};
pending_contributions.AddUnconditionalContributions(unmerged_vector);
pending_contributions.MarkContributionsFinalized(
PrivateAggregationPendingContributions::TimeoutOrDisconnect::kTimeout);
EXPECT_EQ(pending_contributions.CompileFinalUnmergedContributions(
AllApprovalVector(3), PendingReportLimitResult::kNotAtLimit,
NullReportBehavior::kDontSendReport),
unmerged_vector);
EXPECT_EQ(std::move(pending_contributions)
.TakeFinalContributions(AllApprovalVector(3)),
std::vector<blink::mojom::AggregatableReportHistogramContribution>(
{blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/1, /*value=*/4, /*filtering_id=*/3),
blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/4, /*value=*/5, /*filtering_id=*/6)}));
ValidateHistograms(GetExampleHistogramSuffixes(),
/*num_final_unmerged_contributions_sample=*/3,
/*num_merge_keys_sent_or_truncated_sample=*/2);
}
TEST_F(PrivateAggregationPendingContributionsTest,
ContributionsMergedEvenIfConditionalAndUnconditional) {
PrivateAggregationPendingContributions pending_contributions(
kExampleMaxNumContributions, GetExampleHistogramSuffixes());
pending_contributions.AddUnconditionalContributions(
{blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/1, /*value=*/2, /*filtering_id=*/3)});
pending_contributions.AddConditionalContributions(
PAErrorEvent::kContributionTimeoutReached,
{blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/1, /*value=*/2, /*filtering_id=*/3)});
pending_contributions.AddConditionalContributions(
PAErrorEvent::kReportSuccess,
{blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/1, /*value=*/2, /*filtering_id=*/3)});
pending_contributions.MarkContributionsFinalized(
PrivateAggregationPendingContributions::TimeoutOrDisconnect::kTimeout);
EXPECT_EQ(pending_contributions.CompileFinalUnmergedContributions(
AllApprovalVector(1), PendingReportLimitResult::kNotAtLimit,
NullReportBehavior::kDontSendReport),
std::vector<blink::mojom::AggregatableReportHistogramContribution>(
{blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/1, /*value=*/2, /*filtering_id=*/3),
blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/1, /*value=*/2, /*filtering_id=*/3),
blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/1, /*value=*/2, /*filtering_id=*/3)}));
EXPECT_EQ(std::move(pending_contributions)
.TakeFinalContributions(AllApprovalVector(3)),
std::vector<blink::mojom::AggregatableReportHistogramContribution>(
{blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/1, /*value=*/6, /*filtering_id=*/3)}));
ValidateHistograms(GetExampleHistogramSuffixes(),
/*num_final_unmerged_contributions_sample=*/3,
/*num_merge_keys_sent_or_truncated_sample=*/1);
}
TEST_F(PrivateAggregationPendingContributionsTest,
ContributionsNotMergedIfConditonalOnUntriggeredEvent) {
PrivateAggregationPendingContributions pending_contributions(
kExampleMaxNumContributions, GetExampleHistogramSuffixes());
pending_contributions.AddConditionalContributions(
PAErrorEvent::kTooManyContributions,
{blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/1, /*value=*/2, /*filtering_id=*/3)});
pending_contributions.AddConditionalContributions(
PAErrorEvent::kEmptyReportDropped,
{blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/1, /*value=*/2, /*filtering_id=*/3)});
pending_contributions.MarkContributionsFinalized(
PrivateAggregationPendingContributions::TimeoutOrDisconnect::kTimeout);
EXPECT_EQ(pending_contributions.CompileFinalUnmergedContributions(
{}, PendingReportLimitResult::kNotAtLimit,
NullReportBehavior::kDontSendReport),
std::vector<blink::mojom::AggregatableReportHistogramContribution>(
{blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/1, /*value=*/2, /*filtering_id=*/3)}));
EXPECT_EQ(std::move(pending_contributions)
.TakeFinalContributions(AllApprovalVector(1)),
std::vector<blink::mojom::AggregatableReportHistogramContribution>(
{blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/1, /*value=*/2, /*filtering_id=*/3)}));
ValidateHistograms(GetExampleHistogramSuffixes(),
/*num_final_unmerged_contributions_sample=*/1,
/*num_merge_keys_sent_or_truncated_sample=*/1);
}
TEST_F(PrivateAggregationPendingContributionsTest,
ContributionsTruncatedAppropriately) {
for (size_t max_num_contributions : {1, 20, 100, 1000}) {
base::HistogramTester histogram_tester;
PrivateAggregationPendingContributions pending_contributions(
max_num_contributions, GetExampleHistogramSuffixes());
std::vector<blink::mojom::AggregatableReportHistogramContribution>
supplied_contributions_vector;
for (int i = 0; i < 1000; ++i) {
supplied_contributions_vector.emplace_back(
/*bucket=*/0, /*value=*/1, /*filtering_id=*/2);
supplied_contributions_vector.emplace_back(
/*bucket=*/1 + i, /*value=*/2, /*filtering_id=*/3);
}
std::vector<blink::mojom::AggregatableReportHistogramContribution>
expected_unmerged_contribution_vector;
for (size_t i = 0; i < 1000; ++i) {
expected_unmerged_contribution_vector.emplace_back(
/*bucket=*/0, /*value=*/1, /*filtering_id=*/2);
if (i < max_num_contributions - 1) {
expected_unmerged_contribution_vector.emplace_back(
/*bucket=*/1 + i, /*value=*/2, /*filtering_id=*/3);
}
}
std::vector<blink::mojom::AggregatableReportHistogramContribution>
expected_contribution_vector;
expected_contribution_vector.emplace_back(
/*bucket=*/0, /*value=*/1000, /*filtering_id=*/2);
for (size_t i = 0; i < max_num_contributions - 1; ++i) {
expected_contribution_vector.emplace_back(
/*bucket=*/1 + i, /*value=*/2, /*filtering_id=*/3);
}
pending_contributions.AddUnconditionalContributions(
std::move(supplied_contributions_vector));
pending_contributions.MarkContributionsFinalized(
PrivateAggregationPendingContributions::TimeoutOrDisconnect::kTimeout);
EXPECT_EQ(
pending_contributions.CompileFinalUnmergedContributions(
AllApprovalVector(2000), PendingReportLimitResult::kNotAtLimit,
NullReportBehavior::kDontSendReport),
expected_unmerged_contribution_vector);
EXPECT_EQ(std::move(pending_contributions)
.TakeFinalContributions(
AllApprovalVector(999 + max_num_contributions)),
expected_contribution_vector);
ValidateHistograms(
GetExampleHistogramSuffixes(),
/*num_final_unmerged_contributions_sample=*/999 + max_num_contributions,
/*num_merge_keys_sent_or_truncated_sample=*/1001,
/*truncation_result=*/
PrivateAggregationPendingContributions::TruncationResult::
kTruncationDueToUnconditionalContributions,
/*histogram_tester_override=*/&histogram_tester);
}
}
TEST_F(PrivateAggregationPendingContributionsTest,
UnconditionalContributionsPreferentiallyTruncated) {
PrivateAggregationPendingContributions pending_contributions(
kExampleMaxNumContributions, GetExampleHistogramSuffixes());
std::vector<blink::mojom::AggregatableReportHistogramContribution>
unconditional_contributions_vector;
for (int i = 0; i < 1000; ++i) {
unconditional_contributions_vector.emplace_back(
/*bucket=*/1 + i, /*value=*/2, /*filtering_id=*/3);
}
std::vector<blink::mojom::AggregatableReportHistogramContribution>
expected_contribution_vector;
expected_contribution_vector.emplace_back(
/*bucket=*/0, /*value=*/1, /*filtering_id=*/2);
for (size_t i = 0; i < kExampleMaxNumContributions - 1; ++i) {
expected_contribution_vector.emplace_back(
/*bucket=*/1 + i, /*value=*/2, /*filtering_id=*/3);
}
pending_contributions.AddUnconditionalContributions(
std::move(unconditional_contributions_vector));
pending_contributions.AddConditionalContributions(
PAErrorEvent::kTooManyContributions,
{blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/0, /*value=*/1, /*filtering_id=*/2)});
pending_contributions.MarkContributionsFinalized(
PrivateAggregationPendingContributions::TimeoutOrDisconnect::kTimeout);
EXPECT_EQ(pending_contributions.CompileFinalUnmergedContributions(
AllApprovalVector(1000), PendingReportLimitResult::kNotAtLimit,
NullReportBehavior::kDontSendReport),
expected_contribution_vector);
EXPECT_EQ(std::move(pending_contributions)
.TakeFinalContributions(
AllApprovalVector(kExampleMaxNumContributions)),
expected_contribution_vector);
ValidateHistograms(
GetExampleHistogramSuffixes(),
/*num_final_unmerged_contributions_sample=*/kExampleMaxNumContributions,
/*num_merge_keys_sent_or_truncated_sample=*/1000,
PrivateAggregationPendingContributions::TruncationResult::
kTruncationDueToUnconditionalContributions);
}
TEST_F(
PrivateAggregationPendingContributionsTest,
UnconditionalContributionsStillMergedIntoTruncatedConditionalContributions) {
PrivateAggregationPendingContributions pending_contributions(
kExampleMaxNumContributions, GetExampleHistogramSuffixes());
std::vector<blink::mojom::AggregatableReportHistogramContribution>
conditional_contributions_vector;
conditional_contributions_vector.emplace_back(
/*bucket=*/0, /*value=*/1, /*filtering_id=*/2);
for (int i = 0; i < 1000; ++i) {
conditional_contributions_vector.emplace_back(
/*bucket=*/1 + i, /*value=*/2, /*filtering_id=*/3);
}
std::vector<blink::mojom::AggregatableReportHistogramContribution>
expected_final_contribution_vector;
expected_final_contribution_vector.emplace_back(
/*bucket=*/0, /*value=*/2, /*filtering_id=*/2);
for (size_t i = 0; i < kExampleMaxNumContributions - 1; ++i) {
expected_final_contribution_vector.emplace_back(
/*bucket=*/1 + i, /*value=*/2, /*filtering_id=*/3);
}
std::vector<blink::mojom::AggregatableReportHistogramContribution>
expected_unmerged_contribution_vector = conditional_contributions_vector;
expected_unmerged_contribution_vector.resize(kExampleMaxNumContributions);
expected_unmerged_contribution_vector.emplace_back(
/*bucket=*/0, /*value=*/1, /*filtering_id=*/2);
pending_contributions.AddUnconditionalContributions(
{blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/0, /*value=*/1, /*filtering_id=*/2)});
pending_contributions.AddConditionalContributions(
PAErrorEvent::kContributionTimeoutReached,
conditional_contributions_vector);
pending_contributions.MarkContributionsFinalized(
PrivateAggregationPendingContributions::TimeoutOrDisconnect::kTimeout);
EXPECT_EQ(pending_contributions.CompileFinalUnmergedContributions(
AllApprovalVector(1), PendingReportLimitResult::kNotAtLimit,
NullReportBehavior::kDontSendReport),
expected_unmerged_contribution_vector);
EXPECT_EQ(std::move(pending_contributions)
.TakeFinalContributions(
AllApprovalVector(kExampleMaxNumContributions + 1)),
expected_final_contribution_vector);
ValidateHistograms(
GetExampleHistogramSuffixes(),
/*num_final_unmerged_contributions_sample=*/kExampleMaxNumContributions +
1,
/*num_merge_keys_sent_or_truncated_sample=*/1001,
PrivateAggregationPendingContributions::TruncationResult::
kTruncationNotDueToUnconditionalContributions);
}
TEST_F(PrivateAggregationPendingContributionsTest,
ZeroValueContributionsDropped) {
PrivateAggregationPendingContributions pending_contributions(
kExampleMaxNumContributions, GetExampleHistogramSuffixes());
pending_contributions.AddUnconditionalContributions(
{blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/1, /*value=*/2, /*filtering_id=*/3),
blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/4, /*value=*/0, /*filtering_id=*/5)});
pending_contributions.AddConditionalContributions(
PAErrorEvent::kContributionTimeoutReached,
{blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/6, /*value=*/0, /*filtering_id=*/7),
blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/8, /*value=*/9, /*filtering_id=*/10)});
pending_contributions.MarkContributionsFinalized(
PrivateAggregationPendingContributions::TimeoutOrDisconnect::kTimeout);
EXPECT_EQ(pending_contributions.CompileFinalUnmergedContributions(
AllApprovalVector(1), PendingReportLimitResult::kNotAtLimit,
NullReportBehavior::kDontSendReport),
std::vector<blink::mojom::AggregatableReportHistogramContribution>(
{blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/8, /*value=*/9, /*filtering_id=*/10),
blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/1, /*value=*/2, /*filtering_id=*/3)}));
EXPECT_EQ(std::move(pending_contributions)
.TakeFinalContributions(AllApprovalVector(2)),
std::vector<blink::mojom::AggregatableReportHistogramContribution>(
{blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/8, /*value=*/9, /*filtering_id=*/10),
blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/1, /*value=*/2, /*filtering_id=*/3)}));
ValidateHistograms(GetExampleHistogramSuffixes(),
/*num_final_unmerged_contributions_sample=*/2,
/*num_merge_keys_sent_or_truncated_sample=*/2);
}
TEST_F(PrivateAggregationPendingContributionsTest,
ReportSuccessTriggeredAppropriately) {
PrivateAggregationPendingContributions pending_contributions(
kExampleMaxNumContributions, GetExampleHistogramSuffixes());
// We only expect kReportSuccess to be triggered.
std::vector<blink::mojom::AggregatableReportHistogramContribution>
expected_contributions{
{/*bucket=*/static_cast<int>(PAErrorEvent::kReportSuccess),
/*value=*/1, /*filtering_id=*/0},
{/*bucket=*/0, /*value=*/1, /*filtering_id=*/1}};
MakeConditionalContributionForEachInternalError(pending_contributions);
// Avoids triggering empty report.
pending_contributions.AddUnconditionalContributions(
{{/*bucket=*/0, /*value=*/1, /*filtering_id=*/1}});
pending_contributions.MarkContributionsFinalized(
PrivateAggregationPendingContributions::TimeoutOrDisconnect::kDisconnect);
EXPECT_EQ(pending_contributions.CompileFinalUnmergedContributions(
AllApprovalVector(1), PendingReportLimitResult::kNotAtLimit,
NullReportBehavior::kDontSendReport),
expected_contributions);
EXPECT_EQ(std::move(pending_contributions)
.TakeFinalContributions(AllApprovalVector(2)),
expected_contributions);
ValidateHistograms(GetExampleHistogramSuffixes(),
/*num_final_unmerged_contributions_sample=*/2,
/*num_merge_keys_sent_or_truncated_sample=*/2);
}
TEST_F(PrivateAggregationPendingContributionsTest,
EmptyReportDroppedTriggeredAppropriately) {
PrivateAggregationPendingContributions pending_contributions(
kExampleMaxNumContributions, GetExampleHistogramSuffixes());
// We only expect kEmptyReportDropped to be triggered. Note that this is
// triggered based on the unconditional contributions only.
std::vector<blink::mojom::AggregatableReportHistogramContribution>
expected_contributions{
{/*bucket=*/static_cast<int>(PAErrorEvent::kEmptyReportDropped),
/*value=*/1, /*filtering_id=*/0}};
MakeConditionalContributionForEachInternalError(pending_contributions);
pending_contributions.MarkContributionsFinalized(
PrivateAggregationPendingContributions::TimeoutOrDisconnect::kDisconnect);
EXPECT_EQ(pending_contributions.CompileFinalUnmergedContributions(
{}, PendingReportLimitResult::kNotAtLimit,
NullReportBehavior::kDontSendReport),
expected_contributions);
EXPECT_EQ(std::move(pending_contributions)
.TakeFinalContributions(AllApprovalVector(1)),
expected_contributions);
ValidateHistograms(GetExampleHistogramSuffixes(),
/*num_final_unmerged_contributions_sample=*/1,
/*num_merge_keys_sent_or_truncated_sample=*/1);
}
TEST_F(PrivateAggregationPendingContributionsTest,
EmptyReportDroppedNotTriggeredForDeterministicReport) {
PrivateAggregationPendingContributions pending_contributions(
kExampleMaxNumContributions, GetExampleHistogramSuffixes());
// We only expect kReportSuccess to be triggered. Note that this is
// triggered based on the unconditional contributions only.
std::vector<blink::mojom::AggregatableReportHistogramContribution>
expected_contributions{
{/*bucket=*/static_cast<int>(PAErrorEvent::kReportSuccess),
/*value=*/1, /*filtering_id=*/0}};
MakeConditionalContributionForEachInternalError(pending_contributions);
pending_contributions.MarkContributionsFinalized(
PrivateAggregationPendingContributions::TimeoutOrDisconnect::kDisconnect);
EXPECT_EQ(pending_contributions.CompileFinalUnmergedContributions(
{}, PendingReportLimitResult::kNotAtLimit,
NullReportBehavior::kSendNullReport),
expected_contributions);
EXPECT_EQ(std::move(pending_contributions)
.TakeFinalContributions(AllApprovalVector(1)),
expected_contributions);
ValidateHistograms(GetExampleHistogramSuffixes(),
/*num_final_unmerged_contributions_sample=*/1,
/*num_merge_keys_sent_or_truncated_sample=*/1);
}
TEST_F(PrivateAggregationPendingContributionsTest,
PendingReportLimitReachedTriggeredAppropriately) {
PrivateAggregationPendingContributions pending_contributions(
kExampleMaxNumContributions, GetExampleHistogramSuffixes());
std::vector<blink::mojom::AggregatableReportHistogramContribution>
expected_contributions{
{/*bucket=*/static_cast<int>(PAErrorEvent::kEmptyReportDropped),
/*value=*/1, /*filtering_id=*/0},
{/*bucket=*/static_cast<int>(
PAErrorEvent::kPendingReportLimitReached),
/*value=*/1, /*filtering_id=*/0}};
MakeConditionalContributionForEachInternalError(pending_contributions);
pending_contributions.MarkContributionsFinalized(
PrivateAggregationPendingContributions::TimeoutOrDisconnect::kDisconnect);
EXPECT_EQ(pending_contributions.CompileFinalUnmergedContributions(
{}, PendingReportLimitResult::kAtLimit,
NullReportBehavior::kDontSendReport),
expected_contributions);
EXPECT_EQ(std::move(pending_contributions)
.TakeFinalContributions(AllApprovalVector(2)),
expected_contributions);
ValidateHistograms(GetExampleHistogramSuffixes(),
/*num_final_unmerged_contributions_sample=*/2,
/*num_merge_keys_sent_or_truncated_sample=*/2);
}
TEST_F(PrivateAggregationPendingContributionsTest,
AlreadyTriggeredNonInternalError) {
PrivateAggregationPendingContributions pending_contributions(
kExampleMaxNumContributions, GetExampleHistogramSuffixes());
std::vector<blink::mojom::AggregatableReportHistogramContribution>
expected_contributions{
{/*bucket=*/static_cast<int>(PAErrorEvent::kEmptyReportDropped),
/*value=*/1, /*filtering_id=*/0},
{/*bucket=*/static_cast<int>(
PAErrorEvent::kAlreadyTriggeredExternalError),
/*value=*/1, /*filtering_id=*/0}};
MakeConditionalContributionForEachInternalError(pending_contributions);
pending_contributions.AddConditionalContributions(
PAErrorEvent::kAlreadyTriggeredExternalError,
{{/*bucket=*/static_cast<int>(
PAErrorEvent::kAlreadyTriggeredExternalError),
/*value=*/1, /*filtering_id=*/0}});
pending_contributions.MarkContributionsFinalized(
PrivateAggregationPendingContributions::TimeoutOrDisconnect::kDisconnect);
EXPECT_EQ(pending_contributions.CompileFinalUnmergedContributions(
{}, PendingReportLimitResult::kNotAtLimit,
NullReportBehavior::kDontSendReport),
expected_contributions);
EXPECT_EQ(std::move(pending_contributions)
.TakeFinalContributions(AllApprovalVector(2)),
expected_contributions);
ValidateHistograms(GetExampleHistogramSuffixes(),
/*num_final_unmerged_contributions_sample=*/2,
/*num_merge_keys_sent_or_truncated_sample=*/2);
}
TEST_F(PrivateAggregationPendingContributionsTest,
AllContributionsDeniedInProvisionalBudgetTest) {
PrivateAggregationPendingContributions pending_contributions(
kExampleMaxNumContributions, GetExampleHistogramSuffixes());
std::vector<blink::mojom::AggregatableReportHistogramContribution>
expected_contributions{
{/*bucket=*/static_cast<int>(PAErrorEvent::kEmptyReportDropped),
/*value=*/1, /*filtering_id=*/0},
{/*bucket=*/static_cast<int>(PAErrorEvent::kInsufficientBudget),
/*value=*/1, /*filtering_id=*/0}};
MakeConditionalContributionForEachInternalError(pending_contributions);
pending_contributions.AddUnconditionalContributions(
{{/*bucket=*/123, /*value=*/45, /*filtering_id=*/6},
{/*bucket=*/456, /*value=*/78, /*filtering_id=*/9},
{/*bucket=*/789, /*value=*/12, /*filtering_id=*/3}});
pending_contributions.MarkContributionsFinalized(
PrivateAggregationPendingContributions::TimeoutOrDisconnect::kDisconnect);
EXPECT_EQ(pending_contributions.CompileFinalUnmergedContributions(
AllDenialVector(3), PendingReportLimitResult::kNotAtLimit,
NullReportBehavior::kDontSendReport),
expected_contributions);
EXPECT_EQ(std::move(pending_contributions)
.TakeFinalContributions(AllApprovalVector(2)),
expected_contributions);
// Note that contributions denied in the provisional budget query aren't
// included in these histograms.
ValidateHistograms(GetExampleHistogramSuffixes(),
/*num_final_unmerged_contributions_sample=*/2,
/*num_merge_keys_sent_or_truncated_sample=*/2);
}
TEST_F(PrivateAggregationPendingContributionsTest,
AllContributionsDeniedInFinalBudgetTest) {
PrivateAggregationPendingContributions pending_contributions(
kExampleMaxNumContributions, GetExampleHistogramSuffixes());
std::vector<blink::mojom::AggregatableReportHistogramContribution>
unconditional_contributions{
{/*bucket=*/123, /*value=*/45, /*filtering_id=*/6},
{/*bucket=*/456, /*value=*/78, /*filtering_id=*/9},
{/*bucket=*/789, /*value=*/12, /*filtering_id=*/3}};
MakeConditionalContributionForEachInternalError(pending_contributions);
pending_contributions.AddUnconditionalContributions(
unconditional_contributions);
pending_contributions.MarkContributionsFinalized(
PrivateAggregationPendingContributions::TimeoutOrDisconnect::kDisconnect);
std::vector<blink::mojom::AggregatableReportHistogramContribution>
expected_contributions = unconditional_contributions;
expected_contributions.insert(
expected_contributions.begin(),
{/*bucket=*/static_cast<int>(PAErrorEvent::kReportSuccess),
/*value=*/1, /*filtering_id=*/0});
EXPECT_EQ(pending_contributions.CompileFinalUnmergedContributions(
AllApprovalVector(3), PendingReportLimitResult::kNotAtLimit,
NullReportBehavior::kDontSendReport),
expected_contributions);
EXPECT_THAT(std::move(pending_contributions)
.TakeFinalContributions(AllDenialVector(4)),
testing::IsEmpty());
// Note that contributions denied in the final budget query are included in
// these histograms.
ValidateHistograms(GetExampleHistogramSuffixes(),
/*num_final_unmerged_contributions_sample=*/4,
/*num_merge_keys_sent_or_truncated_sample=*/4);
}
TEST_F(PrivateAggregationPendingContributionsTest,
AllContributionsDeniedInBothQueries) {
PrivateAggregationPendingContributions pending_contributions(
kExampleMaxNumContributions, GetExampleHistogramSuffixes());
std::vector<blink::mojom::AggregatableReportHistogramContribution>
expected_contributions{
{/*bucket=*/static_cast<int>(PAErrorEvent::kEmptyReportDropped),
/*value=*/1, /*filtering_id=*/0},
{/*bucket=*/static_cast<int>(PAErrorEvent::kInsufficientBudget),
/*value=*/1, /*filtering_id=*/0}};
MakeConditionalContributionForEachInternalError(pending_contributions);
pending_contributions.AddUnconditionalContributions(
{{/*bucket=*/123, /*value=*/45, /*filtering_id=*/6},
{/*bucket=*/456, /*value=*/78, /*filtering_id=*/9},
{/*bucket=*/789, /*value=*/12, /*filtering_id=*/3}});
pending_contributions.MarkContributionsFinalized(
PrivateAggregationPendingContributions::TimeoutOrDisconnect::kDisconnect);
EXPECT_EQ(pending_contributions.CompileFinalUnmergedContributions(
AllDenialVector(3), PendingReportLimitResult::kNotAtLimit,
NullReportBehavior::kDontSendReport),
expected_contributions);
EXPECT_THAT(std::move(pending_contributions)
.TakeFinalContributions(AllDenialVector(2)),
testing::IsEmpty());
ValidateHistograms(GetExampleHistogramSuffixes(),
/*num_final_unmerged_contributions_sample=*/2,
/*num_merge_keys_sent_or_truncated_sample=*/2);
}
TEST_F(PrivateAggregationPendingContributionsTest, SomeContributionsDenied) {
PrivateAggregationPendingContributions pending_contributions(
kExampleMaxNumContributions, GetExampleHistogramSuffixes());
std::vector<blink::mojom::AggregatableReportHistogramContribution>
expected_first_round_contributions{
{/*bucket=*/static_cast<int>(PAErrorEvent::kInsufficientBudget),
/*value=*/1, /*filtering_id=*/0},
{/*bucket=*/789, /*value=*/12, /*filtering_id=*/3}};
MakeConditionalContributionForEachInternalError(pending_contributions);
pending_contributions.AddUnconditionalContributions(
{{/*bucket=*/123, /*value=*/45, /*filtering_id=*/6},
{/*bucket=*/456, /*value=*/78, /*filtering_id=*/9},
{/*bucket=*/789, /*value=*/12, /*filtering_id=*/3}});
pending_contributions.MarkContributionsFinalized(
PrivateAggregationPendingContributions::TimeoutOrDisconnect::kDisconnect);
EXPECT_EQ(
pending_contributions.CompileFinalUnmergedContributions(
{PrivateAggregationPendingContributions::BudgeterResult::kDenied,
PrivateAggregationPendingContributions::BudgeterResult::kDenied,
PrivateAggregationPendingContributions::BudgeterResult::kApproved},
PendingReportLimitResult::kNotAtLimit,
NullReportBehavior::kDontSendReport),
expected_first_round_contributions);
EXPECT_EQ(
std::move(pending_contributions)
.TakeFinalContributions({PrivateAggregationPendingContributions::
BudgeterResult::kApproved,
PrivateAggregationPendingContributions::
BudgeterResult::kDenied}),
std::vector<blink::mojom::AggregatableReportHistogramContribution>(
{{/*bucket=*/static_cast<int>(PAErrorEvent::kInsufficientBudget),
/*value=*/1, /*filtering_id=*/0}}));
ValidateHistograms(GetExampleHistogramSuffixes(),
/*num_final_unmerged_contributions_sample=*/2,
/*num_merge_keys_sent_or_truncated_sample=*/2);
}
TEST_F(PrivateAggregationPendingContributionsTest,
MergeableContributionsDifferentBudgetingResults) {
PrivateAggregationPendingContributions pending_contributions(
kExampleMaxNumContributions, GetExampleHistogramSuffixes());
pending_contributions.AddUnconditionalContributions(
{{/*bucket=*/123, /*value=*/45, /*filtering_id=*/6},
{/*bucket=*/456, /*value=*/78, /*filtering_id=*/9},
{/*bucket=*/123, /*value=*/45, /*filtering_id=*/6},
{/*bucket=*/123, /*value=*/4, /*filtering_id=*/6}});
std::vector<blink::mojom::AggregatableReportHistogramContribution>
expected_first_round_contributions{
{/*bucket=*/123, /*value=*/45, /*filtering_id=*/6},
{/*bucket=*/456, /*value=*/78, /*filtering_id=*/9},
{/*bucket=*/123, /*value=*/4, /*filtering_id=*/6}};
std::vector<blink::mojom::AggregatableReportHistogramContribution>
expected_second_round_contributions{
{/*bucket=*/456, /*value=*/78, /*filtering_id=*/9},
{/*bucket=*/123, /*value=*/4, /*filtering_id=*/6}};
pending_contributions.MarkContributionsFinalized(
PrivateAggregationPendingContributions::TimeoutOrDisconnect::kDisconnect);
EXPECT_EQ(
pending_contributions.CompileFinalUnmergedContributions(
{PrivateAggregationPendingContributions::BudgeterResult::kApproved,
PrivateAggregationPendingContributions::BudgeterResult::kApproved,
PrivateAggregationPendingContributions::BudgeterResult::kDenied,
PrivateAggregationPendingContributions::BudgeterResult::kApproved},
PendingReportLimitResult::kNotAtLimit,
NullReportBehavior::kDontSendReport),
expected_first_round_contributions);
EXPECT_EQ(
std::move(pending_contributions)
.TakeFinalContributions(
{PrivateAggregationPendingContributions::BudgeterResult::kDenied,
PrivateAggregationPendingContributions::BudgeterResult::
kApproved,
PrivateAggregationPendingContributions::BudgeterResult::
kApproved}),
std::vector<blink::mojom::AggregatableReportHistogramContribution>(
expected_second_round_contributions));
ValidateHistograms(GetExampleHistogramSuffixes(),
/*num_final_unmerged_contributions_sample=*/3,
/*num_merge_keys_sent_or_truncated_sample=*/2);
}
TEST_F(PrivateAggregationPendingContributionsTest, OneHistogramSuffix) {
const std::vector<blink::mojom::AggregatableReportHistogramContribution>
contributions_vector = {
blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/1, /*value=*/2, /*filtering_id=*/3),
blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/4, /*value=*/5, /*filtering_id=*/std::nullopt)};
PrivateAggregationPendingContributions pending_contributions(
kExampleMaxNumContributions, {".SingleSuffix"});
pending_contributions.AddUnconditionalContributions(contributions_vector);
pending_contributions.MarkContributionsFinalized(
PrivateAggregationPendingContributions::TimeoutOrDisconnect::kTimeout);
EXPECT_EQ(pending_contributions.CompileFinalUnmergedContributions(
AllApprovalVector(2), PendingReportLimitResult::kNotAtLimit,
NullReportBehavior::kDontSendReport),
contributions_vector);
EXPECT_EQ(std::move(pending_contributions)
.TakeFinalContributions(AllApprovalVector(2)),
contributions_vector);
ValidateHistograms({".SingleSuffix"},
/*num_final_unmerged_contributions_sample=*/2,
/*num_merge_keys_sent_or_truncated_sample=*/2);
}
TEST_F(PrivateAggregationPendingContributionsTest, NoHistogramSuffixes) {
const std::vector<blink::mojom::AggregatableReportHistogramContribution>
contributions_vector = {
blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/1, /*value=*/2, /*filtering_id=*/3),
blink::mojom::AggregatableReportHistogramContribution(
/*bucket=*/4, /*value=*/5, /*filtering_id=*/std::nullopt)};
PrivateAggregationPendingContributions pending_contributions(
kExampleMaxNumContributions, {});
pending_contributions.AddUnconditionalContributions(contributions_vector);
pending_contributions.MarkContributionsFinalized(
PrivateAggregationPendingContributions::TimeoutOrDisconnect::kTimeout);
EXPECT_EQ(pending_contributions.CompileFinalUnmergedContributions(
AllApprovalVector(2), PendingReportLimitResult::kNotAtLimit,
NullReportBehavior::kDontSendReport),
contributions_vector);
EXPECT_EQ(std::move(pending_contributions)
.TakeFinalContributions(AllApprovalVector(2)),
contributions_vector);
ValidateHistograms({},
/*num_final_unmerged_contributions_sample=*/2,
/*num_merge_keys_sent_or_truncated_sample=*/2);
}
TEST_F(PrivateAggregationPendingContributionsTest,
UnmergedContributionsAtLimit) {
constexpr size_t kMaxUnmergedContributions = 10'000;
std::vector<blink::mojom::AggregatableReportHistogramContribution>
contributions_vector;
for (size_t i = 0; i < kMaxUnmergedContributions + 1; ++i) {
contributions_vector.emplace_back(/*bucket=*/1, /*value=*/1,
/*filtering_id=*/std::nullopt);
}
PrivateAggregationPendingContributions pending_contributions(
kExampleMaxNumContributions, {});
pending_contributions.AddUnconditionalContributions(contributions_vector);
pending_contributions.MarkContributionsFinalized(
PrivateAggregationPendingContributions::TimeoutOrDisconnect::kDisconnect);
EXPECT_EQ(pending_contributions
.CompileFinalUnmergedContributions(
AllApprovalVector(kMaxUnmergedContributions + 1),
PendingReportLimitResult::kNotAtLimit,
NullReportBehavior::kDontSendReport)
.size(),
kMaxUnmergedContributions);
EXPECT_EQ(
std::move(pending_contributions)
.TakeFinalContributions(AllApprovalVector(kMaxUnmergedContributions))
.size(),
1);
ValidateHistograms(
{},
/*num_final_unmerged_contributions_sample=*/kMaxUnmergedContributions,
/*num_merge_keys_sent_or_truncated_sample=*/1);
}
TEST_F(PrivateAggregationPendingContributionsTest, TruncatedMergeKeysAtLimit) {
constexpr size_t kMaxTruncatedMergeKeysTracked = 10'000;
std::vector<blink::mojom::AggregatableReportHistogramContribution>
contributions_vector;
for (size_t i = 0;
i < kExampleMaxNumContributions + kMaxTruncatedMergeKeysTracked + 1;
++i) {
contributions_vector.emplace_back(/*bucket=*/i, /*value=*/1,
/*filtering_id=*/std::nullopt);
}
PrivateAggregationPendingContributions pending_contributions(
kExampleMaxNumContributions, {});
pending_contributions.AddUnconditionalContributions(contributions_vector);
pending_contributions.MarkContributionsFinalized(
PrivateAggregationPendingContributions::TimeoutOrDisconnect::kDisconnect);
EXPECT_EQ(pending_contributions
.CompileFinalUnmergedContributions(
AllApprovalVector(kExampleMaxNumContributions +
kMaxTruncatedMergeKeysTracked + 1),
PendingReportLimitResult::kNotAtLimit,
NullReportBehavior::kDontSendReport)
.size(),
kExampleMaxNumContributions);
EXPECT_EQ(std::move(pending_contributions)
.TakeFinalContributions(
AllApprovalVector(kExampleMaxNumContributions))
.size(),
kExampleMaxNumContributions);
ValidateHistograms(
{},
/*num_final_unmerged_contributions_sample=*/kExampleMaxNumContributions,
/*num_merge_keys_sent_or_truncated_sample=*/kExampleMaxNumContributions +
kMaxTruncatedMergeKeysTracked,
PrivateAggregationPendingContributions::TruncationResult::
kTruncationDueToUnconditionalContributions);
}
} // namespace content