1 // Copyright 2019 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/download/offline_item_utils.h"
6
7 #include <memory>
8 #include <utility>
9 #include <vector>
10
11 #include "components/download/public/common/download_utils.h"
12 #include "components/download/public/common/mock_download_item.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14
15 using ContentId = offline_items_collection::ContentId;
16 using OfflineItem = offline_items_collection::OfflineItem;
17 using OfflineItemFilter = offline_items_collection::OfflineItemFilter;
18 using OfflineItemState = offline_items_collection::OfflineItemState;
19 using OfflineItemProgressUnit =
20 offline_items_collection::OfflineItemProgressUnit;
21 using OfflineItemSchedule = offline_items_collection::OfflineItemSchedule;
22 using FailState = offline_items_collection::FailState;
23 using PendingState = offline_items_collection::PendingState;
24 using DownloadItem = download::DownloadItem;
25 using DownloadSchedule = download::DownloadSchedule;
26
27 using ::testing::_;
28 using ::testing::Return;
29 using ::testing::ReturnRefOfCopy;
30
31 namespace {
32
33 const char kNameSpace[] = "LEGACY_DOWNLOAD";
34
35 // TODO(https://crbug.com/1042727): Fix test GURL scoping and remove this getter
36 // function.
TestUrl()37 GURL TestUrl() {
38 return GURL("http://www.example.com");
39 }
TestOriginalUrl()40 GURL TestOriginalUrl() {
41 return GURL("http://www.exampleoriginalurl.com");
42 }
43
44 } // namespace
45
46 class OfflineItemUtilsTest : public testing::Test {
47 public:
48 OfflineItemUtilsTest() = default;
49 ~OfflineItemUtilsTest() override = default;
50
51 protected:
52 std::unique_ptr<download::MockDownloadItem> CreateDownloadItem(
53 const std::string& guid,
54 const base::FilePath& file_path,
55 const base::FilePath& file_name,
56 const std::string& mime_type,
57 DownloadItem::DownloadState state,
58 bool is_paused,
59 bool is_dangerous,
60 const base::Time& creation_time,
61 const base::Time& last_accessed_time,
62 int64_t received_bytes,
63 int64_t total_bytes,
64 download::DownloadInterruptReason interrupt_reason);
65
66 std::unique_ptr<download::MockDownloadItem> CreateDownloadItem(
67 DownloadItem::DownloadState state,
68 bool is_paused,
69 download::DownloadInterruptReason interrupt_reason);
70
IsDownloadDone(DownloadItem * item)71 bool IsDownloadDone(DownloadItem* item) {
72 return download::IsDownloadDone(item->GetURL(), item->GetState(),
73 item->GetLastReason());
74 }
75 };
76
77 std::unique_ptr<download::MockDownloadItem>
CreateDownloadItem(const std::string & guid,const base::FilePath & file_path,const base::FilePath & file_name,const std::string & mime_type,DownloadItem::DownloadState state,bool is_paused,bool is_dangerous,const base::Time & creation_time,const base::Time & last_access_time,int64_t received_bytes,int64_t total_bytes,download::DownloadInterruptReason interrupt_reason)78 OfflineItemUtilsTest::CreateDownloadItem(
79 const std::string& guid,
80 const base::FilePath& file_path,
81 const base::FilePath& file_name,
82 const std::string& mime_type,
83 DownloadItem::DownloadState state,
84 bool is_paused,
85 bool is_dangerous,
86 const base::Time& creation_time,
87 const base::Time& last_access_time,
88 int64_t received_bytes,
89 int64_t total_bytes,
90 download::DownloadInterruptReason interrupt_reason) {
91 std::unique_ptr<download::MockDownloadItem> item(
92 new ::testing::NiceMock<download::MockDownloadItem>());
93 ON_CALL(*item, GetURL()).WillByDefault(ReturnRefOfCopy(TestUrl()));
94 ON_CALL(*item, GetTabUrl()).WillByDefault(ReturnRefOfCopy(TestUrl()));
95 ON_CALL(*item, GetOriginalUrl())
96 .WillByDefault(ReturnRefOfCopy(TestOriginalUrl()));
97 ON_CALL(*item, GetDangerType())
98 .WillByDefault(Return(download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS));
99 ON_CALL(*item, GetId()).WillByDefault(Return(0));
100 ON_CALL(*item, GetLastReason()).WillByDefault(Return(interrupt_reason));
101 ON_CALL(*item, GetState()).WillByDefault(Return(state));
102 ON_CALL(*item, GetTargetFilePath()).WillByDefault(ReturnRefOfCopy(file_path));
103 ON_CALL(*item, GetFileNameToReportUser()).WillByDefault(Return(file_name));
104 ON_CALL(*item, GetTransitionType())
105 .WillByDefault(Return(ui::PAGE_TRANSITION_LINK));
106 ON_CALL(*item, IsDangerous()).WillByDefault(Return(is_dangerous));
107 ON_CALL(*item, IsPaused()).WillByDefault(Return(is_paused));
108 ON_CALL(*item, GetGuid()).WillByDefault(ReturnRefOfCopy(guid));
109 ON_CALL(*item, GetMimeType()).WillByDefault(Return(mime_type));
110 ON_CALL(*item, GetStartTime()).WillByDefault(Return(creation_time));
111 ON_CALL(*item, GetLastAccessTime()).WillByDefault(Return(last_access_time));
112 ON_CALL(*item, GetReceivedBytes()).WillByDefault(Return(received_bytes));
113 ON_CALL(*item, GetTotalBytes()).WillByDefault(Return(total_bytes));
114 ON_CALL(*item, IsDone()).WillByDefault(Return(IsDownloadDone(item.get())));
115 ON_CALL(*item, GetDownloadSchedule())
116 .WillByDefault(ReturnRefOfCopy(base::Optional<DownloadSchedule>()));
117 return item;
118 }
119
120 std::unique_ptr<download::MockDownloadItem>
CreateDownloadItem(DownloadItem::DownloadState state,bool is_paused,download::DownloadInterruptReason interrupt_reason)121 OfflineItemUtilsTest::CreateDownloadItem(
122 DownloadItem::DownloadState state,
123 bool is_paused,
124 download::DownloadInterruptReason interrupt_reason) {
125 std::string guid = "test_guid";
126 base::FilePath file_path(FILE_PATH_LITERAL("/tmp/example_file_path"));
127 base::FilePath file_name(FILE_PATH_LITERAL("example_file_path"));
128 std::string mime_type = "text/html";
129 return CreateDownloadItem(guid, file_path, file_name, mime_type, state,
130 is_paused, false, base::Time(), base::Time(), 10,
131 100, interrupt_reason);
132 }
133
TEST_F(OfflineItemUtilsTest,BasicConversions)134 TEST_F(OfflineItemUtilsTest, BasicConversions) {
135 std::string guid = "test_guid";
136 base::FilePath file_path(FILE_PATH_LITERAL("/tmp/example_file_path"));
137 base::FilePath file_name(FILE_PATH_LITERAL("image.png"));
138 std::string mime_type = "image/png";
139 base::Time creation_time = base::Time::Now();
140 base::Time completion_time = base::Time::Now();
141 base::Time last_access_time = base::Time::Now();
142 download::DownloadInterruptReason interrupt_reason =
143 download::DOWNLOAD_INTERRUPT_REASON_NONE;
144 bool is_transient = true;
145 bool is_accelerated = true;
146 bool externally_removed = true;
147 bool is_openable = true;
148 bool is_resumable = true;
149 bool allow_metered = true;
150 int64_t time_remaining_ms = 10000;
151 bool is_dangerous = true;
152 int64_t total_bytes = 1000;
153 int64_t received_bytes = 10;
154 std::unique_ptr<download::MockDownloadItem> download = CreateDownloadItem(
155 guid, file_path, file_name, mime_type, DownloadItem::COMPLETE, false,
156 is_dangerous, creation_time, last_access_time, 0, 0, interrupt_reason);
157
158 ON_CALL(*download, IsTransient()).WillByDefault(Return(is_transient));
159 ON_CALL(*download, IsParallelDownload())
160 .WillByDefault(Return(is_accelerated));
161 ON_CALL(*download, GetFileExternallyRemoved())
162 .WillByDefault(Return(externally_removed));
163 ON_CALL(*download, CanOpenDownload()).WillByDefault(Return(is_openable));
164 ON_CALL(*download, CanResume()).WillByDefault(Return(is_resumable));
165 ON_CALL(*download, AllowMetered()).WillByDefault(Return(allow_metered));
166 ON_CALL(*download, GetReceivedBytes()).WillByDefault(Return(received_bytes));
167 ON_CALL(*download, GetTotalBytes()).WillByDefault(Return(total_bytes));
168 ON_CALL(*download, GetEndTime()).WillByDefault(Return(completion_time));
169
170 ON_CALL(*download, TimeRemaining(_))
171 .WillByDefault(testing::DoAll(
172 testing::SetArgPointee<0>(
173 base::TimeDelta::FromMilliseconds(time_remaining_ms)),
174 Return(true)));
175 ON_CALL(*download, IsDangerous()).WillByDefault(Return(is_dangerous));
176
177 OfflineItem offline_item =
178 OfflineItemUtils::CreateOfflineItem(kNameSpace, download.get());
179
180 EXPECT_EQ(ContentId(kNameSpace, guid), offline_item.id);
181 EXPECT_EQ(file_name.AsUTF8Unsafe(), offline_item.title);
182 EXPECT_EQ(file_name.AsUTF8Unsafe(), offline_item.description);
183 EXPECT_EQ(OfflineItemFilter::FILTER_IMAGE, offline_item.filter);
184 EXPECT_EQ(is_transient, offline_item.is_transient);
185 EXPECT_FALSE(offline_item.is_suggested);
186 EXPECT_EQ(is_accelerated, offline_item.is_accelerated);
187 EXPECT_FALSE(offline_item.promote_origin);
188 EXPECT_TRUE(offline_item.can_rename);
189
190 EXPECT_EQ(total_bytes, offline_item.total_size_bytes);
191 EXPECT_EQ(externally_removed, offline_item.externally_removed);
192 EXPECT_EQ(creation_time, offline_item.creation_time);
193 EXPECT_EQ(completion_time, offline_item.completion_time);
194 EXPECT_EQ(last_access_time, offline_item.last_accessed_time);
195 EXPECT_EQ(is_openable, offline_item.is_openable);
196 EXPECT_EQ(file_path, offline_item.file_path);
197 EXPECT_EQ(mime_type, offline_item.mime_type);
198
199 EXPECT_EQ(TestUrl(), offline_item.page_url);
200 EXPECT_EQ(TestOriginalUrl(), offline_item.original_url);
201 EXPECT_FALSE(offline_item.is_off_the_record);
202 EXPECT_EQ("", offline_item.attribution);
203
204 EXPECT_EQ(OfflineItemState::COMPLETE, offline_item.state);
205 EXPECT_EQ(FailState::NO_FAILURE, offline_item.fail_state);
206 EXPECT_EQ(PendingState::NOT_PENDING, offline_item.pending_state);
207 EXPECT_EQ(is_resumable, offline_item.is_resumable);
208 EXPECT_EQ(allow_metered, offline_item.allow_metered);
209 EXPECT_EQ(received_bytes, offline_item.received_bytes);
210 EXPECT_EQ(received_bytes, offline_item.progress.value);
211 ASSERT_TRUE(offline_item.progress.max.has_value());
212 EXPECT_EQ(total_bytes, offline_item.progress.max.value());
213 EXPECT_EQ(OfflineItemProgressUnit::BYTES, offline_item.progress.unit);
214 EXPECT_EQ(time_remaining_ms, offline_item.time_remaining_ms);
215 EXPECT_EQ(is_dangerous, offline_item.is_dangerous);
216 }
217
TEST_F(OfflineItemUtilsTest,StateConversions)218 TEST_F(OfflineItemUtilsTest, StateConversions) {
219 // in-progress
220 std::unique_ptr<download::MockDownloadItem> download1 =
221 CreateDownloadItem(DownloadItem::IN_PROGRESS, false,
222 download::DOWNLOAD_INTERRUPT_REASON_NONE);
223
224 // cancelled
225 std::unique_ptr<download::MockDownloadItem> download2 = CreateDownloadItem(
226 DownloadItem::CANCELLED, false, download::DOWNLOAD_INTERRUPT_REASON_NONE);
227
228 // complete
229 std::unique_ptr<download::MockDownloadItem> download3 = CreateDownloadItem(
230 DownloadItem::COMPLETE, false, download::DOWNLOAD_INTERRUPT_REASON_NONE);
231
232 // interrupted, but auto-resumable
233 std::unique_ptr<download::MockDownloadItem> download4 =
234 CreateDownloadItem(DownloadItem::INTERRUPTED, false,
235 download::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT);
236
237 // paused
238 std::unique_ptr<download::MockDownloadItem> download5 =
239 CreateDownloadItem(DownloadItem::IN_PROGRESS, true,
240 download::DOWNLOAD_INTERRUPT_REASON_NONE);
241
242 // paused, but interrupted
243 std::unique_ptr<download::MockDownloadItem> download6 =
244 CreateDownloadItem(DownloadItem::INTERRUPTED, true,
245 download::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT);
246
247 // interrupted, but invalid resumption mode
248 std::unique_ptr<download::MockDownloadItem> download7 = CreateDownloadItem(
249 DownloadItem::INTERRUPTED, false,
250 download::DOWNLOAD_INTERRUPT_REASON_FILE_SAME_AS_SOURCE);
251
252 // interrupted, not auto-resumable
253 std::unique_ptr<download::MockDownloadItem> download8 =
254 CreateDownloadItem(DownloadItem::INTERRUPTED, false,
255 download::DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE);
256
257 // interrupted, should be auto-resumable, but max retry count reached
258 std::unique_ptr<download::MockDownloadItem> download9 =
259 CreateDownloadItem(DownloadItem::INTERRUPTED, false,
260 download::DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE);
261 ON_CALL(*download9, GetAutoResumeCount()).WillByDefault(Return(10));
262
263 // interrupted, should be auto-resumable, but dangerous
264 std::unique_ptr<download::MockDownloadItem> download10 =
265 CreateDownloadItem(DownloadItem::INTERRUPTED, false,
266 download::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT);
267 ON_CALL(*download10, IsDangerous()).WillByDefault(Return(true));
268
269 OfflineItem offline_item1 =
270 OfflineItemUtils::CreateOfflineItem(kNameSpace, download1.get());
271 EXPECT_EQ(OfflineItemState::IN_PROGRESS, offline_item1.state);
272
273 OfflineItem offline_item2 =
274 OfflineItemUtils::CreateOfflineItem(kNameSpace, download2.get());
275 EXPECT_EQ(OfflineItemState::CANCELLED, offline_item2.state);
276
277 OfflineItem offline_item3 =
278 OfflineItemUtils::CreateOfflineItem(kNameSpace, download3.get());
279 EXPECT_EQ(OfflineItemState::COMPLETE, offline_item3.state);
280
281 OfflineItem offline_item4 =
282 OfflineItemUtils::CreateOfflineItem(kNameSpace, download4.get());
283 EXPECT_EQ(OfflineItemState::PENDING, offline_item4.state);
284
285 OfflineItem offline_item5 =
286 OfflineItemUtils::CreateOfflineItem(kNameSpace, download5.get());
287 EXPECT_EQ(OfflineItemState::PAUSED, offline_item5.state);
288
289 OfflineItem offline_item6 =
290 OfflineItemUtils::CreateOfflineItem(kNameSpace, download6.get());
291 EXPECT_EQ(OfflineItemState::PAUSED, offline_item6.state);
292
293 OfflineItem offline_item7 =
294 OfflineItemUtils::CreateOfflineItem(kNameSpace, download7.get());
295 EXPECT_EQ(OfflineItemState::FAILED, offline_item7.state);
296
297 OfflineItem offline_item8 =
298 OfflineItemUtils::CreateOfflineItem(kNameSpace, download8.get());
299 EXPECT_EQ(OfflineItemState::INTERRUPTED, offline_item8.state);
300
301 OfflineItem offline_item9 =
302 OfflineItemUtils::CreateOfflineItem(kNameSpace, download9.get());
303 EXPECT_EQ(OfflineItemState::PAUSED, offline_item9.state);
304
305 OfflineItem offline_item10 =
306 OfflineItemUtils::CreateOfflineItem(kNameSpace, download10.get());
307 EXPECT_EQ(OfflineItemState::INTERRUPTED, offline_item10.state);
308 }
309
TEST_F(OfflineItemUtilsTest,MimeTypeToFilterConversion)310 TEST_F(OfflineItemUtilsTest, MimeTypeToFilterConversion) {
311 std::string mime_type[5] = {"text/html", "image/png", "video/webm",
312 "audio/aac", "application/octet-stream"};
313 OfflineItemFilter filter[5] = {
314 OfflineItemFilter::FILTER_DOCUMENT, OfflineItemFilter::FILTER_IMAGE,
315 OfflineItemFilter::FILTER_VIDEO, OfflineItemFilter::FILTER_AUDIO,
316 OfflineItemFilter::FILTER_OTHER};
317
318 for (int i = 0; i < 5; i++) {
319 std::unique_ptr<download::MockDownloadItem> download =
320 CreateDownloadItem(DownloadItem::COMPLETE, false,
321 download::DOWNLOAD_INTERRUPT_REASON_NONE);
322 ON_CALL(*download, GetMimeType()).WillByDefault(Return(mime_type[i]));
323
324 OfflineItem offline_item =
325 OfflineItemUtils::CreateOfflineItem(kNameSpace, download.get());
326
327 EXPECT_EQ(mime_type[i], offline_item.mime_type);
328 EXPECT_EQ(filter[i], offline_item.filter);
329 }
330 }
331
TEST_F(OfflineItemUtilsTest,PendingAndFailedStates)332 TEST_F(OfflineItemUtilsTest, PendingAndFailedStates) {
333 // interrupted, but auto-resumable
334 std::unique_ptr<download::MockDownloadItem> download1 =
335 CreateDownloadItem(DownloadItem::INTERRUPTED, false,
336 download::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT);
337 OfflineItem offline_item1 =
338 OfflineItemUtils::CreateOfflineItem(kNameSpace, download1.get());
339 EXPECT_EQ(OfflineItemState::PENDING, offline_item1.state);
340 EXPECT_EQ(FailState::NETWORK_TIMEOUT, offline_item1.fail_state);
341 EXPECT_EQ(PendingState::PENDING_NETWORK, offline_item1.pending_state);
342
343 // failed download: interrupted, but invalid resumption mode
344 std::unique_ptr<download::MockDownloadItem> download2 = CreateDownloadItem(
345 DownloadItem::INTERRUPTED, false,
346 download::DOWNLOAD_INTERRUPT_REASON_FILE_SAME_AS_SOURCE);
347 OfflineItem offline_item2 =
348 OfflineItemUtils::CreateOfflineItem(kNameSpace, download2.get());
349 EXPECT_EQ(OfflineItemState::FAILED, offline_item2.state);
350 EXPECT_EQ(FailState::FILE_SAME_AS_SOURCE, offline_item2.fail_state);
351 EXPECT_EQ(PendingState::NOT_PENDING, offline_item2.pending_state);
352
353 // interrupted, not auto-resumable
354 std::unique_ptr<download::MockDownloadItem> download3 =
355 CreateDownloadItem(DownloadItem::INTERRUPTED, false,
356 download::DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE);
357 OfflineItem offline_item3 =
358 OfflineItemUtils::CreateOfflineItem(kNameSpace, download3.get());
359 EXPECT_EQ(OfflineItemState::INTERRUPTED, offline_item3.state);
360 EXPECT_EQ(FailState::SERVER_NO_RANGE, offline_item3.fail_state);
361 EXPECT_EQ(PendingState::NOT_PENDING, offline_item3.pending_state);
362 }
363
TEST_F(OfflineItemUtilsTest,OfflineItemSchedule)364 TEST_F(OfflineItemUtilsTest, OfflineItemSchedule) {
365 auto time = base::Time::Now();
366 std::vector<DownloadSchedule> download_schedules = {{false, time},
367 {true, base::nullopt}};
368
369 for (const auto& download_schedule : download_schedules) {
370 auto download =
371 CreateDownloadItem(DownloadItem::IN_PROGRESS, false,
372 download::DOWNLOAD_INTERRUPT_REASON_NONE);
373 base::Optional<DownloadSchedule> copy = download_schedule;
374 ON_CALL(*download, GetDownloadSchedule())
375 .WillByDefault(ReturnRefOfCopy(copy));
376 OfflineItem offline_item =
377 OfflineItemUtils::CreateOfflineItem(kNameSpace, download.get());
378 auto offline_item_schedule = base::make_optional<OfflineItemSchedule>(
379 download_schedule.only_on_wifi(), download_schedule.start_time());
380 EXPECT_EQ(offline_item.schedule, offline_item.schedule);
381 }
382 }
383