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