blob: dc7e1d69b2517d06fa1d32f0edd25b5f294ddf1d [file] [log] [blame]
// 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_test_utils.h"
#include <limits.h>
#include <algorithm>
#include <tuple>
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/task_runner_util.h"
#include "base/test/bind.h"
#include "content/browser/attribution_reporting/storable_trigger.h"
#include "url/gurl.h"
namespace content {
namespace {
using AttributionAllowedStatus =
::content::RateLimitTable::AttributionAllowedStatus;
using CreateReportStatus =
::content::ConversionStorage::CreateReportResult::Status;
const char kDefaultImpressionOrigin[] = "https://impression.test/";
const char kDefaultConversionOrigin[] = "https://sub.conversion.test/";
const char kDefaultConversionDestination[] = "https://conversion.test/";
const char kDefaultReportOrigin[] = "https://report.test/";
// Default expiry time for impressions for testing.
const int64_t kExpiryTime = 30;
} // namespace
bool ConversionDisallowingContentBrowserClient::
IsConversionMeasurementOperationAllowed(
content::BrowserContext* browser_context,
ConversionMeasurementOperation operation,
const url::Origin* impression_origin,
const url::Origin* conversion_origin,
const url::Origin* reporting_origin) {
return false;
}
ConfigurableConversionTestBrowserClient::
ConfigurableConversionTestBrowserClient() = default;
ConfigurableConversionTestBrowserClient::
~ConfigurableConversionTestBrowserClient() = default;
bool ConfigurableConversionTestBrowserClient::
IsConversionMeasurementOperationAllowed(
content::BrowserContext* browser_context,
ConversionMeasurementOperation operation,
const url::Origin* impression_origin,
const url::Origin* conversion_origin,
const url::Origin* reporting_origin) {
if (!!blocked_impression_origin_ != !!impression_origin ||
!!blocked_conversion_origin_ != !!conversion_origin ||
!!blocked_reporting_origin_ != !!reporting_origin) {
return true;
}
// Allow the operation if any rule doesn't match.
if ((impression_origin &&
*blocked_impression_origin_ != *impression_origin) ||
(conversion_origin &&
*blocked_conversion_origin_ != *conversion_origin) ||
(reporting_origin && *blocked_reporting_origin_ != *reporting_origin)) {
return true;
}
return false;
}
void ConfigurableConversionTestBrowserClient::
BlockConversionMeasurementInContext(
absl::optional<url::Origin> impression_origin,
absl::optional<url::Origin> conversion_origin,
absl::optional<url::Origin> reporting_origin) {
blocked_impression_origin_ = impression_origin;
blocked_conversion_origin_ = conversion_origin;
blocked_reporting_origin_ = reporting_origin;
}
ConfigurableStorageDelegate::ConfigurableStorageDelegate() = default;
ConfigurableStorageDelegate::~ConfigurableStorageDelegate() = default;
base::Time ConfigurableStorageDelegate::GetReportTime(
const StorableSource& impression,
base::Time conversion_time) const {
return impression.impression_time() + base::Milliseconds(report_time_ms_);
}
int ConfigurableStorageDelegate::GetMaxConversionsPerImpression(
StorableSource::SourceType source_type) const {
return max_conversions_per_impression_;
}
int ConfigurableStorageDelegate::GetMaxImpressionsPerOrigin() const {
return max_impressions_per_origin_;
}
int ConfigurableStorageDelegate::GetMaxConversionsPerOrigin() const {
return max_conversions_per_origin_;
}
int ConfigurableStorageDelegate::GetMaxAttributionDestinationsPerEventSource()
const {
return max_attribution_destinations_per_event_source_;
}
ConversionStorage::Delegate::RateLimitConfig
ConfigurableStorageDelegate::GetRateLimits(
ConversionStorage::AttributionType attribution_type) const {
return rate_limits_;
}
uint64_t ConfigurableStorageDelegate::GetFakeEventSourceTriggerData() const {
return fake_event_source_trigger_data_;
}
base::TimeDelta
ConfigurableStorageDelegate::GetDeleteExpiredImpressionsFrequency() const {
return delete_expired_impressions_frequency_;
}
base::TimeDelta
ConfigurableStorageDelegate::GetDeleteExpiredRateLimitsFrequency() const {
return delete_expired_rate_limits_frequency_;
}
ConversionManager* TestManagerProvider::GetManager(
WebContents* web_contents) const {
return manager_;
}
TestConversionManager::TestConversionManager() = default;
TestConversionManager::~TestConversionManager() = default;
void TestConversionManager::HandleImpression(StorableSource impression) {
num_impressions_++;
last_impression_source_type_ = impression.source_type();
last_impression_origin_ = impression.impression_origin();
last_attribution_source_priority_ = impression.priority();
}
void TestConversionManager::HandleConversion(StorableTrigger conversion) {
num_conversions_++;
last_conversion_destination_ = conversion.conversion_destination();
}
void TestConversionManager::GetActiveImpressionsForWebUI(
base::OnceCallback<void(std::vector<StorableSource>)> callback) {
std::move(callback).Run(impressions_);
}
void TestConversionManager::GetPendingReportsForWebUI(
base::OnceCallback<void(std::vector<AttributionReport>)> callback,
base::Time max_report_time) {
std::move(callback).Run(reports_);
}
const ConversionSessionStorage& TestConversionManager::GetSessionStorage()
const {
return session_storage_;
}
void TestConversionManager::SendReportsForWebUI(base::OnceClosure done) {
reports_.clear();
std::move(done).Run();
}
ConversionSessionStorage& TestConversionManager::GetSessionStorage() {
return session_storage_;
}
const ConversionPolicy& TestConversionManager::GetConversionPolicy() const {
return policy_;
}
void TestConversionManager::ClearData(
base::Time delete_begin,
base::Time delete_end,
base::RepeatingCallback<bool(const url::Origin&)> filter,
base::OnceClosure done) {
impressions_.clear();
reports_.clear();
session_storage_.Reset();
std::move(done).Run();
}
void TestConversionManager::SetActiveImpressionsForWebUI(
std::vector<StorableSource> impressions) {
impressions_ = std::move(impressions);
}
void TestConversionManager::SetReportsForWebUI(
std::vector<AttributionReport> reports) {
reports_ = std::move(reports);
}
void TestConversionManager::Reset() {
num_impressions_ = 0u;
num_conversions_ = 0u;
}
// Builds an impression with default values. This is done as a builder because
// all values needed to be provided at construction time.
ImpressionBuilder::ImpressionBuilder(base::Time time)
: impression_data_(123),
impression_time_(time),
expiry_(base::Milliseconds(kExpiryTime)),
impression_origin_(url::Origin::Create(GURL(kDefaultImpressionOrigin))),
conversion_origin_(url::Origin::Create(GURL(kDefaultConversionOrigin))),
reporting_origin_(url::Origin::Create(GURL(kDefaultReportOrigin))),
source_type_(StorableSource::SourceType::kNavigation),
priority_(0),
attribution_logic_(StorableSource::AttributionLogic::kTruthfully) {}
ImpressionBuilder::~ImpressionBuilder() = default;
ImpressionBuilder& ImpressionBuilder::SetExpiry(base::TimeDelta delta) {
expiry_ = delta;
return *this;
}
ImpressionBuilder& ImpressionBuilder::SetData(uint64_t data) {
impression_data_ = data;
return *this;
}
ImpressionBuilder& ImpressionBuilder::SetImpressionOrigin(url::Origin origin) {
impression_origin_ = std::move(origin);
return *this;
}
ImpressionBuilder& ImpressionBuilder::SetConversionOrigin(url::Origin origin) {
conversion_origin_ = std::move(origin);
return *this;
}
ImpressionBuilder& ImpressionBuilder::SetReportingOrigin(url::Origin origin) {
reporting_origin_ = std::move(origin);
return *this;
}
ImpressionBuilder& ImpressionBuilder::SetSourceType(
StorableSource::SourceType source_type) {
source_type_ = source_type;
return *this;
}
ImpressionBuilder& ImpressionBuilder::SetPriority(int64_t priority) {
priority_ = priority;
return *this;
}
ImpressionBuilder& ImpressionBuilder::SetAttributionLogic(
StorableSource::AttributionLogic attribution_logic) {
attribution_logic_ = attribution_logic;
return *this;
}
ImpressionBuilder& ImpressionBuilder::SetImpressionId(
absl::optional<StorableSource::Id> impression_id) {
impression_id_ = impression_id;
return *this;
}
ImpressionBuilder& ImpressionBuilder::SetDedupKeys(
std::vector<int64_t> dedup_keys) {
dedup_keys_ = std::move(dedup_keys);
return *this;
}
StorableSource ImpressionBuilder::Build() const {
StorableSource impression(
impression_data_, impression_origin_, conversion_origin_,
reporting_origin_, impression_time_,
/*expiry_time=*/impression_time_ + expiry_, source_type_, priority_,
attribution_logic_, impression_id_);
impression.SetDedupKeys(dedup_keys_);
return impression;
}
StorableTrigger DefaultConversion() {
return ConversionBuilder().Build();
}
ConversionBuilder::ConversionBuilder()
: conversion_destination_(
net::SchemefulSite(GURL(kDefaultConversionDestination))),
reporting_origin_(url::Origin::Create(GURL(kDefaultReportOrigin))) {}
ConversionBuilder::~ConversionBuilder() = default;
ConversionBuilder& ConversionBuilder::SetConversionData(
uint64_t conversion_data) {
conversion_data_ = conversion_data;
return *this;
}
ConversionBuilder& ConversionBuilder::SetEventSourceTriggerData(
uint64_t event_source_trigger_data) {
event_source_trigger_data_ = event_source_trigger_data;
return *this;
}
ConversionBuilder& ConversionBuilder::SetConversionDestination(
net::SchemefulSite conversion_destination) {
conversion_destination_ = std::move(conversion_destination);
return *this;
}
ConversionBuilder& ConversionBuilder::SetReportingOrigin(
url::Origin reporting_origin) {
reporting_origin_ = std::move(reporting_origin);
return *this;
}
ConversionBuilder& ConversionBuilder::SetPriority(int64_t priority) {
priority_ = priority;
return *this;
}
ConversionBuilder& ConversionBuilder::SetDedupKey(
absl::optional<int64_t> dedup_key) {
dedup_key_ = dedup_key;
return *this;
}
StorableTrigger ConversionBuilder::Build() const {
return StorableTrigger(conversion_data_, conversion_destination_,
reporting_origin_, event_source_trigger_data_,
priority_, dedup_key_);
}
// Custom comparator for `StorableSource` that does not take impression IDs
// or dedup keys into account.
bool operator==(const StorableSource& a, const StorableSource& b) {
const auto tie = [](const StorableSource& impression) {
return std::make_tuple(
impression.impression_data(), impression.impression_origin(),
impression.conversion_origin(), impression.reporting_origin(),
impression.impression_time(), impression.expiry_time(),
impression.source_type(), impression.priority(),
impression.attribution_logic());
};
return tie(a) == tie(b);
}
// Custom comparator for comparing two vectors of conversion reports. Does not
// compare impression and conversion IDs as they are set by the underlying
// sqlite db and should not be tested.
bool operator==(const AttributionReport& a, const AttributionReport& b) {
const auto tie = [](const AttributionReport& conversion) {
return std::make_tuple(conversion.impression, conversion.conversion_data,
conversion.conversion_time, conversion.report_time,
conversion.priority,
conversion.failed_send_attempts);
};
return tie(a) == tie(b);
}
bool operator==(const SentReportInfo& a, const SentReportInfo& b) {
const auto tie = [](const SentReportInfo& info) {
return std::make_tuple(info.report, info.status, info.http_response_code);
};
return tie(a) == tie(b);
}
std::ostream& operator<<(std::ostream& out, CreateReportStatus status) {
switch (status) {
case CreateReportStatus::kSuccess:
out << "kSuccess";
break;
case CreateReportStatus::kSuccessDroppedLowerPriority:
out << "kSuccessDroppedLowerPriority";
break;
case CreateReportStatus::kInternalError:
out << "kInternalError";
break;
case CreateReportStatus::kNoCapacityForConversionDestination:
out << "kNoCapacityForConversionDestination";
break;
case CreateReportStatus::kNoMatchingImpressions:
out << "kNoMatchingImpressions";
break;
case CreateReportStatus::kDeduplicated:
out << "kDeduplicated";
break;
case CreateReportStatus::kRateLimited:
out << "kRateLimited";
break;
case CreateReportStatus::kPriorityTooLow:
out << "kPriorityTooLow";
break;
case CreateReportStatus::kDroppedForNoise:
out << "kDroppedForNoise";
break;
}
return out;
}
std::ostream& operator<<(std::ostream& out, AttributionAllowedStatus status) {
switch (status) {
case AttributionAllowedStatus::kAllowed:
out << "kAllowed";
break;
case AttributionAllowedStatus::kNotAllowed:
out << "kNotAllowed";
break;
case AttributionAllowedStatus::kError:
out << "kError";
break;
}
return out;
}
std::ostream& operator<<(std::ostream& out,
StorableSource::SourceType source_type) {
switch (source_type) {
case StorableSource::SourceType::kNavigation:
out << "kNavigation";
break;
case StorableSource::SourceType::kEvent:
out << "kEvent";
break;
}
return out;
}
std::ostream& operator<<(std::ostream& out,
StorableSource::AttributionLogic attribution_logic) {
switch (attribution_logic) {
case StorableSource::AttributionLogic::kNever:
out << "kNever";
break;
case StorableSource::AttributionLogic::kTruthfully:
out << "kTruthfully";
break;
case StorableSource::AttributionLogic::kFalsely:
out << "kFalsely";
break;
}
return out;
}
std::ostream& operator<<(std::ostream& out, const StorableTrigger& conversion) {
return out << "{conversion_data=" << conversion.conversion_data()
<< ",conversion_destination="
<< conversion.conversion_destination().Serialize()
<< ",reporting_origin=" << conversion.reporting_origin()
<< ",event_source_trigger_data="
<< conversion.event_source_trigger_data()
<< ",priority=" << conversion.priority() << ",dedup_key="
<< (conversion.dedup_key()
? base::NumberToString(*conversion.dedup_key())
: "null")
<< "}";
}
std::ostream& operator<<(std::ostream& out, const StorableSource& impression) {
out << "{impression_data=" << impression.impression_data()
<< ",impression_origin=" << impression.impression_origin()
<< ",conversion_origin=" << impression.conversion_origin()
<< ",reporting_origin=" << impression.reporting_origin()
<< ",impression_time=" << impression.impression_time()
<< ",expiry_time=" << impression.expiry_time()
<< ",source_type=" << impression.source_type()
<< ",priority=" << impression.priority() << ",impression_id="
<< (impression.impression_id()
? base::NumberToString(**impression.impression_id())
: "null")
<< ",dedup_keys=[";
const char* separator = "";
for (int64_t dedup_key : impression.dedup_keys()) {
out << separator << dedup_key;
separator = ", ";
}
return out << "]}";
}
std::ostream& operator<<(std::ostream& out, const AttributionReport& report) {
return out << "{impression=" << report.impression
<< ",conversion_data=" << report.conversion_data
<< ",conversion_time=" << report.conversion_time
<< ",report_time=" << report.report_time
<< ",priority=" << report.priority << ",conversion_id="
<< (report.conversion_id
? base::NumberToString(**report.conversion_id)
: "null")
<< ",failed_send_attempts=" << report.failed_send_attempts << "}";
}
std::ostream& operator<<(std::ostream& out, SentReportInfo::Status status) {
switch (status) {
case SentReportInfo::Status::kSent:
out << "kSent";
break;
case SentReportInfo::Status::kTransientFailure:
out << "kTransientFailure";
break;
case SentReportInfo::Status::kFailure:
out << "kFailure";
break;
case SentReportInfo::Status::kDropped:
out << "kDropped";
break;
case SentReportInfo::Status::kOffline:
out << "kOffline";
break;
case SentReportInfo::Status::kRemovedFromQueue:
out << "kRemovedFromQueue";
break;
}
return out;
}
std::ostream& operator<<(std::ostream& out, const SentReportInfo& info) {
return out << "{report=" << info.report << ",status=" << info.status
<< ",http_response_code=" << info.http_response_code << "}";
}
std::vector<AttributionReport> GetConversionsToReportForTesting(
ConversionManagerImpl* manager,
base::Time max_report_time) {
base::RunLoop run_loop;
std::vector<AttributionReport> conversion_reports;
manager->conversion_storage_
.AsyncCall(&ConversionStorage::GetConversionsToReport)
.WithArgs(max_report_time, /*limit=*/-1)
.Then(base::BindOnce(base::BindLambdaForTesting(
[&](std::vector<AttributionReport> reports) {
conversion_reports = std::move(reports);
run_loop.Quit();
})));
run_loop.Run();
return conversion_reports;
}
} // namespace content