[email protected] | 1eab4e9 | 2014-05-09 02:17:19 | [diff] [blame] | 1 | // Copyright 2014 The Chromium Authors. All rights reserved. |
[email protected] | 4837225 | 2013-12-20 12:18:01 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
[email protected] | 1eab4e9 | 2014-05-09 02:17:19 | [diff] [blame] | 5 | #include "components/feedback/feedback_uploader.h" |
[email protected] | 4837225 | 2013-12-20 12:18:01 | [diff] [blame] | 6 | |
dcheng | 84c358e | 2016-04-26 07:05:53 | [diff] [blame] | 7 | #include <memory> |
[email protected] | 90cc2a9a | 2014-02-28 22:05:55 | [diff] [blame] | 8 | #include <set> |
| 9 | |
[email protected] | 4837225 | 2013-12-20 12:18:01 | [diff] [blame] | 10 | #include "base/bind.h" |
[email protected] | 4837225 | 2013-12-20 12:18:01 | [diff] [blame] | 11 | #include "base/run_loop.h" |
Ahmed Fakhry | 7ff0cdb | 2017-08-22 20:11:45 | [diff] [blame] | 12 | #include "base/single_thread_task_runner.h" |
[email protected] | 1eab4e9 | 2014-05-09 02:17:19 | [diff] [blame] | 13 | #include "base/stl_util.h" |
Gabriel Charette | 44db142 | 2018-08-06 11:19:33 | [diff] [blame] | 14 | #include "base/task/post_task.h" |
| 15 | #include "base/task/task_traits.h" |
Ahmed Fakhry | 225aa5c | 2018-06-14 03:22:02 | [diff] [blame] | 16 | #include "base/threading/sequenced_task_runner_handle.h" |
Ahmed Fakhry | af8ab05 | 2017-07-21 21:39:07 | [diff] [blame] | 17 | #include "components/feedback/feedback_report.h" |
[email protected] | 1eab4e9 | 2014-05-09 02:17:19 | [diff] [blame] | 18 | #include "components/feedback/feedback_uploader_factory.h" |
Gabriel Charette | c710874 | 2019-08-23 03:31:40 | [diff] [blame] | 19 | #include "content/public/test/browser_task_environment.h" |
[email protected] | 1eab4e9 | 2014-05-09 02:17:19 | [diff] [blame] | 20 | #include "content/public/test/test_browser_context.h" |
Robbie McElrath | d956f86 | 2018-07-09 22:01:02 | [diff] [blame] | 21 | #include "services/network/public/cpp/shared_url_loader_factory.h" |
| 22 | #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" |
| 23 | #include "services/network/test/test_url_loader_factory.h" |
[email protected] | 4837225 | 2013-12-20 12:18:01 | [diff] [blame] | 24 | #include "testing/gtest/include/gtest/gtest.h" |
| 25 | |
Ahmed Fakhry | af8ab05 | 2017-07-21 21:39:07 | [diff] [blame] | 26 | namespace feedback { |
| 27 | |
[email protected] | 4837225 | 2013-12-20 12:18:01 | [diff] [blame] | 28 | namespace { |
| 29 | |
Ahmed Fakhry | af8ab05 | 2017-07-21 21:39:07 | [diff] [blame] | 30 | constexpr char kReportOne[] = "one"; |
| 31 | constexpr char kReportTwo[] = "two"; |
| 32 | constexpr char kReportThree[] = "three"; |
| 33 | constexpr char kReportFour[] = "four"; |
| 34 | constexpr char kReportFive[] = "five"; |
[email protected] | 4837225 | 2013-12-20 12:18:01 | [diff] [blame] | 35 | |
Ahmed Fakhry | af8ab05 | 2017-07-21 21:39:07 | [diff] [blame] | 36 | constexpr base::TimeDelta kRetryDelayForTest = |
[email protected] | 4837225 | 2013-12-20 12:18:01 | [diff] [blame] | 37 | base::TimeDelta::FromMilliseconds(100); |
| 38 | |
Ahmed Fakhry | 7ff0cdb | 2017-08-22 20:11:45 | [diff] [blame] | 39 | class MockFeedbackUploader : public FeedbackUploader { |
Ahmed Fakhry | af8ab05 | 2017-07-21 21:39:07 | [diff] [blame] | 40 | public: |
Robbie McElrath | d956f86 | 2018-07-09 22:01:02 | [diff] [blame] | 41 | MockFeedbackUploader( |
| 42 | scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, |
| 43 | content::BrowserContext* context) |
Evan Stade | c34903a | 2019-10-09 01:15:36 | [diff] [blame] | 44 | : FeedbackUploader(context, |
| 45 | FeedbackUploaderFactory::CreateUploaderTaskRunner()) { |
| 46 | set_url_loader_factory_for_test(url_loader_factory); |
| 47 | } |
Ahmed Fakhry | af8ab05 | 2017-07-21 21:39:07 | [diff] [blame] | 48 | ~MockFeedbackUploader() override {} |
| 49 | |
| 50 | void RunMessageLoop() { |
| 51 | if (ProcessingComplete()) |
| 52 | return; |
Gyuyoung Kim | 6afb508 | 2018-01-19 13:35:57 | [diff] [blame] | 53 | run_loop_ = std::make_unique<base::RunLoop>(); |
Ahmed Fakhry | af8ab05 | 2017-07-21 21:39:07 | [diff] [blame] | 54 | run_loop_->Run(); |
| 55 | } |
| 56 | |
Ahmed Fakhry | 225aa5c | 2018-06-14 03:22:02 | [diff] [blame] | 57 | void SimulateLoadingOfflineReports() { |
| 58 | task_runner()->PostTask( |
| 59 | FROM_HERE, |
| 60 | base::BindOnce( |
| 61 | &FeedbackReport::LoadReportsAndQueue, feedback_reports_path(), |
Ahmed Fakhry | daa7d423 | 2018-07-02 18:11:36 | [diff] [blame] | 62 | base::BindRepeating(&MockFeedbackUploader::QueueSingleReport, |
Jeffrey Kardatzke | be95a711 | 2019-08-29 19:38:30 | [diff] [blame] | 63 | base::SequencedTaskRunnerHandle::Get(), |
| 64 | AsWeakPtr()))); |
Ahmed Fakhry | 225aa5c | 2018-06-14 03:22:02 | [diff] [blame] | 65 | } |
| 66 | |
Ahmed Fakhry | af8ab05 | 2017-07-21 21:39:07 | [diff] [blame] | 67 | const std::map<std::string, unsigned int>& dispatched_reports() const { |
| 68 | return dispatched_reports_; |
| 69 | } |
| 70 | void set_expected_reports(size_t value) { expected_reports_ = value; } |
| 71 | void set_simulate_failure(bool value) { simulate_failure_ = value; } |
| 72 | |
| 73 | private: |
Ahmed Fakhry | 225aa5c | 2018-06-14 03:22:02 | [diff] [blame] | 74 | static void QueueSingleReport( |
| 75 | scoped_refptr<base::SequencedTaskRunner> main_task_runner, |
Jeffrey Kardatzke | be95a711 | 2019-08-29 19:38:30 | [diff] [blame] | 76 | base::WeakPtr<FeedbackUploader> uploader, |
| 77 | scoped_refptr<FeedbackReport> report) { |
Ahmed Fakhry | 225aa5c | 2018-06-14 03:22:02 | [diff] [blame] | 78 | main_task_runner->PostTask( |
Jeffrey Kardatzke | be95a711 | 2019-08-29 19:38:30 | [diff] [blame] | 79 | FROM_HERE, base::BindOnce(&MockFeedbackUploader::RequeueReport, |
| 80 | std::move(uploader), std::move(report))); |
Ahmed Fakhry | 225aa5c | 2018-06-14 03:22:02 | [diff] [blame] | 81 | } |
| 82 | |
Ahmed Fakhry | af8ab05 | 2017-07-21 21:39:07 | [diff] [blame] | 83 | // FeedbackUploaderChrome: |
Ahmed Fakhry | 7ff0cdb | 2017-08-22 20:11:45 | [diff] [blame] | 84 | void StartDispatchingReport() override { |
Jan Wilken Dörrie | 45d34f4 | 2019-06-08 09:40:54 | [diff] [blame] | 85 | if (base::Contains(dispatched_reports_, report_being_dispatched()->data())) |
Ahmed Fakhry | 7ff0cdb | 2017-08-22 20:11:45 | [diff] [blame] | 86 | dispatched_reports_[report_being_dispatched()->data()]++; |
Ahmed Fakhry | af8ab05 | 2017-07-21 21:39:07 | [diff] [blame] | 87 | else |
Ahmed Fakhry | 7ff0cdb | 2017-08-22 20:11:45 | [diff] [blame] | 88 | dispatched_reports_[report_being_dispatched()->data()] = 1; |
Ahmed Fakhry | af8ab05 | 2017-07-21 21:39:07 | [diff] [blame] | 89 | |
| 90 | dispatched_reports_count_++; |
| 91 | |
| 92 | if (simulate_failure_) |
Ahmed Fakhry | 7ff0cdb | 2017-08-22 20:11:45 | [diff] [blame] | 93 | OnReportUploadFailure(true /* should_retry */); |
Ahmed Fakhry | af8ab05 | 2017-07-21 21:39:07 | [diff] [blame] | 94 | else |
| 95 | OnReportUploadSuccess(); |
| 96 | |
| 97 | if (ProcessingComplete()) { |
| 98 | if (run_loop_) |
| 99 | run_loop_->Quit(); |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | bool ProcessingComplete() { |
| 104 | return (dispatched_reports_count_ >= expected_reports_); |
| 105 | } |
| 106 | |
| 107 | std::unique_ptr<base::RunLoop> run_loop_; |
| 108 | std::map<std::string, unsigned int> dispatched_reports_; |
| 109 | size_t dispatched_reports_count_ = 0; |
| 110 | size_t expected_reports_ = 0; |
| 111 | bool simulate_failure_ = false; |
| 112 | |
| 113 | DISALLOW_COPY_AND_ASSIGN(MockFeedbackUploader); |
| 114 | }; |
| 115 | |
[email protected] | 4837225 | 2013-12-20 12:18:01 | [diff] [blame] | 116 | } // namespace |
| 117 | |
[email protected] | 4837225 | 2013-12-20 12:18:01 | [diff] [blame] | 118 | class FeedbackUploaderTest : public testing::Test { |
Ahmed Fakhry | af8ab05 | 2017-07-21 21:39:07 | [diff] [blame] | 119 | public: |
Ahmed Fakhry | 7ff0cdb | 2017-08-22 20:11:45 | [diff] [blame] | 120 | FeedbackUploaderTest() { |
Ahmed Fakhry | af8ab05 | 2017-07-21 21:39:07 | [diff] [blame] | 121 | FeedbackUploader::SetMinimumRetryDelayForTesting(kRetryDelayForTest); |
Robbie McElrath | d956f86 | 2018-07-09 22:01:02 | [diff] [blame] | 122 | test_shared_loader_factory_ = |
| 123 | base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( |
| 124 | &test_url_loader_factory_); |
Ahmed Fakhry | 225aa5c | 2018-06-14 03:22:02 | [diff] [blame] | 125 | RecreateUploader(); |
[email protected] | 4837225 | 2013-12-20 12:18:01 | [diff] [blame] | 126 | } |
| 127 | |
Ahmed Fakhry | 7ff0cdb | 2017-08-22 20:11:45 | [diff] [blame] | 128 | ~FeedbackUploaderTest() override = default; |
[email protected] | 4837225 | 2013-12-20 12:18:01 | [diff] [blame] | 129 | |
Ahmed Fakhry | 225aa5c | 2018-06-14 03:22:02 | [diff] [blame] | 130 | void RecreateUploader() { |
Robbie McElrath | d956f86 | 2018-07-09 22:01:02 | [diff] [blame] | 131 | uploader_ = std::make_unique<MockFeedbackUploader>( |
| 132 | test_shared_loader_factory_, &context_); |
Ahmed Fakhry | 225aa5c | 2018-06-14 03:22:02 | [diff] [blame] | 133 | } |
| 134 | |
[email protected] | 4837225 | 2013-12-20 12:18:01 | [diff] [blame] | 135 | void QueueReport(const std::string& data) { |
Ahmed Fakhry | daa7d423 | 2018-07-02 18:11:36 | [diff] [blame] | 136 | uploader_->QueueReport(std::make_unique<std::string>(data)); |
[email protected] | 4837225 | 2013-12-20 12:18:01 | [diff] [blame] | 137 | } |
| 138 | |
Ahmed Fakhry | 7ff0cdb | 2017-08-22 20:11:45 | [diff] [blame] | 139 | MockFeedbackUploader* uploader() const { return uploader_.get(); } |
[email protected] | 4837225 | 2013-12-20 12:18:01 | [diff] [blame] | 140 | |
Ahmed Fakhry | af8ab05 | 2017-07-21 21:39:07 | [diff] [blame] | 141 | private: |
Robbie McElrath | d956f86 | 2018-07-09 22:01:02 | [diff] [blame] | 142 | network::TestURLLoaderFactory test_url_loader_factory_; |
| 143 | scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_; |
Gabriel Charette | 798fde7 | 2019-08-20 22:24:04 | [diff] [blame] | 144 | content::BrowserTaskEnvironment task_environment_; |
Ahmed Fakhry | 7ff0cdb | 2017-08-22 20:11:45 | [diff] [blame] | 145 | content::TestBrowserContext context_; |
| 146 | std::unique_ptr<MockFeedbackUploader> uploader_; |
[email protected] | 4837225 | 2013-12-20 12:18:01 | [diff] [blame] | 147 | |
Ahmed Fakhry | af8ab05 | 2017-07-21 21:39:07 | [diff] [blame] | 148 | DISALLOW_COPY_AND_ASSIGN(FeedbackUploaderTest); |
[email protected] | 4837225 | 2013-12-20 12:18:01 | [diff] [blame] | 149 | }; |
| 150 | |
Ahmed Fakhry | af8ab05 | 2017-07-21 21:39:07 | [diff] [blame] | 151 | TEST_F(FeedbackUploaderTest, QueueMultiple) { |
[email protected] | 4837225 | 2013-12-20 12:18:01 | [diff] [blame] | 152 | QueueReport(kReportOne); |
| 153 | QueueReport(kReportTwo); |
| 154 | QueueReport(kReportThree); |
| 155 | QueueReport(kReportFour); |
| 156 | |
Ahmed Fakhry | af8ab05 | 2017-07-21 21:39:07 | [diff] [blame] | 157 | EXPECT_EQ(uploader()->dispatched_reports().size(), 4u); |
| 158 | EXPECT_EQ(uploader()->dispatched_reports().at(kReportOne), 1u); |
| 159 | EXPECT_EQ(uploader()->dispatched_reports().at(kReportTwo), 1u); |
| 160 | EXPECT_EQ(uploader()->dispatched_reports().at(kReportThree), 1u); |
| 161 | EXPECT_EQ(uploader()->dispatched_reports().at(kReportFour), 1u); |
[email protected] | 4837225 | 2013-12-20 12:18:01 | [diff] [blame] | 162 | } |
| 163 | |
Ahmed Fakhry | af8ab05 | 2017-07-21 21:39:07 | [diff] [blame] | 164 | TEST_F(FeedbackUploaderTest, QueueMultipleWithFailures) { |
| 165 | EXPECT_EQ(kRetryDelayForTest, uploader()->retry_delay()); |
[email protected] | 4837225 | 2013-12-20 12:18:01 | [diff] [blame] | 166 | QueueReport(kReportOne); |
[email protected] | 4837225 | 2013-12-20 12:18:01 | [diff] [blame] | 167 | |
Ahmed Fakhry | af8ab05 | 2017-07-21 21:39:07 | [diff] [blame] | 168 | // Simulate a failure in reports two and three. Make sure the backoff delay |
| 169 | // will be applied twice, and the reports will eventually be sent. |
| 170 | uploader()->set_simulate_failure(true); |
| 171 | QueueReport(kReportTwo); |
| 172 | EXPECT_EQ(kRetryDelayForTest * 2, uploader()->retry_delay()); |
| 173 | QueueReport(kReportThree); |
| 174 | EXPECT_EQ(kRetryDelayForTest * 4, uploader()->retry_delay()); |
| 175 | uploader()->set_simulate_failure(false); |
| 176 | |
| 177 | // Once a successful report is sent, the backoff delay is reset back to its |
| 178 | // minimum value. |
| 179 | QueueReport(kReportFour); |
| 180 | EXPECT_EQ(kRetryDelayForTest, uploader()->retry_delay()); |
[email protected] | 4837225 | 2013-12-20 12:18:01 | [diff] [blame] | 181 | QueueReport(kReportFive); |
| 182 | |
Ahmed Fakhry | af8ab05 | 2017-07-21 21:39:07 | [diff] [blame] | 183 | // Wait for the pending two failed reports to be sent. |
| 184 | uploader()->set_expected_reports(7); |
| 185 | uploader()->RunMessageLoop(); |
[email protected] | 4837225 | 2013-12-20 12:18:01 | [diff] [blame] | 186 | |
Ahmed Fakhry | af8ab05 | 2017-07-21 21:39:07 | [diff] [blame] | 187 | EXPECT_EQ(uploader()->dispatched_reports().size(), 5u); |
| 188 | EXPECT_EQ(uploader()->dispatched_reports().at(kReportOne), 1u); |
| 189 | EXPECT_EQ(uploader()->dispatched_reports().at(kReportTwo), 2u); |
| 190 | EXPECT_EQ(uploader()->dispatched_reports().at(kReportThree), 2u); |
| 191 | EXPECT_EQ(uploader()->dispatched_reports().at(kReportFour), 1u); |
| 192 | EXPECT_EQ(uploader()->dispatched_reports().at(kReportFive), 1u); |
[email protected] | 4837225 | 2013-12-20 12:18:01 | [diff] [blame] | 193 | } |
| 194 | |
Ahmed Fakhry | 225aa5c | 2018-06-14 03:22:02 | [diff] [blame] | 195 | TEST_F(FeedbackUploaderTest, SimulateOfflineReports) { |
| 196 | // Simulate offline reports by failing to upload three reports. |
| 197 | uploader()->set_simulate_failure(true); |
| 198 | QueueReport(kReportOne); |
| 199 | QueueReport(kReportTwo); |
| 200 | QueueReport(kReportThree); |
| 201 | |
| 202 | // All three reports will be attempted to be uploaded, but the uploader queue |
| 203 | // will remain having three reports since they all failed. |
| 204 | uploader()->set_expected_reports(3); |
| 205 | uploader()->RunMessageLoop(); |
| 206 | EXPECT_EQ(uploader()->dispatched_reports().size(), 3u); |
| 207 | EXPECT_FALSE(uploader()->QueueEmpty()); |
| 208 | |
| 209 | // Simulate a sign out / resign in by recreating the uploader. This should not |
| 210 | // clear any pending feedback report files on disk, and hence they can be |
| 211 | // reloaded. |
| 212 | RecreateUploader(); |
| 213 | uploader()->SimulateLoadingOfflineReports(); |
| 214 | uploader()->set_expected_reports(3); |
| 215 | uploader()->RunMessageLoop(); |
| 216 | |
| 217 | // The three reports were loaded, successfully uploaded, and the uploader |
| 218 | // queue is now empty. |
| 219 | EXPECT_EQ(uploader()->dispatched_reports().size(), 3u); |
| 220 | EXPECT_TRUE(uploader()->QueueEmpty()); |
| 221 | } |
| 222 | |
[email protected] | 4837225 | 2013-12-20 12:18:01 | [diff] [blame] | 223 | } // namespace feedback |