1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/offline_pages/android/offline_page_auto_fetcher_service.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "base/callback_helpers.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/test/bind.h"
13 #include "base/test/mock_callback.h"
14 #include "chrome/browser/offline_pages/offline_page_model_factory.h"
15 #include "chrome/browser/offline_pages/request_coordinator_factory.h"
16 #include "chrome/browser/offline_pages/test_request_coordinator_builder.h"
17 #include "chrome/common/offline_page_auto_fetcher.mojom.h"
18 #include "components/offline_pages/core/auto_fetch.h"
19 #include "components/offline_pages/core/background/request_coordinator.h"
20 #include "components/offline_pages/core/background/request_coordinator_stub_taco.h"
21 #include "components/offline_pages/core/background/test_request_queue_store.h"
22 #include "components/offline_pages/core/client_namespace_constants.h"
23 #include "components/offline_pages/core/stub_offline_page_model.h"
24 #include "content/public/test/browser_task_environment.h"
25 #include "content/public/test/navigation_simulator.h"
26 #include "testing/gmock/include/gmock/gmock.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28
29 namespace offline_pages {
30 namespace {
31 using testing::_;
32 using OfflinePageAutoFetcherScheduleResult =
33 chrome::mojom::OfflinePageAutoFetcherScheduleResult;
34
35 const int kTabId = 1;
TestClientId()36 ClientId TestClientId() {
37 return auto_fetch::MakeClientId(auto_fetch::ClientIdMetadata(kTabId));
38 }
TestRequest(ClientId client_id=TestClientId ())39 SavePageRequest TestRequest(ClientId client_id = TestClientId()) {
40 return SavePageRequest(123, GURL("http://www.url.com"), client_id,
41 base::Time(), true);
42 }
43
44 class MockDelegate : public OfflinePageAutoFetcherService::Delegate {
45 public:
46 MOCK_METHOD5(ShowAutoFetchCompleteNotification,
47 void(const base::string16& pageTitle,
48 const std::string& original_url,
49 const std::string& final_url,
50 int android_tab_id,
51 int64_t offline_id));
52 };
53
54 class TestOfflinePageModel : public StubOfflinePageModel {
55 public:
56 // Change signature for the mocked method to make it easier to use.
57 MOCK_METHOD1(GetPageByOfflineId_, OfflinePageItem*(int64_t offline_id));
GetPageByOfflineId(int64_t offline_id,SingleOfflinePageItemCallback callback)58 void GetPageByOfflineId(int64_t offline_id,
59 SingleOfflinePageItemCallback callback) override {
60 std::move(callback).Run(GetPageByOfflineId_(offline_id));
61 }
62 };
63
64 class OfflinePageAutoFetcherServiceTest : public testing::Test {
65 public:
SetUp()66 void SetUp() override {
67 request_coordinator_taco_.CreateRequestCoordinator();
68 service_ = std::make_unique<OfflinePageAutoFetcherService>(
69 request_coordinator(), &offline_page_model_, &delegate_);
70 }
71
TearDown()72 void TearDown() override {
73 task_environment_.RunUntilIdle();
74 service_.reset();
75 }
request_coordinator()76 RequestCoordinator* request_coordinator() {
77 return request_coordinator_taco_.request_coordinator();
78 }
queue_store()79 TestRequestQueueStore* queue_store() {
80 return static_cast<TestRequestQueueStore*>(
81 request_coordinator()->queue_for_testing()->GetStoreForTesting());
82 }
83
GetRequestsSync()84 std::vector<std::unique_ptr<SavePageRequest>> GetRequestsSync() {
85 bool completed = false;
86 std::vector<std::unique_ptr<SavePageRequest>> result;
87 queue_store()->GetRequests(base::BindLambdaForTesting(
88 [&](bool success,
89 std::vector<std::unique_ptr<SavePageRequest>> requests) {
90 completed = true;
91 result = std::move(requests);
92 }));
93 task_environment_.RunUntilIdle();
94 CHECK(completed);
95 return result;
96 }
97
98 protected:
99 content::BrowserTaskEnvironment task_environment_;
100 MockDelegate delegate_;
101 TestOfflinePageModel offline_page_model_;
102
103 RequestCoordinatorStubTaco request_coordinator_taco_;
104 std::unique_ptr<OfflinePageAutoFetcherService> service_;
105 };
106
TEST_F(OfflinePageAutoFetcherServiceTest,TryScheduleSuccess)107 TEST_F(OfflinePageAutoFetcherServiceTest, TryScheduleSuccess) {
108 base::MockCallback<
109 base::OnceCallback<void(OfflinePageAutoFetcherScheduleResult)>>
110 result_callback;
111 EXPECT_CALL(result_callback,
112 Run(OfflinePageAutoFetcherScheduleResult::kScheduled));
113 service_->TrySchedule(false, GURL("http://foo.com"), kTabId,
114 result_callback.Get());
115 task_environment_.RunUntilIdle();
116 EXPECT_EQ(1ul, GetRequestsSync().size());
117 }
118
TEST_F(OfflinePageAutoFetcherServiceTest,AttemptInvalidURL)119 TEST_F(OfflinePageAutoFetcherServiceTest, AttemptInvalidURL) {
120 base::MockCallback<
121 base::OnceCallback<void(OfflinePageAutoFetcherScheduleResult)>>
122 result_callback;
123 EXPECT_CALL(result_callback,
124 Run(OfflinePageAutoFetcherScheduleResult::kOtherError));
125 service_->TrySchedule(false, GURL("ftp://foo.com"), kTabId,
126 result_callback.Get());
127 task_environment_.RunUntilIdle();
128 EXPECT_EQ(0ul, GetRequestsSync().size());
129 }
130
TEST_F(OfflinePageAutoFetcherServiceTest,TryScheduleDuplicate)131 TEST_F(OfflinePageAutoFetcherServiceTest, TryScheduleDuplicate) {
132 base::MockCallback<
133 base::RepeatingCallback<void(OfflinePageAutoFetcherScheduleResult)>>
134 result_callback;
135 EXPECT_CALL(result_callback,
136 Run(OfflinePageAutoFetcherScheduleResult::kScheduled))
137 .Times(1);
138 EXPECT_CALL(result_callback,
139 Run(OfflinePageAutoFetcherScheduleResult::kAlreadyScheduled))
140 .Times(1);
141 // The page should only be saved once, because the fragment is ignored.
142 service_->TrySchedule(false, GURL("http://foo.com#A"), kTabId,
143 result_callback.Get());
144 service_->TrySchedule(false, GURL("http://foo.com#Z"), kTabId,
145 result_callback.Get());
146 task_environment_.RunUntilIdle();
147 EXPECT_EQ(1ul, GetRequestsSync().size());
148 }
149
TEST_F(OfflinePageAutoFetcherServiceTest,AttemptAutoScheduleMoreThanMaximum)150 TEST_F(OfflinePageAutoFetcherServiceTest, AttemptAutoScheduleMoreThanMaximum) {
151 base::MockCallback<
152 base::RepeatingCallback<void(OfflinePageAutoFetcherScheduleResult)>>
153 result_callback;
154 testing::InSequence in_sequence;
155 EXPECT_CALL(result_callback,
156 Run(OfflinePageAutoFetcherScheduleResult::kScheduled))
157 .Times(3);
158 EXPECT_CALL(result_callback,
159 Run(OfflinePageAutoFetcherScheduleResult::kNotEnoughQuota))
160 .Times(1);
161 EXPECT_CALL(result_callback,
162 Run(OfflinePageAutoFetcherScheduleResult::kScheduled))
163 .Times(1);
164
165 // Three requests within quota.
166 service_->TrySchedule(false, GURL("http://foo.com/1"), kTabId,
167 result_callback.Get());
168 service_->TrySchedule(false, GURL("http://foo.com/2"), kTabId,
169 result_callback.Get());
170 service_->TrySchedule(false, GURL("http://foo.com/3"), kTabId,
171 result_callback.Get());
172
173 // Quota is exhausted.
174 service_->TrySchedule(false, GURL("http://foo.com/4"), kTabId,
175 result_callback.Get());
176
177 // User-requested, quota is not enforced.
178 service_->TrySchedule(true, GURL("http://foo.com/5"), kTabId,
179 result_callback.Get());
180
181 task_environment_.RunUntilIdle();
182 }
183
TEST_F(OfflinePageAutoFetcherServiceTest,TryScheduleMoreThanMaximumUserRequested)184 TEST_F(OfflinePageAutoFetcherServiceTest,
185 TryScheduleMoreThanMaximumUserRequested) {
186 base::MockCallback<
187 base::RepeatingCallback<void(OfflinePageAutoFetcherScheduleResult)>>
188 result_callback;
189 EXPECT_CALL(result_callback,
190 Run(OfflinePageAutoFetcherScheduleResult::kScheduled))
191 .Times(4);
192 service_->TrySchedule(true, GURL("http://foo.com/1"), kTabId,
193 result_callback.Get());
194 service_->TrySchedule(true, GURL("http://foo.com/2"), kTabId,
195 result_callback.Get());
196 service_->TrySchedule(true, GURL("http://foo.com/3"), kTabId,
197 result_callback.Get());
198 service_->TrySchedule(true, GURL("http://foo.com/4"), kTabId,
199 result_callback.Get());
200 task_environment_.RunUntilIdle();
201 }
202
TEST_F(OfflinePageAutoFetcherServiceTest,CancelSuccess)203 TEST_F(OfflinePageAutoFetcherServiceTest, CancelSuccess) {
204 service_->TrySchedule(false, GURL("http://foo.com"), kTabId,
205 base::DoNothing());
206 task_environment_.RunUntilIdle();
207 service_->CancelSchedule(GURL("http://foo.com"));
208 task_environment_.RunUntilIdle();
209 EXPECT_EQ(0ul, GetRequestsSync().size());
210 }
211
TEST_F(OfflinePageAutoFetcherServiceTest,CancelNotExist)212 TEST_F(OfflinePageAutoFetcherServiceTest, CancelNotExist) {
213 service_->TrySchedule(false, GURL("http://foo.com"), kTabId,
214 base::DoNothing());
215 task_environment_.RunUntilIdle();
216 service_->CancelSchedule(GURL("http://NOT-FOO.com"));
217 task_environment_.RunUntilIdle();
218 EXPECT_EQ(1ul, GetRequestsSync().size());
219 }
220
TEST_F(OfflinePageAutoFetcherServiceTest,CancelQueueEmpty)221 TEST_F(OfflinePageAutoFetcherServiceTest, CancelQueueEmpty) {
222 service_->CancelSchedule(GURL("http://foo.com"));
223 task_environment_.RunUntilIdle();
224 }
225
226 // Simulate a completed auto fetch request, and verify that
227 // ShowAutoFetchCompleteNotification() is called on the delegate.
TEST_F(OfflinePageAutoFetcherServiceTest,NotifyOnAutoFetchCompleted)228 TEST_F(OfflinePageAutoFetcherServiceTest, NotifyOnAutoFetchCompleted) {
229 const SavePageRequest kTestRequest = TestRequest();
230 const int64_t kOfflineId = 1234;
231 OfflinePageItem returned_item(kTestRequest.url(), kOfflineId,
232 kTestRequest.client_id(), base::FilePath(),
233 2000);
234 returned_item.title = base::ASCIIToUTF16("Cows");
235 EXPECT_CALL(offline_page_model_,
236 GetPageByOfflineId_(kTestRequest.request_id()))
237 .WillOnce(testing::Return(&returned_item));
238 EXPECT_CALL(delegate_, ShowAutoFetchCompleteNotification(
239 returned_item.title, kTestRequest.url().spec(),
240 kTestRequest.url().spec(), kTabId, kOfflineId));
241 service_->OnCompleted(kTestRequest,
242 RequestNotifier::BackgroundSavePageResult::SUCCESS);
243 task_environment_.RunUntilIdle();
244 }
245
246 // Simulate a failed auto-fetch request, and verify that
247 // it is ignored.
TEST_F(OfflinePageAutoFetcherServiceTest,DontNotifyOnAutoFetchFail)248 TEST_F(OfflinePageAutoFetcherServiceTest, DontNotifyOnAutoFetchFail) {
249 const SavePageRequest kTestRequest = TestRequest();
250 EXPECT_CALL(offline_page_model_, GetPageByOfflineId_(_)).Times(0);
251 service_->OnCompleted(
252 kTestRequest, RequestNotifier::BackgroundSavePageResult::LOADING_FAILURE);
253 task_environment_.RunUntilIdle();
254 }
255
256 // Simulate a completed non-auto-fetch request, and verify that
257 // it is ignored.
TEST_F(OfflinePageAutoFetcherServiceTest,DontNotifyOnOtherRequestCompleted)258 TEST_F(OfflinePageAutoFetcherServiceTest, DontNotifyOnOtherRequestCompleted) {
259 const ClientId kClientId("other-namespace", "id");
260 const SavePageRequest kTestRequest = TestRequest(kClientId);
261 EXPECT_CALL(offline_page_model_, GetPageByOfflineId_(_)).Times(0);
262 service_->OnCompleted(kTestRequest,
263 RequestNotifier::BackgroundSavePageResult::SUCCESS);
264
265 task_environment_.RunUntilIdle();
266 }
267
268 } // namespace
269 } // namespace offline_pages
270