Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(484)

Unified Diff: net/reporting/reporting_delivery_agent_unittest.cc

Issue 2739483002: Reporting: Implement delivery agent. (Closed)
Patch Set: Make requested changes. Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « net/reporting/reporting_delivery_agent.cc ('k') | net/reporting/reporting_report.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: net/reporting/reporting_delivery_agent_unittest.cc
diff --git a/net/reporting/reporting_delivery_agent_unittest.cc b/net/reporting/reporting_delivery_agent_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..b2d8fbfd3e4c561639e631ba77093d8e77448020
--- /dev/null
+++ b/net/reporting/reporting_delivery_agent_unittest.cc
@@ -0,0 +1,382 @@
+// Copyright 2017 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_delivery_agent.h"
+
+#include <vector>
+
+#include "base/json/json_reader.h"
+#include "base/memory/ptr_util.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "base/test/values_test_util.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "net/base/backoff_entry.h"
+#include "net/reporting/reporting_cache.h"
+#include "net/reporting/reporting_report.h"
+#include "net/reporting/reporting_test_util.h"
+#include "net/reporting/reporting_uploader.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+#include "url/origin.h"
+
+namespace net {
+namespace {
+
+class MockUploader : public ReportingUploader {
+ public:
+ class PendingUpload {
+ public:
+ PendingUpload(MockUploader* uploader,
+ const GURL& url,
+ const std::string& json,
+ const Callback& callback)
+ : uploader_(uploader), url_(url), json_(json), callback_(callback) {
+ DCHECK(uploader_);
+ }
+
+ ~PendingUpload() {}
+
+ void Complete(Outcome outcome) {
+ callback_.Run(outcome);
+ // Deletes |this|.
+ uploader_->OnUploadComplete(this);
+ }
+
+ const GURL& url() const { return url_; }
+ const std::string& json() const { return json_; }
+
+ std::unique_ptr<base::Value> GetValue() const {
+ return base::JSONReader::Read(json_);
+ }
+
+ private:
+ MockUploader* uploader_;
+ GURL url_;
+ std::string json_;
+ Callback callback_;
+ };
+
+ MockUploader() {}
+ ~MockUploader() override {}
+
+ void StartUpload(const GURL& url,
+ const std::string& json,
+ const Callback& callback) override {
+ uploads_.push_back(
+ base::MakeUnique<PendingUpload>(this, url, json, callback));
+ }
+
+ const std::vector<std::unique_ptr<PendingUpload>>& pending_uploads() const {
+ return uploads_;
+ }
+
+ void OnUploadComplete(PendingUpload* upload) {
+ for (auto it = uploads_.begin(); it != uploads_.end(); ++it) {
+ if (it->get() == upload) {
+ uploads_.erase(it);
+ return;
+ }
+ }
+ NOTREACHED();
+ }
+
+ private:
+ std::vector<std::unique_ptr<PendingUpload>> uploads_;
+};
+
+class ReportingDeliveryAgentTest : public ::testing::Test {
+ protected:
+ ReportingDeliveryAgentTest()
+ : agent_(&clock_, &cache_, &uploader_, &backoff_policy_) {
+ backoff_policy_.num_errors_to_ignore = 0;
+ backoff_policy_.initial_delay_ms = 60000;
+ backoff_policy_.multiply_factor = 2.0;
+ backoff_policy_.jitter_factor = 0.0;
+ backoff_policy_.maximum_backoff_ms = -1;
+ backoff_policy_.entry_lifetime_ms = 0;
+ backoff_policy_.always_use_initial_delay = false;
+ }
+
+ base::TimeTicks tomorrow() {
+ return clock_.NowTicks() + base::TimeDelta::FromDays(1);
+ }
+
+ const std::vector<std::unique_ptr<MockUploader::PendingUpload>>&
+ pending_uploads() const {
+ return uploader_.pending_uploads();
+ }
+
+ const GURL kUrl_ = GURL("https://origin/path");
+ const url::Origin kOrigin_ = url::Origin(GURL("https://origin/"));
+ const GURL kEndpoint_ = GURL("https://endpoint/");
+ const std::string kGroup_ = "group";
+ const std::string kType_ = "type";
+
+ base::SimpleTestTickClock clock_;
+ ReportingCache cache_;
+ MockUploader uploader_;
+ BackoffEntry::Policy backoff_policy_;
+ ReportingDeliveryAgent agent_;
+};
+
+TEST_F(ReportingDeliveryAgentTest, SuccessfulUpload) {
+ static const int kAgeMillis = 12345;
+
+ base::DictionaryValue body;
+ body.SetString("key", "value");
+
+ cache_.SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
+ kGroup_, tomorrow());
+ cache_.AddReport(kUrl_, kGroup_, kType_, body.CreateDeepCopy(),
+ clock_.NowTicks(), 0);
+
+ clock_.Advance(base::TimeDelta::FromMilliseconds(kAgeMillis));
+
+ agent_.SendReports();
+
+ ASSERT_EQ(1u, pending_uploads().size());
+ EXPECT_EQ(kEndpoint_, pending_uploads()[0]->url());
+ {
+ auto value = pending_uploads()[0]->GetValue();
+
+ base::ListValue* list;
+ ASSERT_TRUE(value->GetAsList(&list));
+ EXPECT_EQ(1u, list->GetSize());
+
+ base::DictionaryValue* report;
+ ASSERT_TRUE(list->GetDictionary(0, &report));
+ EXPECT_EQ(4u, report->size());
+
+ ExpectDictIntegerValue(kAgeMillis, *report, "age");
+ ExpectDictStringValue(kType_, *report, "type");
+ ExpectDictStringValue(kUrl_.spec(), *report, "url");
+ ExpectDictDictionaryValue(body, *report, "report");
+ }
+ pending_uploads()[0]->Complete(ReportingUploader::Outcome::SUCCESS);
+
+ // Successful upload should remove delivered reports.
+ std::vector<const ReportingReport*> reports;
+ cache_.GetReports(&reports);
+ EXPECT_TRUE(reports.empty());
+
+ // TODO(juliatuttle): Check that BackoffEntry was informed of success.
+}
+
+TEST_F(ReportingDeliveryAgentTest, FailedUpload) {
+ cache_.SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
+ kGroup_, tomorrow());
+ cache_.AddReport(kUrl_, kGroup_, kType_,
+ base::MakeUnique<base::DictionaryValue>(), clock_.NowTicks(),
+ 0);
+
+ agent_.SendReports();
+
+ ASSERT_EQ(1u, pending_uploads().size());
+ pending_uploads()[0]->Complete(ReportingUploader::Outcome::FAILURE);
+
+ // Failed upload should increment reports' attempts.
+ std::vector<const ReportingReport*> reports;
+ cache_.GetReports(&reports);
+ ASSERT_EQ(1u, reports.size());
+ EXPECT_EQ(1, reports[0]->attempts);
+
+ // Since endpoint is now failing, an upload won't be started despite a pending
+ // report.
+ ASSERT_TRUE(pending_uploads().empty());
+ agent_.SendReports();
+ EXPECT_TRUE(pending_uploads().empty());
+}
+
+TEST_F(ReportingDeliveryAgentTest, RemoveEndpointUpload) {
+ static const url::Origin kDifferentOrigin(GURL("https://origin2/"));
+
+ cache_.SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
+ kGroup_, tomorrow());
+ cache_.SetClient(kDifferentOrigin, kEndpoint_,
+ ReportingClient::Subdomains::EXCLUDE, kGroup_, tomorrow());
+ ASSERT_TRUE(FindClientInCache(&cache_, kOrigin_, kEndpoint_));
+ ASSERT_TRUE(FindClientInCache(&cache_, kDifferentOrigin, kEndpoint_));
+
+ cache_.AddReport(kUrl_, kGroup_, kType_,
+ base::MakeUnique<base::DictionaryValue>(), clock_.NowTicks(),
+ 0);
+
+ agent_.SendReports();
+
+ ASSERT_EQ(1u, pending_uploads().size());
+ pending_uploads()[0]->Complete(ReportingUploader::Outcome::REMOVE_ENDPOINT);
+
+ // "Remove endpoint" upload should remove endpoint from *all* origins and
+ // increment reports' attempts.
+ std::vector<const ReportingReport*> reports;
+ cache_.GetReports(&reports);
+ ASSERT_EQ(1u, reports.size());
+ EXPECT_EQ(1, reports[0]->attempts);
+
+ EXPECT_FALSE(FindClientInCache(&cache_, kOrigin_, kEndpoint_));
+ EXPECT_FALSE(FindClientInCache(&cache_, kDifferentOrigin, kEndpoint_));
+
+ // Since endpoint is now failing, an upload won't be started despite a pending
+ // report.
+ agent_.SendReports();
+ EXPECT_TRUE(pending_uploads().empty());
+}
+
+TEST_F(ReportingDeliveryAgentTest, ConcurrentRemove) {
+ cache_.SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
+ kGroup_, tomorrow());
+ cache_.AddReport(kUrl_, kGroup_, kType_,
+ base::MakeUnique<base::DictionaryValue>(), clock_.NowTicks(),
+ 0);
+
+ agent_.SendReports();
+ ASSERT_EQ(1u, pending_uploads().size());
+
+ // Remove the report while the upload is running.
+ std::vector<const ReportingReport*> reports;
+ cache_.GetReports(&reports);
+ EXPECT_EQ(1u, reports.size());
+
+ const ReportingReport* report = reports[0];
+ EXPECT_FALSE(cache_.IsReportDoomedForTesting(report));
+
+ // Report should appear removed, even though the cache has doomed it.
+ cache_.RemoveReports(reports);
+ cache_.GetReports(&reports);
+ EXPECT_TRUE(reports.empty());
+ EXPECT_TRUE(cache_.IsReportDoomedForTesting(report));
+
+ // Completing upload shouldn't crash, and report should still be gone.
+ pending_uploads()[0]->Complete(ReportingUploader::Outcome::SUCCESS);
+ cache_.GetReports(&reports);
+ EXPECT_TRUE(reports.empty());
+ // This is slightly sketchy since |report| has been freed, but it nonetheless
+ // should not be in the set of doomed reports.
+ EXPECT_FALSE(cache_.IsReportDoomedForTesting(report));
+}
+
+// Test that the agent will combine reports destined for the same endpoint, even
+// if the reports are from different origins.
+TEST_F(ReportingDeliveryAgentTest,
+ BatchReportsFromDifferentOriginsToSameEndpoint) {
+ static const GURL kDifferentUrl("https://origin2/path");
+ static const url::Origin kDifferentOrigin(kDifferentUrl);
+
+ cache_.SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
+ kGroup_, tomorrow());
+ cache_.SetClient(kDifferentOrigin, kEndpoint_,
+ ReportingClient::Subdomains::EXCLUDE, kGroup_, tomorrow());
+
+ cache_.AddReport(kUrl_, kGroup_, kType_,
+ base::MakeUnique<base::DictionaryValue>(), clock_.NowTicks(),
+ 0);
+ cache_.AddReport(kDifferentUrl, kGroup_, kType_,
+ base::MakeUnique<base::DictionaryValue>(), clock_.NowTicks(),
+ 0);
+
+ agent_.SendReports();
+ ASSERT_EQ(1u, pending_uploads().size());
+
+ pending_uploads()[0]->Complete(ReportingUploader::Outcome::SUCCESS);
+ EXPECT_EQ(0u, pending_uploads().size());
+}
+
+// Test that the agent won't start a second upload to the same endpoint (even
+// for a different origin) while one is pending, but will once it is no longer
+// pending.
+TEST_F(ReportingDeliveryAgentTest, SerializeUploadsToEndpoint) {
+ static const GURL kDifferentUrl("https://origin2/path");
+ static const url::Origin kDifferentOrigin(kDifferentUrl);
+
+ cache_.SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
+ kGroup_, tomorrow());
+ cache_.SetClient(kDifferentOrigin, kEndpoint_,
+ ReportingClient::Subdomains::EXCLUDE, kGroup_, tomorrow());
+
+ cache_.AddReport(kUrl_, kGroup_, kType_,
+ base::MakeUnique<base::DictionaryValue>(), clock_.NowTicks(),
+ 0);
+
+ agent_.SendReports();
+ EXPECT_EQ(1u, pending_uploads().size());
+
+ cache_.AddReport(kDifferentUrl, kGroup_, kType_,
+ base::MakeUnique<base::DictionaryValue>(), clock_.NowTicks(),
+ 0);
+
+ agent_.SendReports();
+ ASSERT_EQ(1u, pending_uploads().size());
+
+ pending_uploads()[0]->Complete(ReportingUploader::Outcome::SUCCESS);
+ EXPECT_EQ(0u, pending_uploads().size());
+
+ agent_.SendReports();
+ ASSERT_EQ(1u, pending_uploads().size());
+
+ pending_uploads()[0]->Complete(ReportingUploader::Outcome::SUCCESS);
+ EXPECT_EQ(0u, pending_uploads().size());
+}
+
+// Test that the agent won't start a second upload for an (origin, group) while
+// one is pending, even if a different endpoint is available, but will once the
+// original delivery is complete and the (origin, group) is no longer pending.
+TEST_F(ReportingDeliveryAgentTest, SerializeUploadsToGroup) {
+ static const GURL kDifferentEndpoint("https://endpoint2/");
+
+ cache_.SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
+ kGroup_, tomorrow());
+ cache_.SetClient(kOrigin_, kDifferentEndpoint,
+ ReportingClient::Subdomains::EXCLUDE, kGroup_, tomorrow());
+
+ cache_.AddReport(kUrl_, kGroup_, kType_,
+ base::MakeUnique<base::DictionaryValue>(), clock_.NowTicks(),
+ 0);
+
+ agent_.SendReports();
+ EXPECT_EQ(1u, pending_uploads().size());
+
+ cache_.AddReport(kUrl_, kGroup_, kType_,
+ base::MakeUnique<base::DictionaryValue>(), clock_.NowTicks(),
+ 0);
+
+ agent_.SendReports();
+ EXPECT_EQ(1u, pending_uploads().size());
+
+ pending_uploads()[0]->Complete(ReportingUploader::Outcome::SUCCESS);
+ EXPECT_EQ(0u, pending_uploads().size());
+
+ agent_.SendReports();
+ EXPECT_EQ(1u, pending_uploads().size());
+}
+
+// Tests that the agent will start parallel uploads to different groups within
+// the same origin.
+TEST_F(ReportingDeliveryAgentTest, ParallelizeUploadsAcrossGroups) {
+ static const GURL kDifferentEndpoint("https://endpoint2/");
+ static const std::string kDifferentGroup("group2");
+
+ cache_.SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
+ kGroup_, tomorrow());
+ cache_.SetClient(kOrigin_, kDifferentEndpoint,
+ ReportingClient::Subdomains::EXCLUDE, kDifferentGroup,
+ tomorrow());
+
+ cache_.AddReport(kUrl_, kGroup_, kType_,
+ base::MakeUnique<base::DictionaryValue>(), clock_.NowTicks(),
+ 0);
+ cache_.AddReport(kUrl_, kDifferentGroup, kType_,
+ base::MakeUnique<base::DictionaryValue>(), clock_.NowTicks(),
+ 0);
+
+ agent_.SendReports();
+ EXPECT_EQ(2u, pending_uploads().size());
+
+ pending_uploads()[1]->Complete(ReportingUploader::Outcome::SUCCESS);
+ pending_uploads()[0]->Complete(ReportingUploader::Outcome::SUCCESS);
+ EXPECT_EQ(0u, pending_uploads().size());
+}
+
+} // namespace
+} // namespace net
« no previous file with comments | « net/reporting/reporting_delivery_agent.cc ('k') | net/reporting/reporting_report.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698