blob: de195b9772f2276e5cd5d7b8696c97e8c6b29c15 [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/conversions/conversion_reporter_impl.h"
#include <stdint.h>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/strcat.h"
#include "base/task/post_task.h"
#include "base/test/bind.h"
#include "base/test/simple_test_clock.h"
#include "content/browser/conversions/conversion_manager.h"
#include "content/browser/conversions/conversion_test_utils.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/content_client.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_browser_context.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
namespace {
// Create a report which should be sent at |report_time|. Impression
// data/conversion data/conversion id are all the same for simplicity.
ConversionReport GetReport(base::Time conversion_time,
base::Time report_time,
int64_t conversion_id) {
// Construct impressions with a null impression time as it is not used for
// reporting.
return ConversionReport(ImpressionBuilder(base::Time()).Build(),
/*conversion_data=*/"", conversion_time, report_time,
/*conversion_id=*/conversion_id);
}
// NetworkSender that keep track of the last sent report id.
class MockNetworkSender : public ConversionReporterImpl::NetworkSender {
public:
MockNetworkSender() = default;
void SendReport(ConversionReport* conversion_report,
ReportSentCallback sent_callback) override {
last_sent_report_id_ = *conversion_report->conversion_id;
num_reports_sent_++;
std::move(sent_callback).Run();
}
int64_t last_sent_report_id() { return last_sent_report_id_; }
size_t num_reports_sent() { return num_reports_sent_; }
void Reset() {
num_reports_sent_ = 0u;
last_sent_report_id_ = -1;
}
private:
size_t num_reports_sent_ = 0u;
int64_t last_sent_report_id_ = -1;
};
} // namespace
class ConversionReporterImplTest : public testing::Test {
public:
ConversionReporterImplTest()
: task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME),
browser_context_(std::make_unique<TestBrowserContext>()),
reporter_(std::make_unique<ConversionReporterImpl>(
browser_context_->GetDefaultStoragePartition(),
task_environment_.GetMockClock())) {
auto network_sender = std::make_unique<MockNetworkSender>();
sender_ = network_sender.get();
reporter_->SetNetworkSenderForTesting(std::move(network_sender));
}
const base::Clock& clock() { return *task_environment_.GetMockClock(); }
protected:
// |task_enviorment_| must be initialized first.
content::BrowserTaskEnvironment task_environment_;
std::unique_ptr<TestBrowserContext> browser_context_;
std::unique_ptr<ConversionReporterImpl> reporter_;
MockNetworkSender* sender_;
};
TEST_F(ConversionReporterImplTest,
ReportAddedWithImmediateReportTime_ReportSent) {
reporter_->AddReportsToQueue(
{GetReport(clock().Now(), clock().Now(), /*conversion_id=*/1)},
base::BindRepeating(
[](int64_t conversion_id) { EXPECT_EQ(1L, conversion_id); }));
// Fast forward by 0, as we yield the thread when a report is scheduled to be
// sent.
task_environment_.FastForwardBy(base::TimeDelta());
EXPECT_EQ(1, sender_->last_sent_report_id());
}
TEST_F(ConversionReporterImplTest,
ReportWithReportTimeBeforeCurrentTime_ReportSent) {
reporter_->AddReportsToQueue(
{GetReport(clock().Now(), clock().Now() - base::TimeDelta::FromHours(10),
/*conversion_id=*/1)},
base::BindRepeating(
[](int64_t conversion_id) { EXPECT_EQ(1L, conversion_id); }));
// Fast forward by 0, as we yield the thread when a report is scheduled to be
// sent.
task_environment_.FastForwardBy(base::TimeDelta());
EXPECT_EQ(1, sender_->last_sent_report_id());
}
TEST_F(ConversionReporterImplTest,
ReportWithDelayedReportTime_NotSentUntilDelay) {
const base::TimeDelta delay = base::TimeDelta::FromMinutes(30);
reporter_->AddReportsToQueue(
{GetReport(clock().Now(), clock().Now() + delay, /*conversion_id=*/1)},
base::DoNothing());
task_environment_.FastForwardBy(base::TimeDelta());
EXPECT_EQ(0u, sender_->num_reports_sent());
task_environment_.FastForwardBy(delay - base::TimeDelta::FromSeconds(1));
EXPECT_EQ(0u, sender_->num_reports_sent());
task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
EXPECT_EQ(1u, sender_->num_reports_sent());
}
TEST_F(ConversionReporterImplTest, DuplicateReportScheduled_Ignored) {
reporter_->AddReportsToQueue(
{GetReport(clock().Now(), clock().Now() + base::TimeDelta::FromMinutes(1),
/*conversion_id=*/1)},
base::DoNothing());
// A duplicate report should not be scheduled.
reporter_->AddReportsToQueue(
{GetReport(clock().Now(), clock().Now() + base::TimeDelta::FromMinutes(1),
/*conversion_id=*/1)},
base::DoNothing());
task_environment_.FastForwardBy(base::TimeDelta::FromMinutes(1));
EXPECT_EQ(1u, sender_->num_reports_sent());
}
TEST_F(ConversionReporterImplTest,
NewReportWithPreviouslySeenConversionId_Scheduled) {
reporter_->AddReportsToQueue(
{GetReport(clock().Now(), clock().Now(), /*conversion_id=*/1)},
base::DoNothing());
task_environment_.FastForwardBy(base::TimeDelta());
EXPECT_EQ(1u, sender_->num_reports_sent());
// We should schedule the new report because the previous report has been
// sent.
reporter_->AddReportsToQueue(
{GetReport(clock().Now(), clock().Now(), /*conversion_id=*/1)},
base::DoNothing());
task_environment_.FastForwardBy(base::TimeDelta());
EXPECT_EQ(2u, sender_->num_reports_sent());
}
TEST_F(ConversionReporterImplTest, ManyReportsAddedAtOnce_SentInOrder) {
std::vector<ConversionReport> reports;
int64_t last_report_id = 0UL;
for (int i = 1; i < 10; i++) {
reports.push_back(GetReport(clock().Now(),
clock().Now() + base::TimeDelta::FromMinutes(i),
/*conversion_id=*/i));
}
reporter_->AddReportsToQueue(
reports, base::BindLambdaForTesting([&](int64_t conversion_id) {
last_report_id = conversion_id;
}));
task_environment_.FastForwardBy(base::TimeDelta());
EXPECT_EQ(0u, sender_->num_reports_sent());
for (int i = 1; i < 10; i++) {
task_environment_.FastForwardBy(base::TimeDelta::FromMinutes(1));
EXPECT_EQ(static_cast<size_t>(i), sender_->num_reports_sent());
EXPECT_EQ(static_cast<int64_t>(i), sender_->last_sent_report_id());
EXPECT_EQ(static_cast<int64_t>(i), last_report_id);
}
}
TEST_F(ConversionReporterImplTest, ManyReportsAddedSeparately_SentInOrder) {
int64_t last_report_id = 0;
auto report_sent_callback = base::BindLambdaForTesting(
[&](int64_t conversion_id) { last_report_id = conversion_id; });
for (int i = 1; i < 10; i++) {
reporter_->AddReportsToQueue(
{GetReport(clock().Now(),
clock().Now() + base::TimeDelta::FromMinutes(i),
/*conversion_id=*/i)},
report_sent_callback);
}
task_environment_.FastForwardBy(base::TimeDelta());
EXPECT_EQ(0u, sender_->num_reports_sent());
for (int i = 1; i < 10; i++) {
task_environment_.FastForwardBy(base::TimeDelta::FromMinutes(1));
EXPECT_EQ(static_cast<size_t>(i), sender_->num_reports_sent());
EXPECT_EQ(static_cast<int64_t>(i), sender_->last_sent_report_id());
EXPECT_EQ(static_cast<int64_t>(i), last_report_id);
}
}
TEST_F(ConversionReporterImplTest, EmbedderDisallowsConversions_ReportNotSent) {
ConversionDisallowingContentBrowserClient disallowed_browser_client;
ContentBrowserClient* old_browser_client =
SetBrowserClientForTesting(&disallowed_browser_client);
reporter_->AddReportsToQueue(
{GetReport(clock().Now(), clock().Now(), /*conversion_id=*/1)},
base::BindRepeating(
[](int64_t conversion_id) { EXPECT_EQ(1L, conversion_id); }));
// Fast forward by 0, as we yield the thread when a report is scheduled to be
// sent.
task_environment_.FastForwardBy(base::TimeDelta());
EXPECT_EQ(0u, sender_->num_reports_sent());
SetBrowserClientForTesting(old_browser_client);
}
TEST_F(ConversionReporterImplTest, EmbedderDisallowedContext_ReportNotSent) {
ConfigurableConversionTestBrowserClient browser_client;
ContentBrowserClient* old_browser_client =
SetBrowserClientForTesting(&browser_client);
browser_client.BlockConversionMeasurementInContext(
base::make_optional(
url::Origin::Create(GURL("https://impression.example"))),
base::make_optional(
url::Origin::Create(GURL("https://conversion.example"))),
base::make_optional(
url::Origin::Create(GURL("https://reporting.example"))));
struct {
GURL impression_origin;
GURL conversion_origin;
GURL reporting_origin;
bool report_allowed;
} kTestCases[] = {
{GURL("https://impression.example"), GURL("https://conversion.example"),
GURL("https://reporting.example"), false},
{GURL("https://conversion.example"), GURL("https://impression.example"),
GURL("https://reporting.example"), true},
{GURL("https://impression.example"), GURL("https://conversion.example"),
GURL("https://other.example"), true},
};
for (const auto& test_case : kTestCases) {
auto impression =
ImpressionBuilder(base::Time())
.SetImpressionOrigin(
url::Origin::Create(test_case.impression_origin))
.SetConversionOrigin(
url::Origin::Create(test_case.conversion_origin))
.SetReportingOrigin(url::Origin::Create(test_case.reporting_origin))
.Build();
std::vector<ConversionReport> reports{
ConversionReport(std::move(impression),
/*conversion_data=*/"", clock().Now(), clock().Now(),
/*conversion_id=*/1)};
reporter_->AddReportsToQueue(std::move(reports),
base::BindRepeating([](int64_t conversion_id) {
EXPECT_EQ(1L, conversion_id);
}));
// Fast forward by 0, as we yield the thread when a report is scheduled to
// be sent.
task_environment_.FastForwardBy(base::TimeDelta());
EXPECT_EQ(static_cast<size_t>(test_case.report_allowed),
sender_->num_reports_sent())
<< "impression_origin; " << test_case.impression_origin
<< ", conversion_origin: " << test_case.conversion_origin
<< ", reporting_origin: " << test_case.reporting_origin;
sender_->Reset();
}
SetBrowserClientForTesting(old_browser_client);
}
} // namespace content