blob: 9ddd95d1d538d6970d5720a2e59209da552f7163 [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/attribution_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/task_runner_util.h"
#include "base/test/bind.h"
#include "url/gurl.h"
namespace content {
namespace {
using AttributionAllowedStatus =
::content::RateLimitTable::AttributionAllowedStatus;
using CreateReportStatus =
::content::AttributionStorage::CreateReportResult::Status;
using DeactivatedSource = ::content::AttributionStorage::DeactivatedSource;
const char kDefaultImpressionOrigin[] = "https://impression.test/";
const char kDefaultTriggerOrigin[] = "https://sub.conversion.test/";
const char kDefaultTriggerDestination[] = "https://conversion.test/";
const char kDefaultReportOrigin[] = "https://report.test/";
// Default expiry time for impressions for testing.
const int64_t kExpiryTime = 30;
} // namespace
MockAttributionReportingContentBrowserClient::
MockAttributionReportingContentBrowserClient() = default;
MockAttributionReportingContentBrowserClient::
~MockAttributionReportingContentBrowserClient() = default;
base::GUID DefaultExternalReportID() {
return base::GUID::ParseLowercase("21abd97f-73e8-4b88-9389-a9fee6abda5e");
}
ConfigurableStorageDelegate::ConfigurableStorageDelegate() = default;
ConfigurableStorageDelegate::~ConfigurableStorageDelegate() = default;
base::Time ConfigurableStorageDelegate::GetReportTime(
const StorableSource& source,
base::Time trigger_time) const {
return source.impression_time() + base::Milliseconds(report_time_ms_);
}
int ConfigurableStorageDelegate::GetMaxAttributionsPerSource(
StorableSource::SourceType source_type) const {
return max_attributions_per_source_;
}
int ConfigurableStorageDelegate::GetMaxSourcesPerOrigin() const {
return max_sources_per_origin_;
}
int ConfigurableStorageDelegate::GetMaxAttributionsPerOrigin() const {
return max_attributions_per_origin_;
}
int ConfigurableStorageDelegate::GetMaxAttributionDestinationsPerEventSource()
const {
return max_attribution_destinations_per_event_source_;
}
AttributionStorage::Delegate::RateLimitConfig
ConfigurableStorageDelegate::GetRateLimits(
AttributionStorage::AttributionType attribution_type) const {
return rate_limits_;
}
uint64_t ConfigurableStorageDelegate::GetFakeEventSourceTriggerData() const {
return fake_event_source_trigger_data_;
}
base::TimeDelta ConfigurableStorageDelegate::GetDeleteExpiredSourcesFrequency()
const {
return delete_expired_sources_frequency_;
}
base::TimeDelta
ConfigurableStorageDelegate::GetDeleteExpiredRateLimitsFrequency() const {
return delete_expired_rate_limits_frequency_;
}
base::GUID ConfigurableStorageDelegate::NewReportID() const {
return DefaultExternalReportID();
}
AttributionManager* TestManagerProvider::GetManager(
WebContents* web_contents) const {
return manager_;
}
TestAttributionManager::TestAttributionManager() = default;
TestAttributionManager::~TestAttributionManager() = default;
void TestAttributionManager::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void TestAttributionManager::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
void TestAttributionManager::HandleSource(StorableSource source) {
handled_sources_.push_back(std::move(source));
}
void TestAttributionManager::HandleTrigger(StorableTrigger trigger) {
handled_triggers_.push_back(std::move(trigger));
}
void TestAttributionManager::GetActiveSourcesForWebUI(
base::OnceCallback<void(std::vector<StorableSource>)> callback) {
std::move(callback).Run(sources_);
}
void TestAttributionManager::GetPendingReportsForWebUI(
base::OnceCallback<void(std::vector<AttributionReport>)> callback) {
std::move(callback).Run(reports_);
}
void TestAttributionManager::SendReportsForWebUI(base::OnceClosure done) {
reports_.clear();
std::move(done).Run();
}
const AttributionPolicy& TestAttributionManager::GetAttributionPolicy() const {
return policy_;
}
void TestAttributionManager::ClearData(
base::Time delete_begin,
base::Time delete_end,
base::RepeatingCallback<bool(const url::Origin&)> filter,
base::OnceClosure done) {
sources_.clear();
reports_.clear();
std::move(done).Run();
}
void TestAttributionManager::SetActiveSourcesForWebUI(
std::vector<StorableSource> sources) {
sources_ = std::move(sources);
}
void TestAttributionManager::SetReportsForWebUI(
std::vector<AttributionReport> reports) {
reports_ = std::move(reports);
}
void TestAttributionManager::NotifySourcesChanged() {
for (Observer& observer : observers_)
observer.OnSourcesChanged();
}
void TestAttributionManager::NotifyReportsChanged() {
for (Observer& observer : observers_)
observer.OnReportsChanged();
}
void TestAttributionManager::NotifySourceDeactivated(
const DeactivatedSource& source) {
for (Observer& observer : observers_)
observer.OnSourceDeactivated(source);
}
void TestAttributionManager::NotifyReportSent(const SentReportInfo& info) {
for (Observer& observer : observers_)
observer.OnReportSent(info);
}
void TestAttributionManager::NotifyReportDropped(
const AttributionStorage::CreateReportResult& result) {
for (Observer& observer : observers_)
observer.OnReportDropped(result);
}
void TestAttributionManager::Reset() {
handled_sources_.clear();
handled_triggers_.clear();
}
// Builds an impression with default values. This is done as a builder because
// all values needed to be provided at construction time.
SourceBuilder::SourceBuilder(base::Time time)
: impression_time_(time),
expiry_(base::Milliseconds(kExpiryTime)),
impression_origin_(url::Origin::Create(GURL(kDefaultImpressionOrigin))),
conversion_origin_(url::Origin::Create(GURL(kDefaultTriggerOrigin))),
reporting_origin_(url::Origin::Create(GURL(kDefaultReportOrigin))) {}
SourceBuilder::~SourceBuilder() = default;
SourceBuilder& SourceBuilder::SetExpiry(base::TimeDelta delta) {
expiry_ = delta;
return *this;
}
SourceBuilder& SourceBuilder::SetSourceEventId(uint64_t source_event_id) {
source_event_id_ = source_event_id;
return *this;
}
SourceBuilder& SourceBuilder::SetImpressionOrigin(url::Origin origin) {
impression_origin_ = std::move(origin);
return *this;
}
SourceBuilder& SourceBuilder::SetConversionOrigin(url::Origin origin) {
conversion_origin_ = std::move(origin);
return *this;
}
SourceBuilder& SourceBuilder::SetReportingOrigin(url::Origin origin) {
reporting_origin_ = std::move(origin);
return *this;
}
SourceBuilder& SourceBuilder::SetSourceType(
StorableSource::SourceType source_type) {
source_type_ = source_type;
return *this;
}
SourceBuilder& SourceBuilder::SetPriority(int64_t priority) {
priority_ = priority;
return *this;
}
SourceBuilder& SourceBuilder::SetAttributionLogic(
StorableSource::AttributionLogic attribution_logic) {
attribution_logic_ = attribution_logic;
return *this;
}
SourceBuilder& SourceBuilder::SetImpressionId(
absl::optional<StorableSource::Id> impression_id) {
impression_id_ = impression_id;
return *this;
}
SourceBuilder& SourceBuilder::SetDedupKeys(std::vector<int64_t> dedup_keys) {
dedup_keys_ = std::move(dedup_keys);
return *this;
}
StorableSource SourceBuilder::Build() const {
StorableSource impression(
source_event_id_, 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 DefaultTrigger() {
return TriggerBuilder().Build();
}
TriggerBuilder::TriggerBuilder()
: conversion_destination_(
net::SchemefulSite(GURL(kDefaultTriggerDestination))),
reporting_origin_(url::Origin::Create(GURL(kDefaultReportOrigin))) {}
TriggerBuilder::~TriggerBuilder() = default;
TriggerBuilder& TriggerBuilder::SetTriggerData(uint64_t trigger_data) {
trigger_data_ = trigger_data;
return *this;
}
TriggerBuilder& TriggerBuilder::SetEventSourceTriggerData(
uint64_t event_source_trigger_data) {
event_source_trigger_data_ = event_source_trigger_data;
return *this;
}
TriggerBuilder& TriggerBuilder::SetConversionDestination(
net::SchemefulSite conversion_destination) {
conversion_destination_ = std::move(conversion_destination);
return *this;
}
TriggerBuilder& TriggerBuilder::SetReportingOrigin(
url::Origin reporting_origin) {
reporting_origin_ = std::move(reporting_origin);
return *this;
}
TriggerBuilder& TriggerBuilder::SetPriority(int64_t priority) {
priority_ = priority;
return *this;
}
TriggerBuilder& TriggerBuilder::SetDedupKey(absl::optional<int64_t> dedup_key) {
dedup_key_ = dedup_key;
return *this;
}
StorableTrigger TriggerBuilder::Build() const {
return StorableTrigger(trigger_data_, conversion_destination_,
reporting_origin_, event_source_trigger_data_,
priority_, dedup_key_);
}
ReportBuilder::ReportBuilder(StorableSource source)
: source_(std::move(source)),
external_report_id_(DefaultExternalReportID()) {}
ReportBuilder::~ReportBuilder() = default;
ReportBuilder& ReportBuilder::SetTriggerData(uint64_t trigger_data) {
trigger_data_ = trigger_data;
return *this;
}
ReportBuilder& ReportBuilder::SetConversionTime(base::Time time) {
conversion_time_ = time;
return *this;
}
ReportBuilder& ReportBuilder::SetReportTime(base::Time time) {
report_time_ = time;
return *this;
}
ReportBuilder& ReportBuilder::SetPriority(int64_t priority) {
priority_ = priority;
return *this;
}
ReportBuilder& ReportBuilder::SetExternalReportId(
base::GUID external_report_id) {
external_report_id_ = std::move(external_report_id);
return *this;
}
ReportBuilder& ReportBuilder::SetReportId(
absl::optional<AttributionReport::Id> id) {
report_id_ = id;
return *this;
}
AttributionReport ReportBuilder::Build() const {
return AttributionReport(source_, trigger_data_, conversion_time_,
report_time_, priority_, external_report_id_,
report_id_);
}
// Custom comparator for `StorableSource` that does not take impression IDs
// into account.
bool operator==(const StorableSource& a, const StorableSource& b) {
const auto tie = [](const StorableSource& impression) {
return std::make_tuple(
impression.source_event_id(), impression.impression_origin(),
impression.conversion_origin(), impression.reporting_origin(),
impression.impression_time(), impression.expiry_time(),
impression.source_type(), impression.priority(),
impression.attribution_logic(), impression.dedup_keys());
};
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.trigger_data,
conversion.conversion_time, conversion.report_time,
conversion.priority, conversion.external_report_id,
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);
}
bool operator==(const DeactivatedSource& a, const DeactivatedSource& b) {
const auto tie = [](const DeactivatedSource& deactivated_source) {
return std::make_tuple(deactivated_source.source,
deactivated_source.reason);
};
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, DeactivatedSource::Reason reason) {
switch (reason) {
case DeactivatedSource::Reason::kReplacedByNewerSource:
out << "kReplacedByNewerSource";
break;
case DeactivatedSource::Reason::kReachedAttributionLimit:
out << "kReachedAttributionLimit";
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 << "{trigger_data=" << conversion.trigger_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 << "{source_event_id=" << impression.source_event_id()
<< ",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()
<< ",attribution_logic=" << impression.attribution_logic()
<< ",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
<< ",trigger_data=" << report.trigger_data
<< ",conversion_time=" << report.conversion_time
<< ",report_time=" << report.report_time
<< ",priority=" << report.priority
<< ",external_report_id=" << report.external_report_id
<< ",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::ostream& operator<<(std::ostream& out,
const DeactivatedSource& deactivated_source) {
return out << "{source=" << deactivated_source.source
<< ",reason=" << deactivated_source.reason << "}";
}
std::vector<AttributionReport> GetAttributionsToReportForTesting(
AttributionManagerImpl* manager,
base::Time max_report_time) {
base::RunLoop run_loop;
std::vector<AttributionReport> attribution_reports;
manager->attribution_storage_
.AsyncCall(&AttributionStorage::GetAttributionsToReport)
.WithArgs(max_report_time, /*limit=*/-1)
.Then(base::BindOnce(base::BindLambdaForTesting(
[&](std::vector<AttributionReport> reports) {
attribution_reports = std::move(reports);
run_loop.Quit();
})));
run_loop.Run();
return attribution_reports;
}
} // namespace content