| // 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_manager_impl.h" |
| |
| #include <stdint.h> |
| |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/callback_forward.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/run_loop.h" |
| #include "base/sequenced_task_runner.h" |
| #include "base/test/bind_test_util.h" |
| #include "base/time/clock.h" |
| #include "base/time/time.h" |
| #include "content/browser/conversions/conversion_test_utils.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| constexpr base::TimeDelta kExpiredReportOffset = |
| base::TimeDelta::FromMinutes(2); |
| |
| class ConstantStartupDelayPolicy : public ConversionPolicy { |
| public: |
| ConstantStartupDelayPolicy() = default; |
| ~ConstantStartupDelayPolicy() override = default; |
| |
| base::Time GetReportTimeForExpiredReportAtStartup( |
| base::Time now) const override { |
| return now + kExpiredReportOffset; |
| } |
| }; |
| |
| // Mock reporter that tracks reports being queued by the ConversionManager. |
| class TestConversionReporter |
| : public ConversionManagerImpl::ConversionReporter { |
| public: |
| TestConversionReporter() = default; |
| ~TestConversionReporter() override = default; |
| |
| // ConversionManagerImpl::ConversionReporter |
| void AddReportsToQueue(std::vector<ConversionReport> reports) override { |
| num_reports_ += reports.size(); |
| last_conversion_id_ = *reports.back().conversion_id; |
| last_report_time_ = reports.back().report_time; |
| |
| if (quit_closure_ && num_reports_ >= expected_num_reports_) |
| std::move(quit_closure_).Run(); |
| } |
| |
| size_t num_reports() { return num_reports_; } |
| |
| int64_t last_conversion_id() { return last_conversion_id_; } |
| |
| base::Time last_report_time() { return last_report_time_; } |
| |
| void WaitForNumReports(size_t expected_num_reports) { |
| if (num_reports_ >= expected_num_reports) |
| return; |
| |
| expected_num_reports_ = expected_num_reports; |
| base::RunLoop wait_loop; |
| quit_closure_ = wait_loop.QuitClosure(); |
| wait_loop.Run(); |
| } |
| |
| private: |
| size_t expected_num_reports_ = 0u; |
| size_t num_reports_ = 0u; |
| int64_t last_conversion_id_ = 0UL; |
| base::Time last_report_time_; |
| base::OnceClosure quit_closure_; |
| }; |
| |
| // Time after impression that a conversion can first be sent. See |
| // ConversionStorageDelegateImpl::GetReportTimeForConversion(). |
| constexpr base::TimeDelta kFirstReportingWindow = base::TimeDelta::FromDays(2); |
| |
| // Give impressions a sufficiently long expiry. |
| constexpr base::TimeDelta kImpressionExpiry = base::TimeDelta::FromDays(30); |
| |
| } // namespace |
| |
| class ConversionManagerImplTest : public testing::Test { |
| public: |
| ConversionManagerImplTest() |
| : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) { |
| EXPECT_TRUE(dir_.CreateUniqueTempDir()); |
| CreateManager(); |
| } |
| |
| void CreateManager() { |
| auto reporter = std::make_unique<TestConversionReporter>(); |
| test_reporter_ = reporter.get(); |
| conversion_manager_ = ConversionManagerImpl::CreateForTesting( |
| std::move(reporter), std::make_unique<ConstantStartupDelayPolicy>(), |
| task_environment_.GetMockClock(), dir_.GetPath(), |
| base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()})); |
| } |
| |
| const base::Clock& clock() { return *task_environment_.GetMockClock(); } |
| |
| protected: |
| base::ScopedTempDir dir_; |
| BrowserTaskEnvironment task_environment_; |
| std::unique_ptr<ConversionManagerImpl> conversion_manager_; |
| TestConversionReporter* test_reporter_ = nullptr; |
| }; |
| |
| TEST_F(ConversionManagerImplTest, ImpressionConverted_ReportQueued) { |
| conversion_manager_->HandleImpression( |
| ImpressionBuilder(clock().Now()).SetExpiry(kImpressionExpiry).Build()); |
| conversion_manager_->HandleConversion(DefaultConversion()); |
| |
| // Reports are queued in intervals ahead of when they should be |
| // sent. Make sure the report is not queued earlier than this. |
| task_environment_.FastForwardBy(kFirstReportingWindow - |
| kConversionManagerQueueReportsInterval - |
| base::TimeDelta::FromMinutes(1)); |
| EXPECT_EQ(0u, test_reporter_->num_reports()); |
| |
| task_environment_.FastForwardBy(base::TimeDelta::FromMinutes(1)); |
| EXPECT_EQ(1u, test_reporter_->num_reports()); |
| } |
| |
| TEST_F(ConversionManagerImplTest, QueuedReportNotSent_QueuedAgain) { |
| conversion_manager_->HandleImpression( |
| ImpressionBuilder(clock().Now()).SetExpiry(kImpressionExpiry).Build()); |
| conversion_manager_->HandleConversion(DefaultConversion()); |
| task_environment_.FastForwardBy(kFirstReportingWindow - |
| kConversionManagerQueueReportsInterval); |
| EXPECT_EQ(1u, test_reporter_->num_reports()); |
| |
| // If the report is not sent, it should be added to the queue again. |
| task_environment_.FastForwardBy(kConversionManagerQueueReportsInterval); |
| EXPECT_EQ(2u, test_reporter_->num_reports()); |
| } |
| |
| TEST_F(ConversionManagerImplTest, QueuedReportSent_NotQueuedAgain) { |
| conversion_manager_->HandleImpression( |
| ImpressionBuilder(clock().Now()).SetExpiry(kImpressionExpiry).Build()); |
| conversion_manager_->HandleConversion(DefaultConversion()); |
| task_environment_.FastForwardBy(kFirstReportingWindow - |
| kConversionManagerQueueReportsInterval); |
| EXPECT_EQ(1u, test_reporter_->num_reports()); |
| |
| // Notify the manager that the report has been sent. |
| conversion_manager_->HandleSentReport(test_reporter_->last_conversion_id()); |
| |
| // The report should not be added to the queue again. |
| task_environment_.FastForwardBy(kConversionManagerQueueReportsInterval); |
| EXPECT_EQ(1u, test_reporter_->num_reports()); |
| } |
| |
| // Add a conversion to storage and reset the manager to mimic a report being |
| // available at startup. |
| TEST_F(ConversionManagerImplTest, ExpiredReportsAtStartup_Queued) { |
| // Create a report that will be reported at t= 2 days. |
| conversion_manager_->HandleImpression( |
| ImpressionBuilder(clock().Now()).SetExpiry(kImpressionExpiry).Build()); |
| conversion_manager_->HandleConversion(DefaultConversion()); |
| |
| // Create another conversion that will be reported at t= |
| // (kFirstReportingWindow + 2 * kConversionManagerQueueReportsInterval). |
| task_environment_.FastForwardBy(2 * kConversionManagerQueueReportsInterval); |
| conversion_manager_->HandleImpression( |
| ImpressionBuilder(clock().Now()).SetExpiry(kImpressionExpiry).Build()); |
| conversion_manager_->HandleConversion(DefaultConversion()); |
| |
| EXPECT_EQ(0u, test_reporter_->num_reports()); |
| |
| // Reset the manager to simulate shutdown. |
| conversion_manager_.reset(); |
| |
| // Fast forward past the expected report time of the first conversion, t = |
| // (kFirstReportingWindow+ 1 minute). |
| task_environment_.FastForwardBy(kFirstReportingWindow - |
| (2 * kConversionManagerQueueReportsInterval) + |
| base::TimeDelta::FromMinutes(1)); |
| |
| // Create the manager and check that the first report is queued immediately. |
| CreateManager(); |
| test_reporter_->WaitForNumReports(1); |
| EXPECT_EQ(1u, test_reporter_->num_reports()); |
| conversion_manager_->HandleSentReport(test_reporter_->last_conversion_id()); |
| |
| // The second report is still queued at the correct time. |
| task_environment_.FastForwardBy(kConversionManagerQueueReportsInterval); |
| EXPECT_EQ(2u, test_reporter_->num_reports()); |
| } |
| |
| // This functionality is tested more thoroughly in the ConversionStorageSql |
| // unit tests. Here, just test to make sure the basic control flow is working. |
| TEST_F(ConversionManagerImplTest, ClearData) { |
| for (bool match_url : {true, false}) { |
| base::Time start = clock().Now(); |
| conversion_manager_->HandleImpression( |
| ImpressionBuilder(start).SetExpiry(kImpressionExpiry).Build()); |
| conversion_manager_->HandleConversion(DefaultConversion()); |
| |
| base::RunLoop run_loop; |
| conversion_manager_->ClearData( |
| start, start + base::TimeDelta::FromMinutes(1), |
| base::BindLambdaForTesting( |
| [match_url](const url::Origin& _) { return match_url; }), |
| run_loop.QuitClosure()); |
| run_loop.Run(); |
| |
| task_environment_.FastForwardBy(kFirstReportingWindow - |
| kConversionManagerQueueReportsInterval); |
| size_t expected_reports = match_url ? 0u : 1u; |
| EXPECT_EQ(expected_reports, test_reporter_->num_reports()); |
| } |
| } |
| |
| TEST_F(ConversionManagerImplTest, ExpiredReportsAtStartup_Delayed) { |
| // Create a report that will be reported at t= 2 days. |
| base::Time start_time = clock().Now(); |
| conversion_manager_->HandleImpression( |
| ImpressionBuilder(clock().Now()).SetExpiry(kImpressionExpiry).Build()); |
| conversion_manager_->HandleConversion(DefaultConversion()); |
| EXPECT_EQ(0u, test_reporter_->num_reports()); |
| |
| // Reset the manager to simulate shutdown. |
| conversion_manager_.reset(); |
| |
| // Fast forward past the expected report time of the first conversion, t = |
| // (kFirstReportingWindow+ 1 minute). |
| task_environment_.FastForwardBy(kFirstReportingWindow + |
| base::TimeDelta::FromMinutes(1)); |
| |
| CreateManager(); |
| test_reporter_->WaitForNumReports(1); |
| |
| // Ensure that the expired report is delayed based on the time the browser |
| // started. |
| EXPECT_EQ(start_time + kFirstReportingWindow + |
| base::TimeDelta::FromMinutes(1) + kExpiredReportOffset, |
| test_reporter_->last_report_time()); |
| } |
| |
| TEST_F(ConversionManagerImplTest, NonExpiredReportsQueuedAtStartup_NotDelayed) { |
| // Create a report that will be reported at t= 2 days. |
| base::Time start_time = clock().Now(); |
| conversion_manager_->HandleImpression( |
| ImpressionBuilder(clock().Now()).SetExpiry(kImpressionExpiry).Build()); |
| conversion_manager_->HandleConversion(DefaultConversion()); |
| EXPECT_EQ(0u, test_reporter_->num_reports()); |
| |
| // Reset the manager to simulate shutdown. |
| conversion_manager_.reset(); |
| |
| // Fast forward just before the expected report time. |
| task_environment_.FastForwardBy(kFirstReportingWindow - |
| base::TimeDelta::FromMinutes(1)); |
| |
| // Ensure that this report does not receive additional delay. |
| CreateManager(); |
| test_reporter_->WaitForNumReports(1); |
| EXPECT_EQ(1u, test_reporter_->num_reports()); |
| EXPECT_EQ(start_time + kFirstReportingWindow, |
| test_reporter_->last_report_time()); |
| } |
| |
| } // namespace content |