blob: d31ca9384b13f8225f98654bbfdfd50ef3061ef9 [file] [log] [blame]
// Copyright 2016 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 "chrome/browser/safe_browsing/certificate_reporting_service.h"
#include <string>
#include "base/atomic_sequence_num.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/task/post_task.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/simple_test_clock.h"
#include "base/test/thread_test_helper.h"
#include "base/time/clock.h"
#include "base/time/time.h"
#include "chrome/browser/safe_browsing/certificate_reporting_service_test_utils.h"
#include "chrome/browser/safe_browsing/safe_browsing_service.h"
#include "chrome/browser/safe_browsing/test_safe_browsing_service.h"
#include "chrome/browser/ssl/certificate_error_report.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/test_browser_thread.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "crypto/rsa_private_key.h"
#include "net/cert/x509_certificate.h"
#include "net/cert/x509_util.h"
#include "net/ssl/ssl_info.h"
#include "net/test/url_request/url_request_failed_job.h"
#include "net/test/url_request/url_request_mock_data_job.h"
#include "net/url_request/url_request_filter.h"
#include "net/url_request/url_request_test_util.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
using certificate_reporting_test_utils::CertificateReportingServiceTestHelper;
using certificate_reporting_test_utils::CertificateReportingServiceObserver;
using certificate_reporting_test_utils::EventHistogramTester;
using certificate_reporting_test_utils::ReportExpectation;
using certificate_reporting_test_utils::RetryStatus;
namespace {
// Maximum number of reports kept in the certificate reporting service's retry
// queue.
const size_t kMaxReportCountInQueue = 3;
const char* kFailedReportHistogram = "SSL.CertificateErrorReportFailure";
// NSS requires that serial numbers be unique even for the same issuer;
// as all fake certificates will contain the same issuer name, it's
// necessary to ensure the serial number is unique, as otherwise
// NSS will fail to parse.
base::AtomicSequenceNumber g_serial_number;
scoped_refptr<net::X509Certificate> CreateFakeCert() {
std::unique_ptr<crypto::RSAPrivateKey> unused_key;
std::string cert_der;
if (!net::x509_util::CreateKeyAndSelfSignedCert(
"CN=Error", static_cast<uint32_t>(g_serial_number.GetNext()),
base::Time::Now() - base::TimeDelta::FromMinutes(5),
base::Time::Now() + base::TimeDelta::FromMinutes(5), &unused_key,
&cert_der)) {
return nullptr;
}
return net::X509Certificate::CreateFromBytes(cert_der.data(),
cert_der.size());
}
std::string MakeReport(const std::string& hostname) {
net::SSLInfo ssl_info;
ssl_info.cert = ssl_info.unverified_cert = CreateFakeCert();
CertificateErrorReport report(hostname, ssl_info);
std::string serialized_report;
EXPECT_TRUE(report.Serialize(&serialized_report));
return serialized_report;
}
void CheckReport(const CertificateReportingService::Report& report,
const std::string& expected_hostname,
bool expected_is_retry_upload,
const base::Time& expected_creation_time) {
CertificateErrorReport error_report;
EXPECT_TRUE(error_report.InitializeFromString(report.serialized_report));
EXPECT_EQ(expected_hostname, error_report.hostname());
EXPECT_EQ(expected_is_retry_upload, error_report.is_retry_upload());
EXPECT_EQ(expected_creation_time, report.creation_time);
}
// Class for histogram testing. The failed report histogram is checked once
// after teardown to ensure all in flight requests have completed.
class ReportHistogramTestHelper {
public:
// Sets the expected histogram value to be checked during teardown.
void SetExpectedFailedReportCount(unsigned int num_expected_failed_report) {
num_expected_failed_report_ = num_expected_failed_report;
}
void CheckHistogram() {
if (num_expected_failed_report_ != 0) {
histogram_tester_.ExpectUniqueSample(kFailedReportHistogram,
-net::ERR_SSL_PROTOCOL_ERROR,
num_expected_failed_report_);
} else {
histogram_tester_.ExpectTotalCount(kFailedReportHistogram, 0);
}
}
private:
unsigned int num_expected_failed_report_ = 0;
base::HistogramTester histogram_tester_;
};
} // namespace
TEST(CertificateReportingServiceReportListTest, BoundedReportList) {
EventHistogramTester event_histogram_tester;
// Create a report list with maximum of 2 items.
CertificateReportingService::BoundedReportList list(2 /* max_size */);
EXPECT_EQ(0u, list.items().size());
// Add a ten minute old report.
list.Add(CertificateReportingService::Report(
1, base::Time::Now() - base::TimeDelta::FromMinutes(10),
std::string("report1_ten_minutes_old")));
EXPECT_EQ(1u, list.items().size());
EXPECT_EQ("report1_ten_minutes_old", list.items()[0].serialized_report);
// Add another report. Items are ordered from newest to oldest report by
// creation time. Oldest is at the end.
list.Add(CertificateReportingService::Report(
2, base::Time::Now() - base::TimeDelta::FromMinutes(5),
std::string("report2_five_minutes_old")));
EXPECT_EQ(2u, list.items().size());
EXPECT_EQ("report2_five_minutes_old", list.items()[0].serialized_report);
EXPECT_EQ("report1_ten_minutes_old", list.items()[1].serialized_report);
// Adding a third report removes the oldest report (report1) from the list.
list.Add(CertificateReportingService::Report(
3, base::Time::Now(), std::string("report3_zero_minutes_old")));
EXPECT_EQ(2u, list.items().size());
EXPECT_EQ("report3_zero_minutes_old", list.items()[0].serialized_report);
EXPECT_EQ("report2_five_minutes_old", list.items()[1].serialized_report);
// Adding a report older than the oldest report in the list (report2) is
// a no-op.
list.Add(CertificateReportingService::Report(
0, base::Time::Now() - base::TimeDelta::FromMinutes(10),
std::string("report0_ten_minutes_old")));
EXPECT_EQ(2u, list.items().size());
EXPECT_EQ("report3_zero_minutes_old", list.items()[0].serialized_report);
EXPECT_EQ("report2_five_minutes_old", list.items()[1].serialized_report);
event_histogram_tester.SetExpectedValues(0 /* submitted */, 0 /* failed */,
0 /* successful */, 2 /* dropped */);
}
class CertificateReportingServiceReporterOnIOThreadTest
: public ::testing::Test {
public:
void SetUp() override {
test_shared_loader_factory_ =
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory_);
message_loop_.reset(new base::MessageLoopForIO());
event_histogram_tester_.reset(new EventHistogramTester());
}
void TearDown() override {
// Check histograms as the last thing. This makes sure no in-flight report
// is missed.
histogram_test_helper_.CheckHistogram();
event_histogram_tester_.reset();
}
protected:
network::TestURLLoaderFactory* test_url_loader_factory() {
return &test_url_loader_factory_;
}
scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory() {
return test_shared_loader_factory_;
}
void SetExpectedFailedReportCountOnTearDown(unsigned int count) {
histogram_test_helper_.SetExpectedFailedReportCount(count);
}
EventHistogramTester* event_histogram_tester() {
return event_histogram_tester_.get();
}
private:
std::unique_ptr<base::MessageLoopForIO> message_loop_;
std::unique_ptr<content::TestBrowserThread> io_thread_;
network::TestURLLoaderFactory test_url_loader_factory_;
scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
ReportHistogramTestHelper histogram_test_helper_;
// Histogram tester for reporting events. This is a member instead of a local
// so that we can check the histogram after the test teardown. At that point
// all in flight reports should be completed or deleted because
// of CleanUpOnIOThread().
std::unique_ptr<EventHistogramTester> event_histogram_tester_;
};
TEST_F(CertificateReportingServiceReporterOnIOThreadTest,
Reporter_RetriesEnabled) {
SetExpectedFailedReportCountOnTearDown(6);
std::unique_ptr<base::SimpleTestClock> clock(new base::SimpleTestClock());
const base::Time reference_time = base::Time::Now();
clock->SetNow(reference_time);
const GURL kFailureURL("https://www.foo.com/");
test_url_loader_factory()->AddResponse(
kFailureURL, network::ResourceResponseHead(), std::string(),
network::URLLoaderCompletionStatus(net::ERR_SSL_PROTOCOL_ERROR));
CertificateErrorReporter* certificate_error_reporter =
new CertificateErrorReporter(test_shared_loader_factory(), kFailureURL);
CertificateReportingService::BoundedReportList* list =
new CertificateReportingService::BoundedReportList(2);
// Create a reporter with retries enabled.
CertificateReportingService::Reporter reporter(
std::unique_ptr<CertificateErrorReporter>(certificate_error_reporter),
std::unique_ptr<CertificateReportingService::BoundedReportList>(list),
clock.get(), base::TimeDelta::FromSeconds(100),
true /* retries_enabled */);
EXPECT_EQ(0u, list->items().size());
EXPECT_EQ(0u, reporter.inflight_report_count_for_testing());
// Sending a failed report will put the report in the retry list.
reporter.Send(MakeReport("report1"));
EXPECT_EQ(1u, reporter.inflight_report_count_for_testing());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0u, reporter.inflight_report_count_for_testing());
ASSERT_EQ(1u, list->items().size());
CheckReport(list->items()[0], "report1", false, reference_time);
// Sending a second failed report will also put it in the retry list.
clock->Advance(base::TimeDelta::FromSeconds(10));
reporter.Send(MakeReport("report2"));
base::RunLoop().RunUntilIdle();
ASSERT_EQ(2u, list->items().size());
CheckReport(list->items()[0], "report2", false,
reference_time + base::TimeDelta::FromSeconds(10));
CheckReport(list->items()[1], "report1", false, reference_time);
// Sending a third report should remove the first report from the retry list.
clock->Advance(base::TimeDelta::FromSeconds(10));
reporter.Send(MakeReport("report3"));
base::RunLoop().RunUntilIdle();
ASSERT_EQ(2u, list->items().size());
CheckReport(list->items()[0], "report3", false,
reference_time + base::TimeDelta::FromSeconds(20));
CheckReport(list->items()[1], "report2", false,
reference_time + base::TimeDelta::FromSeconds(10));
// Retry sending all pending reports. All should fail and get added to the
// retry list again. Report creation time doesn't change for retried reports.
clock->Advance(base::TimeDelta::FromSeconds(10));
reporter.SendPending();
base::RunLoop().RunUntilIdle();
ASSERT_EQ(2u, list->items().size());
CheckReport(list->items()[0], "report3", true,
reference_time + base::TimeDelta::FromSeconds(20));
CheckReport(list->items()[1], "report2", true,
reference_time + base::TimeDelta::FromSeconds(10));
// Advance the clock to 115 seconds. This makes report3 95 seconds old, and
// report2 105 seconds old. report2 should be dropped because it's older than
// max age (100 seconds).
clock->SetNow(reference_time + base::TimeDelta::FromSeconds(115));
reporter.SendPending();
EXPECT_EQ(1u, reporter.inflight_report_count_for_testing());
base::RunLoop().RunUntilIdle();
ASSERT_EQ(1u, list->items().size());
CheckReport(list->items()[0], "report3", true,
reference_time + base::TimeDelta::FromSeconds(20));
// Send pending reports again, this time successfully. There should be no
// pending reports left.
const GURL kSuccessURL("https://www.bar.com/");
test_url_loader_factory()->AddResponse(kSuccessURL.spec(), "dummy data");
certificate_error_reporter->set_upload_url_for_testing(kSuccessURL);
clock->Advance(base::TimeDelta::FromSeconds(1));
reporter.SendPending();
EXPECT_EQ(1u, reporter.inflight_report_count_for_testing());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0u, list->items().size());
// report1 was submitted once, failed once, dropped once.
// report2 was submitted twice, failed twice, dropped once.
// report3 was submitted four times, failed thrice, succeeded once.
event_histogram_tester()->SetExpectedValues(
7 /* submitted */, 6 /* failed */, 1 /* successful */, 2 /* dropped */);
}
// Same as above, but retries are disabled.
TEST_F(CertificateReportingServiceReporterOnIOThreadTest,
Reporter_RetriesDisabled) {
SetExpectedFailedReportCountOnTearDown(2);
std::unique_ptr<base::SimpleTestClock> clock(new base::SimpleTestClock());
base::Time reference_time = base::Time::Now();
clock->SetNow(reference_time);
const GURL kFailureURL("https://www.foo.com/");
test_url_loader_factory()->AddResponse(
kFailureURL, network::ResourceResponseHead(), std::string(),
network::URLLoaderCompletionStatus(net::ERR_SSL_PROTOCOL_ERROR));
CertificateErrorReporter* certificate_error_reporter =
new CertificateErrorReporter(test_shared_loader_factory(), kFailureURL);
CertificateReportingService::BoundedReportList* list =
new CertificateReportingService::BoundedReportList(2);
// Create a reporter with retries disabled.
CertificateReportingService::Reporter reporter(
std::unique_ptr<CertificateErrorReporter>(certificate_error_reporter),
std::unique_ptr<CertificateReportingService::BoundedReportList>(list),
clock.get(), base::TimeDelta::FromSeconds(100),
false /* retries_enabled */);
EXPECT_EQ(0u, list->items().size());
EXPECT_EQ(0u, reporter.inflight_report_count_for_testing());
// Sending a failed report will not put the report in the retry list.
reporter.Send("report1");
EXPECT_EQ(1u, reporter.inflight_report_count_for_testing());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0u, reporter.inflight_report_count_for_testing());
ASSERT_EQ(0u, list->items().size());
// Sending a second failed report will also not put it in the retry list.
clock->Advance(base::TimeDelta::FromSeconds(10));
reporter.Send("report2");
base::RunLoop().RunUntilIdle();
ASSERT_EQ(0u, list->items().size());
// Send pending reports. Nothing should be sent.
clock->Advance(base::TimeDelta::FromSeconds(10));
reporter.SendPending();
base::RunLoop().RunUntilIdle();
ASSERT_EQ(0u, list->items().size());
// report1 and report2 were both submitted once, failed once.
event_histogram_tester()->SetExpectedValues(
2 /* submitted */, 2 /* failed */, 0 /* successful */, 0 /* dropped */);
}
class CertificateReportingServiceTest : public ::testing::Test {
public:
CertificateReportingServiceTest()
: thread_bundle_(content::TestBrowserThreadBundle::REAL_IO_THREAD),
io_task_runner_(base::CreateSingleThreadTaskRunnerWithTraits(
{content::BrowserThread::IO})) {}
~CertificateReportingServiceTest() override {}
void SetUp() override {
service_observer_.Clear();
safe_browsing::SafeBrowsingService::RegisterFactory(&sb_service_factory);
sb_service_ = sb_service_factory.CreateSafeBrowsingService();
test_helper_ =
base::MakeRefCounted<CertificateReportingServiceTestHelper>();
clock_.reset(new base::SimpleTestClock());
service_.reset(new CertificateReportingService(
sb_service_.get(), test_helper_, &profile_,
test_helper_->server_public_key(),
test_helper_->server_public_key_version(), kMaxReportCountInQueue,
base::TimeDelta::FromHours(24), clock_.get(),
base::Bind(&CertificateReportingServiceObserver::OnServiceReset,
base::Unretained(&service_observer_))));
service_observer_.WaitForReset();
event_histogram_tester_.reset(new EventHistogramTester());
}
void TearDown() override {
test_helper()->ExpectNoRequests(service());
service_->Shutdown();
service_.reset(nullptr);
histogram_test_helper_.CheckHistogram();
event_histogram_tester_.reset();
}
protected:
CertificateReportingService* service() { return service_.get(); }
// Sets service enabled state and waits for a reset event.
void SetServiceEnabledAndWait(bool enabled) {
service_observer_.Clear();
service()->SetEnabled(enabled);
service_observer_.WaitForReset();
}
void AdvanceClock(base::TimeDelta delta) { clock_->Advance(delta); }
void SetNow(base::Time now) { clock_->SetNow(now); }
void SetExpectedFailedReportCountOnTearDown(unsigned int count) {
histogram_test_helper_.SetExpectedFailedReportCount(count);
}
CertificateReportingServiceTestHelper* test_helper() {
return test_helper_.get();
}
EventHistogramTester* event_histogram_tester() {
return event_histogram_tester_.get();
}
void WaitForNoReports() {
if (!service_->GetReporterForTesting()->inflight_report_count_for_testing())
return;
base::RunLoop run_loop;
service_->GetReporterForTesting()
->SetClosureWhenNoInflightReportsForTesting(run_loop.QuitClosure());
run_loop.Run();
}
private:
// Must be initialized before url_request_context_getter_
content::TestBrowserThreadBundle thread_bundle_;
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
std::unique_ptr<CertificateReportingService> service_;
std::unique_ptr<base::SimpleTestClock> clock_;
scoped_refptr<safe_browsing::SafeBrowsingService> sb_service_;
safe_browsing::TestSafeBrowsingServiceFactory sb_service_factory;
TestingProfile profile_;
scoped_refptr<CertificateReportingServiceTestHelper> test_helper_;
ReportHistogramTestHelper histogram_test_helper_;
CertificateReportingServiceObserver service_observer_;
std::unique_ptr<EventHistogramTester> event_histogram_tester_;
};
TEST_F(CertificateReportingServiceTest, SendSuccessful) {
SetExpectedFailedReportCountOnTearDown(0);
// Let all reports succeed.
test_helper()->SetFailureMode(certificate_reporting_test_utils::
ReportSendingResult::REPORTS_SUCCESSFUL);
service()->Send(MakeReport("report0"));
service()->Send(MakeReport("report1"));
test_helper()->WaitForRequestsDestroyed(
ReportExpectation::Successful({{"report0", RetryStatus::NOT_RETRIED},
{"report1", RetryStatus::NOT_RETRIED}}));
WaitForNoReports();
// report0 and report1 were both submitted once, succeeded once.
event_histogram_tester()->SetExpectedValues(
2 /* submitted */, 0 /* failed */, 2 /* successful */, 0 /* dropped */);
}
TEST_F(CertificateReportingServiceTest, SendFailure) {
SetExpectedFailedReportCountOnTearDown(4);
// Let all reports fail.
test_helper()->SetFailureMode(
certificate_reporting_test_utils::ReportSendingResult::REPORTS_FAIL);
// Send two reports. Both should fail and get queued.
service()->Send(MakeReport("report0"));
service()->Send(MakeReport("report1"));
test_helper()->WaitForRequestsDestroyed(
ReportExpectation::Failed({{"report0", RetryStatus::NOT_RETRIED},
{"report1", RetryStatus::NOT_RETRIED}}));
// Need this to ensure the pending reports are queued.
WaitForNoReports();
// Send pending reports. Previously queued reports should be queued again.
service()->SendPending();
test_helper()->WaitForRequestsDestroyed(ReportExpectation::Failed(
{{"report0", RetryStatus::RETRIED}, {"report1", RetryStatus::RETRIED}}));
// Let all reports succeed.
test_helper()->SetFailureMode(certificate_reporting_test_utils::
ReportSendingResult::REPORTS_SUCCESSFUL);
// Send a third report. This should not be queued.
service()->Send(MakeReport("report2"));
test_helper()->WaitForRequestsDestroyed(
ReportExpectation::Successful({{"report2", RetryStatus::NOT_RETRIED}}));
// Need this to ensure the pending reports are queued.
WaitForNoReports();
// Send pending reports. Previously failed and queued two reports should be
// observed.
service()->SendPending();
test_helper()->WaitForRequestsDestroyed(ReportExpectation::Successful(
{{"report0", RetryStatus::RETRIED}, {"report1", RetryStatus::RETRIED}}));
WaitForNoReports();
// report0 and report1 were both submitted thrice, failed twice, succeeded
// once. report2 was submitted once, succeeded once.
event_histogram_tester()->SetExpectedValues(
7 /* submitted */, 4 /* failed */, 3 /* successful */, 0 /* dropped */);
}
TEST_F(CertificateReportingServiceTest, Disabled_ShouldNotSend) {
SetExpectedFailedReportCountOnTearDown(0);
// Let all reports succeed.
test_helper()->SetFailureMode(certificate_reporting_test_utils::
ReportSendingResult::REPORTS_SUCCESSFUL);
// Disable the service.
SetServiceEnabledAndWait(false);
// Send a report. Report attempt should be cancelled and no sent reports
// should be observed.
service()->Send(MakeReport("report0"));
// Enable the service and send a report again. It should be sent successfully.
SetServiceEnabledAndWait(true);
service()->Send(MakeReport("report1"));
test_helper()->WaitForRequestsDestroyed(
ReportExpectation::Successful({{"report1", RetryStatus::NOT_RETRIED}}));
WaitForNoReports();
// report0 was never sent. report1 was submitted once, succeeded once.
event_histogram_tester()->SetExpectedValues(
1 /* submitted */, 0 /* failed */, 1 /* successful */, 0 /* dropped */);
}
TEST_F(CertificateReportingServiceTest, Disabled_ShouldClearPendingReports) {
SetExpectedFailedReportCountOnTearDown(1);
// Let all reports fail.
test_helper()->SetFailureMode(
certificate_reporting_test_utils::ReportSendingResult::REPORTS_FAIL);
service()->Send(MakeReport("report0"));
test_helper()->WaitForRequestsDestroyed(
ReportExpectation::Failed({{"report0", RetryStatus::NOT_RETRIED}}));
// Need this to ensure the pending reports are queued.
WaitForNoReports();
// Disable the service.
SetServiceEnabledAndWait(false);
// Sending has no effect while disabled.
service()->SendPending();
// Re-enable the service and send pending reports. Pending reports should have
// been cleared when the service was disabled, so no report should be seen.
SetServiceEnabledAndWait(true);
// Sending with empty queue has no effect.
service()->SendPending();
WaitForNoReports();
// report0 was submitted once, failed once.
event_histogram_tester()->SetExpectedValues(
1 /* submitted */, 1 /* failed */, 0 /* successful */, 0 /* dropped */);
}
TEST_F(CertificateReportingServiceTest, DontSendOldReports) {
SetExpectedFailedReportCountOnTearDown(5);
SetNow(base::Time::Now());
// Let all reports fail.
test_helper()->SetFailureMode(
certificate_reporting_test_utils::ReportSendingResult::REPORTS_FAIL);
// Send a report, then advance the clock and send another report.
service()->Send(MakeReport("report0"));
AdvanceClock(base::TimeDelta::FromHours(5));
service()->Send(MakeReport("report1"));
test_helper()->WaitForRequestsDestroyed(
ReportExpectation::Failed({{"report0", RetryStatus::NOT_RETRIED},
{"report1", RetryStatus::NOT_RETRIED}}));
// Advance the clock to 20 hours, putting it 25 hours ahead of the reference
// time. This makes the report0 older than max age (24 hours). The report1 is
// now 20 hours old.
AdvanceClock(base::TimeDelta::FromHours(20));
// Need this to ensure the pending reports are queued.
WaitForNoReports();
// Send pending reports. report0 should be discarded since it's too old.
// report1 should be queued again.
service()->SendPending();
test_helper()->WaitForRequestsDestroyed(
ReportExpectation::Failed({{"report1", RetryStatus::RETRIED}}));
// Send a third report.
service()->Send(MakeReport("report2"));
test_helper()->WaitForRequestsDestroyed(
ReportExpectation::Failed({{"report2", RetryStatus::NOT_RETRIED}}));
// Advance the clock 5 hours. The report1 will now be 25 hours old.
AdvanceClock(base::TimeDelta::FromHours(5));
// Need this to ensure the pending reports are queued.
WaitForNoReports();
// Send pending reports. report1 should be discarded since it's too old.
// report2 should be queued again.
service()->SendPending();
test_helper()->WaitForRequestsDestroyed(
ReportExpectation::Failed({{"report2", RetryStatus::RETRIED}}));
// Advance the clock 20 hours again so that report2 is 25 hours old and is
// older than max age (24 hours)
AdvanceClock(base::TimeDelta::FromHours(20));
// Need this to ensure the pending reports are queued.
WaitForNoReports();
// Send pending reports. report2 should be discarded since it's too old. No
// other reports remain.
service()->SendPending();
WaitForNoReports();
// report0 was submitted once, failed once, dropped once.
// report1 was submitted twice, failed twice, dropped once.
// report2 was submitted twice, failed twice, dropped once.
// Need to wait for SendPending() to complete.
event_histogram_tester()->SetExpectedValues(
5 /* submitted */, 5 /* failed */, 0 /* successful */, 3 /* dropped */);
}
TEST_F(CertificateReportingServiceTest, DiscardOldReports) {
SetExpectedFailedReportCountOnTearDown(7);
SetNow(base::Time::Now());
// Let all reports fail.
test_helper()->SetFailureMode(
certificate_reporting_test_utils::ReportSendingResult::REPORTS_FAIL);
// Send three more reports within five hours of each other. After this:
// report0 is 0 hours after reference time (15 hours old).
// report1 is 5 hours after reference time (10 hours old).
// report2 is 10 hours after reference time (5 hours old).
// report3 is 15 hours after reference time (0 hours old).
service()->Send(MakeReport("report0"));
AdvanceClock(base::TimeDelta::FromHours(5));
service()->Send(MakeReport("report1"));
AdvanceClock(base::TimeDelta::FromHours(5));
service()->Send(MakeReport("report2"));
AdvanceClock(base::TimeDelta::FromHours(5));
service()->Send(MakeReport("report3"));
test_helper()->WaitForRequestsDestroyed(
ReportExpectation::Failed({{"report0", RetryStatus::NOT_RETRIED},
{"report1", RetryStatus::NOT_RETRIED},
{"report2", RetryStatus::NOT_RETRIED},
{"report3", RetryStatus::NOT_RETRIED}}));
// Need this to ensure the pending reports are queued.
WaitForNoReports();
// Send pending reports. Four reports were generated above, but the service
// only queues three reports, so the very first one should be dropped since
// it's the oldest.
service()->SendPending();
test_helper()->WaitForRequestsDestroyed(
ReportExpectation::Failed({{"report1", RetryStatus::RETRIED},
{"report2", RetryStatus::RETRIED},
{"report3", RetryStatus::RETRIED}}));
// Let all reports succeed.
test_helper()->SetFailureMode(certificate_reporting_test_utils::
ReportSendingResult::REPORTS_SUCCESSFUL);
// Advance the clock by 15 hours. Current time is now 30 hours after reference
// time. The ages of reports are now as follows:
// report1 is 25 hours old.
// report2 is 20 hours old.
// report3 is 15 hours old.
AdvanceClock(base::TimeDelta::FromHours(15));
// Need this to ensure the pending reports are queued.
WaitForNoReports();
// Send pending reports. Only report2 and report3 should be sent, report1
// should be ignored because it's too old.
service()->SendPending();
test_helper()->WaitForRequestsDestroyed(ReportExpectation::Successful(
{{"report2", RetryStatus::RETRIED}, {"report3", RetryStatus::RETRIED}}));
// Do a final send. No reports should be sent.
service()->SendPending();
WaitForNoReports();
// report0 was submitted once, failed once, dropped once.
// report1 was submitted twice, failed twice, dropped once.
// report2 was submitted thrice, failed twice, succeeded once.
// report3 was submitted thrice, failed twice, succeeded once.
event_histogram_tester()->SetExpectedValues(
9 /* submitted */, 7 /* failed */, 2 /* successful */, 2 /* dropped */);
}
// A delayed report should successfully upload when it's resumed.
TEST_F(CertificateReportingServiceTest, Delayed_Resumed) {
SetExpectedFailedReportCountOnTearDown(0);
// Let reports hang.
test_helper()->SetFailureMode(
certificate_reporting_test_utils::ReportSendingResult::REPORTS_DELAY);
// Send a report. The report upload hangs, so no error or success callbacks
// should be called.
service()->Send(MakeReport("report0"));
// Resume the report upload and run the callbacks. The report should be
// successfully sent.
test_helper()->ResumeDelayedRequest();
test_helper()->WaitForRequestsDestroyed(
ReportExpectation::Delayed({{"report0", RetryStatus::NOT_RETRIED}}));
WaitForNoReports();
// report0 was submitted once, succeeded once.
event_histogram_tester()->SetExpectedValues(
1 /* submitted */, 0 /* failed */, 1 /* successful */, 0 /* dropped */);
}
// Delayed reports should cleaned when the service is reset.
TEST_F(CertificateReportingServiceTest, Delayed_Reset) {
SetExpectedFailedReportCountOnTearDown(0);
// Let reports hang.
test_helper()->SetFailureMode(
certificate_reporting_test_utils::ReportSendingResult::REPORTS_DELAY);
// Send a report. The report is triggered but hangs, so no error or success
// callbacks should be called.
service()->Send(MakeReport("report0"));
// Disable the service. This should reset the reporting service and
// clear all pending reports.
SetServiceEnabledAndWait(false);
// Resume delayed report. No report should be observed since the service
// should have reset and all pending reports should be cleared. If any report
// is observed, the next test_helper()->WaitForRequestsDestroyed() will fail.
test_helper()->ResumeDelayedRequest();
// Enable the service.
SetServiceEnabledAndWait(true);
// Send a report. The report is triggered but hangs, so no error or success
// callbacks should be called. The report id is again 0 since the pending
// report queue has been cleared above.
service()->Send(MakeReport("report1"));
// Resume delayed report. Two reports are successfully sent.
test_helper()->ResumeDelayedRequest();
test_helper()->WaitForRequestsDestroyed(
ReportExpectation::Delayed({{"report0", RetryStatus::NOT_RETRIED},
{"report1", RetryStatus::NOT_RETRIED}}));
WaitForNoReports();
// report0 was submitted once, but neither failed nor succeeded because the
// report queue was cleared. report1 was submitted once, succeeded once.
event_histogram_tester()->SetExpectedValues(
2 /* submitted */, 0 /* failed */, 1 /* successful */, 0 /* dropped */);
}