| // 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 "content/browser/attribution_reporting/conversion_policy.h" |
| |
| #include <memory> |
| #include <vector> |
| |
| #include "base/time/time.h" |
| #include "content/browser/attribution_reporting/conversion_test_utils.h" |
| #include "content/browser/attribution_reporting/storable_source.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| const StorableSource::SourceType kSourceTypes[] = { |
| StorableSource::SourceType::kNavigation, |
| StorableSource::SourceType::kEvent, |
| }; |
| |
| class ConfigurableConversionPolicy : public ConversionPolicy { |
| public: |
| explicit ConfigurableConversionPolicy(bool should_noise) |
| : should_noise_(should_noise) {} |
| |
| protected: |
| bool ShouldNoiseConversionData() const override { return should_noise_; } |
| |
| uint64_t MakeNoisedConversionData(uint64_t max) const override { return 1; } |
| |
| private: |
| bool should_noise_; |
| }; |
| |
| class ConversionPolicyTest : public testing::Test { |
| public: |
| ConversionPolicyTest() = default; |
| }; |
| |
| } // namespace |
| |
| TEST_F(ConversionPolicyTest, HighEntropyConversionData_StrippedToLowerBits) { |
| std::unique_ptr<ConversionPolicy> policy = |
| std::make_unique<ConfigurableConversionPolicy>(/*should_noise=*/false); |
| |
| EXPECT_EQ(0u, policy->GetSanitizedConversionData( |
| 8, StorableSource::SourceType::kNavigation)); |
| EXPECT_EQ(1u, policy->GetSanitizedConversionData( |
| 9, StorableSource::SourceType::kNavigation)); |
| |
| EXPECT_EQ(0u, policy->GetSanitizedConversionData( |
| 2, StorableSource::SourceType::kEvent)); |
| EXPECT_EQ(1u, policy->GetSanitizedConversionData( |
| 3, StorableSource::SourceType::kEvent)); |
| } |
| |
| TEST_F(ConversionPolicyTest, SanitizeHighEntropyImpressionData_Unchanged) { |
| uint64_t impression_data = 256LU; |
| |
| // The policy should not alter the impression data, and return the base 10 |
| // representation. |
| EXPECT_EQ(256LU, |
| ConversionPolicy().GetSanitizedImpressionData(impression_data)); |
| } |
| |
| TEST_F(ConversionPolicyTest, LowEntropyConversionData_Unchanged) { |
| std::unique_ptr<ConversionPolicy> policy = |
| std::make_unique<ConfigurableConversionPolicy>(/*should_noise=*/false); |
| |
| for (uint64_t conversion_data = 0; conversion_data < 8; conversion_data++) { |
| EXPECT_EQ(conversion_data, |
| policy->GetSanitizedConversionData( |
| conversion_data, StorableSource::SourceType::kNavigation)); |
| } |
| for (uint64_t conversion_data = 0; conversion_data < 2; conversion_data++) { |
| EXPECT_EQ(conversion_data, |
| policy->GetSanitizedConversionData( |
| conversion_data, StorableSource::SourceType::kEvent)); |
| } |
| } |
| |
| TEST_F(ConversionPolicyTest, SanitizeConversionData_OutputHasNoise) { |
| // The policy should include noise when sanitizing data. |
| for (auto source_type : kSourceTypes) { |
| EXPECT_EQ(1LU, ConfigurableConversionPolicy(/*should_noise=*/true) |
| .GetSanitizedConversionData(0UL, source_type)); |
| } |
| } |
| |
| // This test will fail flakily if noise is used. |
| TEST_F(ConversionPolicyTest, DebugMode_ConversionDataNotNoised) { |
| const uint64_t conversion_data = 0UL; |
| for (auto source_type : kSourceTypes) { |
| for (int i = 0; i < 100; i++) { |
| EXPECT_EQ(conversion_data, |
| ConversionPolicy(/*debug_mode=*/true) |
| .GetSanitizedConversionData(conversion_data, source_type)); |
| } |
| } |
| } |
| |
| TEST_F(ConversionPolicyTest, NoExpiryForImpression_DefaultUsed) { |
| const base::Time impression_time = base::Time::Now(); |
| |
| for (auto source_type : kSourceTypes) { |
| EXPECT_EQ( |
| impression_time + base::Days(30), |
| ConversionPolicy().GetExpiryTimeForImpression( |
| /*declared_expiry=*/absl::nullopt, impression_time, source_type)); |
| } |
| } |
| |
| TEST_F(ConversionPolicyTest, LargeImpressionExpirySpecified_ClampedTo30Days) { |
| constexpr base::TimeDelta declared_expiry = base::Days(60); |
| const base::Time impression_time = base::Time::Now(); |
| |
| for (auto source_type : kSourceTypes) { |
| EXPECT_EQ(impression_time + base::Days(30), |
| ConversionPolicy().GetExpiryTimeForImpression( |
| declared_expiry, impression_time, source_type)); |
| } |
| } |
| |
| TEST_F(ConversionPolicyTest, SmallImpressionExpirySpecified_ClampedTo1Day) { |
| const struct { |
| base::TimeDelta declared_expiry; |
| base::TimeDelta want_expiry; |
| } kTestCases[] = { |
| {base::Days(-1), base::Days(1)}, |
| {base::Days(0), base::Days(1)}, |
| {base::Days(1) - base::Milliseconds(1), base::Days(1)}, |
| }; |
| |
| const base::Time impression_time = base::Time::Now(); |
| |
| for (auto source_type : kSourceTypes) { |
| for (const auto& test_case : kTestCases) { |
| EXPECT_EQ(impression_time + test_case.want_expiry, |
| ConversionPolicy().GetExpiryTimeForImpression( |
| test_case.declared_expiry, impression_time, source_type)); |
| } |
| } |
| } |
| |
| TEST_F(ConversionPolicyTest, NonWholeDayImpressionExpirySpecified_Rounded) { |
| const struct { |
| StorableSource::SourceType source_type; |
| base::TimeDelta declared_expiry; |
| base::TimeDelta want_expiry; |
| } kTestCases[] = { |
| {StorableSource::SourceType::kNavigation, base::Hours(36), |
| base::Hours(36)}, |
| {StorableSource::SourceType::kEvent, base::Hours(36), base::Days(2)}, |
| |
| {StorableSource::SourceType::kNavigation, |
| base::Days(1) + base::Milliseconds(1), |
| base::Days(1) + base::Milliseconds(1)}, |
| {StorableSource::SourceType::kEvent, |
| base::Days(1) + base::Milliseconds(1), base::Days(1)}, |
| }; |
| |
| const base::Time impression_time = base::Time::Now(); |
| |
| for (const auto& test_case : kTestCases) { |
| EXPECT_EQ( |
| impression_time + test_case.want_expiry, |
| ConversionPolicy().GetExpiryTimeForImpression( |
| test_case.declared_expiry, impression_time, test_case.source_type)); |
| } |
| } |
| |
| TEST_F(ConversionPolicyTest, ImpressionExpirySpecified_ExpiryOverrideDefault) { |
| constexpr base::TimeDelta declared_expiry = base::Days(10); |
| const base::Time impression_time = base::Time::Now(); |
| |
| for (auto source_type : kSourceTypes) { |
| EXPECT_EQ(impression_time + base::Days(10), |
| ConversionPolicy().GetExpiryTimeForImpression( |
| declared_expiry, impression_time, source_type)); |
| } |
| } |
| |
| TEST_F(ConversionPolicyTest, GetFailedReportDelay) { |
| const struct { |
| int failed_send_attempts; |
| absl::optional<base::TimeDelta> expected; |
| } kTestCases[] = { |
| {1, base::Minutes(5)}, |
| {2, base::Minutes(15)}, |
| {3, absl::nullopt}, |
| }; |
| |
| for (const auto& test_case : kTestCases) { |
| EXPECT_EQ(test_case.expected, ConversionPolicy().GetFailedReportDelay( |
| test_case.failed_send_attempts)) |
| << "failed_send_attempts=" << test_case.failed_send_attempts; |
| } |
| } |
| |
| } // namespace content |