| Index: net/reporting/reporting_service_unittest.cc
|
| diff --git a/net/reporting/reporting_service_unittest.cc b/net/reporting/reporting_service_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..5d18ff141e15340bb12147a4a75a7f6c4637c19c
|
| --- /dev/null
|
| +++ b/net/reporting/reporting_service_unittest.cc
|
| @@ -0,0 +1,400 @@
|
| +// 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 "net/reporting/reporting_service.h"
|
| +
|
| +#include <string>
|
| +#include <vector>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/json/json_reader.h"
|
| +#include "base/memory/ptr_util.h"
|
| +#include "base/test/simple_test_tick_clock.h"
|
| +#include "base/values.h"
|
| +#include "net/reporting/reporting_uploader.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +#include "url/gurl.h"
|
| +
|
| +namespace net {
|
| +namespace {
|
| +
|
| +class MockUploader : public ReportingUploader {
|
| + public:
|
| + class PendingDelivery {
|
| + public:
|
| + PendingDelivery(MockUploader* uploader,
|
| + const GURL& url,
|
| + const std::string& json,
|
| + const Callback& callback)
|
| + : uploader_(uploader), url_(url), json_(json), callback_(callback) {
|
| + DCHECK(uploader_);
|
| + }
|
| +
|
| + ~PendingDelivery() {}
|
| +
|
| + void Complete(Outcome outcome) {
|
| + callback_.Run(outcome);
|
| + // Deletes |this|.
|
| + uploader_->OnDeliveryComplete(this);
|
| + }
|
| +
|
| + const GURL& url() const { return url_; }
|
| + const std::string& json() const { return json_; }
|
| +
|
| + private:
|
| + MockUploader* uploader_;
|
| + GURL url_;
|
| + std::string json_;
|
| + Callback callback_;
|
| + };
|
| +
|
| + MockUploader() {}
|
| + ~MockUploader() override {}
|
| +
|
| + void AttemptDelivery(const GURL& url,
|
| + const std::string& json,
|
| + const Callback& callback) override {
|
| + deliveries_.push_back(
|
| + base::MakeUnique<PendingDelivery>(this, url, json, callback));
|
| + }
|
| +
|
| + const std::vector<std::unique_ptr<PendingDelivery>>& GetPendingDeliveries()
|
| + const {
|
| + return deliveries_;
|
| + }
|
| +
|
| + void OnDeliveryComplete(PendingDelivery* delivery) {
|
| + for (auto it = deliveries_.begin(); it != deliveries_.end(); ++it) {
|
| + if (it->get() == delivery) {
|
| + deliveries_.erase(it);
|
| + return;
|
| + }
|
| + }
|
| + NOTREACHED();
|
| + }
|
| +
|
| + private:
|
| + std::vector<std::unique_ptr<PendingDelivery>> deliveries_;
|
| +};
|
| +
|
| +class ReportingServiceTest : public ::testing::Test {
|
| + protected:
|
| + ReportingServiceTest() {}
|
| +
|
| + void CreateServiceWithDefaultPolicy() {
|
| + ReportingService::Policy policy = ReportingService::Policy::GetDefault();
|
| + CreateServiceWithPolicy(policy);
|
| + }
|
| +
|
| + void CreateServiceWithPolicy(const ReportingService::Policy& policy) {
|
| + policy_ = policy;
|
| + service_ = base::MakeUnique<ReportingService>(policy);
|
| + clock_ = new base::SimpleTestTickClock();
|
| + service_->set_clock_for_testing(base::WrapUnique(clock_));
|
| + uploader_ = new MockUploader();
|
| + service_->set_uploader(base::WrapUnique(uploader_));
|
| + }
|
| +
|
| + void QueueReport(const GURL& url) {
|
| + service_->QueueReport(base::MakeUnique<base::DictionaryValue>(), url,
|
| + url.GetOrigin(), "default", "test");
|
| + }
|
| +
|
| + const std::vector<std::unique_ptr<MockUploader::PendingDelivery>>&
|
| + GetPendingDeliveries() {
|
| + return uploader_->GetPendingDeliveries();
|
| + }
|
| +
|
| + bool HasEndpoint(const std::string& endpoint_url) {
|
| + return service_->HasEndpointForTesting(GURL(endpoint_url));
|
| + }
|
| +
|
| + bool HasClient(const std::string& endpoint_url,
|
| + const std::string& origin_url) {
|
| + return service_->HasClientForTesting(GURL(endpoint_url), GURL(origin_url));
|
| + }
|
| +
|
| + int GetEndpointFailures(const std::string& endpoint_url) {
|
| + return service_->GetEndpointFailuresForTesting(GURL(endpoint_url));
|
| + }
|
| +
|
| + // |policy_| is a copy of the policy used to configure |service_|, for easier
|
| + // reference.
|
| + ReportingService::Policy policy_;
|
| + // |clock_| and |uploader_| are owned by |service_|, not the test.
|
| + base::SimpleTestTickClock* clock_;
|
| + MockUploader* uploader_;
|
| + std::unique_ptr<ReportingService> service_;
|
| +};
|
| +
|
| +TEST_F(ReportingServiceTest, ProcessHeaderFromInsecureOrigin) {
|
| + CreateServiceWithDefaultPolicy();
|
| +
|
| + service_->ProcessHeader(GURL("http://insecure/"),
|
| + "{\"url\":\"https://endpoint/\",\"max-age\":1}");
|
| + EXPECT_FALSE(HasClient("https://endpoint/", "http://insecure"));
|
| +}
|
| +
|
| +TEST_F(ReportingServiceTest, ProcessInvalidHeaders) {
|
| + static const struct {
|
| + const char* header_value;
|
| + const char* description;
|
| + } kInvalidHeaderTestCases[] = {
|
| + {"{\"max-age\":1}", "missing url"},
|
| + {"{\"url\":0,\"max-age\":1}", "non-string url"},
|
| + {"{\"url\":\"http://insecure/\",\"max-age\":1}", "insecure url"},
|
| +
|
| + {"{\"url\":\"https://endpoint/\"}", "missing max-age"},
|
| + {"{\"url\":\"https://endpoint/\",\"max-age\":\"\"}",
|
| + "non-integer max-age"},
|
| + {"{\"url\":\"https://endpoint/\",\"max-age\":-1}", "negative max-age"},
|
| +
|
| + {"{\"url\":\"https://endpoint/\",\"max-age\":1,\"group\":0}",
|
| + "non-string group"},
|
| +
|
| + {"{\"url\":\"https://endpoint/\",\"max-age\":1,\"includeSubdomains\":0}",
|
| + "non-boolean includeSubdomains"},
|
| + };
|
| +
|
| + CreateServiceWithDefaultPolicy();
|
| +
|
| + for (size_t i = 0; i < arraysize(kInvalidHeaderTestCases); ++i) {
|
| + auto& test_case = kInvalidHeaderTestCases[i];
|
| + service_->ProcessHeader(GURL("https://origin/"), test_case.header_value);
|
| + EXPECT_FALSE(HasClient("https://endpoint/", "https://origin/"));
|
| + }
|
| +}
|
| +
|
| +TEST_F(ReportingServiceTest, ProcessValidHeader) {
|
| + CreateServiceWithDefaultPolicy();
|
| +
|
| + service_->ProcessHeader(GURL("https://origin/"),
|
| + "{\"url\":\"https://endpoint/\",\"max-age\":1}");
|
| + EXPECT_TRUE(HasClient("https://endpoint/", "https://origin/"));
|
| +
|
| +#if 0
|
| + EXPECT_EQ(1u, GetCache().GetEndpoints().size());
|
| + const std::unique_ptr<ReportingEndpoint>* endpoint_ptr =
|
| + GetCache().GetEndpoint(GURL("https://endpoint/"));
|
| + ASSERT_TRUE(endpoint_ptr);
|
| + const std::unique_ptr<ReportingEndpoint>& endpoint = *endpoint_ptr;
|
| + EXPECT_EQ(GURL("https://endpoint/"), endpoint->url);
|
| +
|
| + EXPECT_EQ(1u, endpoint->clients.count(GURL("https://origin/")));
|
| + auto client_it = endpoint->clients.find(GURL("https://origin/"));
|
| + ASSERT_TRUE(client_it != endpoint->clients.end());
|
| + const ReportingClient& client = client_it->second;
|
| + EXPECT_EQ(GURL("https://origin"), client.origin);
|
| + EXPECT_FALSE(client.subdomains);
|
| + EXPECT_EQ("default", client.group);
|
| + EXPECT_EQ(base::TimeDelta::FromSeconds(1), client.ttl);
|
| +#endif
|
| +}
|
| +
|
| +TEST_F(ReportingServiceTest, ProcessZeroMaxAgeHeader) {
|
| + CreateServiceWithDefaultPolicy();
|
| +
|
| + service_->ProcessHeader(GURL("https://origin/"),
|
| + "{\"url\":\"https://endpoint/\",\"max-age\":1}");
|
| + EXPECT_TRUE(HasClient("https://endpoint/", "https://origin/"));
|
| + service_->ProcessHeader(GURL("https://origin/"),
|
| + "{\"url\":\"https://endpoint/\",\"max-age\":0}");
|
| + EXPECT_FALSE(HasClient("https://endpoint/", "https://origin/"));
|
| +}
|
| +
|
| +TEST_F(ReportingServiceTest, DeliverySuccess) {
|
| + CreateServiceWithDefaultPolicy();
|
| +
|
| + service_->ProcessHeader(GURL("https://origin/"),
|
| + "{\"url\":\"https://endpoint/\",\"max-age\":1}");
|
| + QueueReport(GURL("https://origin/path"));
|
| + service_->SendReports();
|
| +
|
| + ASSERT_EQ(1u, GetPendingDeliveries().size());
|
| + const std::unique_ptr<MockUploader::PendingDelivery>& delivery =
|
| + GetPendingDeliveries()[0];
|
| + EXPECT_EQ(GURL("https://endpoint/"), delivery->url());
|
| + {
|
| + auto reports_value = base::JSONReader::Read(delivery->json());
|
| + const base::ListValue* reports;
|
| + ASSERT_TRUE(reports_value->GetAsList(&reports));
|
| + ASSERT_EQ(1u, reports->GetSize());
|
| + const base::DictionaryValue* report;
|
| + ASSERT_TRUE(reports->GetDictionary(0u, &report));
|
| + std::string type;
|
| + ASSERT_TRUE(report->GetString("type", &type));
|
| + EXPECT_EQ("test", type);
|
| + std::string url;
|
| + ASSERT_TRUE(report->GetString("url", &url));
|
| + EXPECT_EQ("https://origin/path", url);
|
| + }
|
| +
|
| + delivery->Complete(ReportingUploader::SUCCESS);
|
| +
|
| + EXPECT_EQ(0, GetEndpointFailures("https://endpoint/"));
|
| +}
|
| +
|
| +TEST_F(ReportingServiceTest, DeliveryFailure) {
|
| + CreateServiceWithDefaultPolicy();
|
| +
|
| + service_->ProcessHeader(GURL("https://origin/"),
|
| + "{\"url\":\"https://endpoint/\",\"max-age\":1}");
|
| + QueueReport(GURL("https://origin/path"));
|
| + service_->SendReports();
|
| +
|
| + GetPendingDeliveries()[0]->Complete(ReportingUploader::FAILURE);
|
| +
|
| + EXPECT_EQ(1, GetEndpointFailures("https://endpoint/"));
|
| +}
|
| +
|
| +TEST_F(ReportingServiceTest, DeliveryRemoveEndpoint) {
|
| + CreateServiceWithDefaultPolicy();
|
| +
|
| + service_->ProcessHeader(GURL("https://origin/"),
|
| + "{\"url\":\"https://endpoint/\",\"max-age\":1}");
|
| + QueueReport(GURL("https://origin/path"));
|
| + service_->SendReports();
|
| +
|
| + GetPendingDeliveries()[0]->Complete(ReportingUploader::REMOVE_ENDPOINT);
|
| +
|
| + EXPECT_FALSE(HasClient("https://endpoint/", "https://origin/"));
|
| +}
|
| +
|
| +TEST_F(ReportingServiceTest, NoDeliveryToPendingEndpoint) {
|
| + CreateServiceWithDefaultPolicy();
|
| +
|
| + service_->ProcessHeader(GURL("https://origin/"),
|
| + "{\"url\":\"https://endpoint/\",\"max-age\":1}");
|
| + QueueReport(GURL("https://origin/"));
|
| + service_->SendReports();
|
| +
|
| + GetPendingDeliveries()[0]->Complete(ReportingUploader::FAILURE);
|
| +
|
| + // Without advancing the clock, the endpoint should still be pending, so the
|
| + // Manager should not try to deliver reports to it.
|
| + service_->SendReports();
|
| +
|
| + EXPECT_TRUE(GetPendingDeliveries().empty());
|
| +}
|
| +
|
| +TEST_F(ReportingServiceTest, NoDeliveryToMismatchedEndpoint) {
|
| + CreateServiceWithDefaultPolicy();
|
| +
|
| + service_->ProcessHeader(GURL("https://origin1/"),
|
| + "{\"url\":\"https://endpoint1/\",\"max-age\":1}");
|
| + QueueReport(GURL("https://origin2/"));
|
| + service_->SendReports();
|
| +
|
| + EXPECT_TRUE(GetPendingDeliveries().empty());
|
| +}
|
| +
|
| +TEST_F(ReportingServiceTest, BatchReportsForSameOrigin) {
|
| + CreateServiceWithDefaultPolicy();
|
| +
|
| + service_->ProcessHeader(GURL("https://origin/"),
|
| + "{\"url\":\"https://endpoint/\",\"max-age\":1}");
|
| + QueueReport(GURL("https://origin/"));
|
| + QueueReport(GURL("https://origin/"));
|
| + service_->SendReports();
|
| +
|
| + EXPECT_EQ(1u, GetPendingDeliveries().size());
|
| +
|
| + GetPendingDeliveries()[0]->Complete(ReportingUploader::FAILURE);
|
| +}
|
| +
|
| +TEST_F(ReportingServiceTest, BatchReportsForSameEndpoint) {
|
| + CreateServiceWithDefaultPolicy();
|
| +
|
| + service_->ProcessHeader(GURL("https://origin1/"),
|
| + "{\"url\":\"https://endpoint/\",\"max-age\":1}");
|
| + service_->ProcessHeader(GURL("https://origin2/"),
|
| + "{\"url\":\"https://endpoint/\",\"max-age\":1}");
|
| + QueueReport(GURL("https://origin1/"));
|
| + QueueReport(GURL("https://origin2/"));
|
| + service_->SendReports();
|
| +
|
| + EXPECT_EQ(1u, GetPendingDeliveries().size());
|
| +
|
| + GetPendingDeliveries()[0]->Complete(ReportingUploader::FAILURE);
|
| +}
|
| +
|
| +TEST_F(ReportingServiceTest, ExpiredEndpoint) {
|
| + CreateServiceWithDefaultPolicy();
|
| +
|
| + service_->ProcessHeader(GURL("https://origin/"),
|
| + "{\"url\":\"https://endpoint/\",\"max-age\":1}");
|
| + EXPECT_TRUE(HasEndpoint("https://endpoint/"));
|
| +
|
| + clock_->Advance(base::TimeDelta::FromSeconds(2));
|
| + service_->CollectGarbageForTesting();
|
| + EXPECT_FALSE(HasEndpoint("https://endpoint/"));
|
| +}
|
| +
|
| +TEST_F(ReportingServiceTest, UnusedEndpoint) {
|
| + CreateServiceWithDefaultPolicy();
|
| +
|
| + service_->ProcessHeader(
|
| + GURL("https://origin/"),
|
| + "{\"url\":\"https://endpoint/\",\"max-age\":999999999}");
|
| + EXPECT_TRUE(HasEndpoint("https://endpoint/"));
|
| +
|
| + clock_->Advance(2 * policy_.endpoint_lifetime);
|
| + service_->CollectGarbageForTesting();
|
| + EXPECT_FALSE(HasEndpoint("https://endpoint/"));
|
| +}
|
| +
|
| +TEST_F(ReportingServiceTest, FailedEndpoint) {
|
| + ReportingService::Policy policy = ReportingService::Policy::GetDefault();
|
| + policy.endpoint_backoff.initial_delay_ms = 0;
|
| + policy.endpoint_backoff.maximum_backoff_ms = 0;
|
| + policy.endpoint_backoff.entry_lifetime_ms = 1000;
|
| + policy.max_endpoint_failures = 5;
|
| + policy.max_report_failures = 999;
|
| + CreateServiceWithPolicy(policy);
|
| +
|
| + service_->ProcessHeader(
|
| + GURL("https://origin/"),
|
| + "{\"url\":\"https://endpoint/\",\"max-age\":999999999}");
|
| +
|
| + QueueReport(GURL("https://origin/"));
|
| +
|
| + for (int i = 0; i < policy_.max_endpoint_failures + 1; i++) {
|
| + EXPECT_TRUE(HasEndpoint("https://endpoint/"));
|
| + service_->SendReports();
|
| + EXPECT_EQ(1u, GetPendingDeliveries().size());
|
| + GetPendingDeliveries()[0]->Complete(ReportingUploader::FAILURE);
|
| + }
|
| + service_->CollectGarbageForTesting();
|
| + EXPECT_TRUE(HasEndpoint("https://endpoint/"));
|
| +
|
| + clock_->Advance(base::TimeDelta::FromMilliseconds(
|
| + 2 * policy_.endpoint_backoff.entry_lifetime_ms));
|
| +
|
| + service_->CollectGarbageForTesting();
|
| + EXPECT_FALSE(HasEndpoint("https://endpoint/"));
|
| +}
|
| +
|
| +TEST_F(ReportingServiceTest, EvictedEndpoint) {
|
| + ReportingService::Policy policy = ReportingService::Policy::GetDefault();
|
| + policy.max_endpoint_count = 1;
|
| + CreateServiceWithPolicy(policy);
|
| +
|
| + service_->ProcessHeader(GURL("https://origin1/"),
|
| + "{\"url\":\"https://endpoint1/\",\"max-age\":1}");
|
| + EXPECT_TRUE(HasEndpoint("https://endpoint1"));
|
| +
|
| + service_->ProcessHeader(GURL("https://origin2/"),
|
| + "{\"url\":\"https://endpoint2/\",\"max-age\":1}");
|
| + EXPECT_FALSE(HasEndpoint("https://endpoint1"));
|
| + EXPECT_TRUE(HasEndpoint("https://endpoint2"));
|
| +}
|
| +
|
| +// TODO: Test:
|
| +// Expired report (happens after lifetime passes -> GC)
|
| +// Failed report (happens after failed attempt -> attempt)
|
| +// Evicted report (happens after queue or failed attempt -> queue)
|
| +// No-action cases (e.g. don't evict pending report).
|
| +
|
| +} // namespace
|
| +} // namespace net
|
|
|