1 // Copyright (c) 2012 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/download_item_model.h"
6 
7 #include <stddef.h>
8 #include <stdint.h>
9 
10 #include <vector>
11 
12 #include "base/check_op.h"
13 #include "base/i18n/rtl.h"
14 #include "base/stl_util.h"
15 #include "base/strings/string16.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "build/build_config.h"
19 #include "components/download/public/common/mock_download_item.h"
20 #include "testing/gmock/include/gmock/gmock.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22 #include "ui/base/text/bytes_formatting.h"
23 
24 using download::DownloadItem;
25 using safe_browsing::DownloadFileType;
26 using ::testing::Mock;
27 using ::testing::NiceMock;
28 using ::testing::Return;
29 using ::testing::ReturnRef;
30 using ::testing::ReturnRefOfCopy;
31 using ::testing::SetArgPointee;
32 using ::testing::_;
33 
34 namespace {
35 
36 // Create a char array that has as many elements as there are download
37 // interrupt reasons. We can then use that in a static_assert to make sure
38 // that all the interrupt reason codes are accounted for. The reason codes are
39 // unfortunately sparse, making this necessary.
40 char kInterruptReasonCounter[] = {
41     0,  // download::DOWNLOAD_INTERRUPT_REASON_NONE
42 #define INTERRUPT_REASON(name,value) 0,
43 #include "components/download/public/common/download_interrupt_reason_values.h"
44 #undef INTERRUPT_REASON
45 };
46 const size_t kInterruptReasonCount = base::size(kInterruptReasonCounter);
47 
48 // Default target path for a mock download item in DownloadItemModelTest.
49 const base::FilePath::CharType kDefaultTargetFilePath[] =
50     FILE_PATH_LITERAL("/foo/bar/foo.bar");
51 
52 const base::FilePath::CharType kDefaultDisplayFileName[] =
53     FILE_PATH_LITERAL("foo.bar");
54 
55 // Default URL for a mock download item in DownloadItemModelTest.
56 const char kDefaultURL[] = "http://example.com/foo.bar";
57 
58 class DownloadItemModelTest : public testing::Test {
59  public:
DownloadItemModelTest()60   DownloadItemModelTest()
61       : model_(&item_) {}
62 
~DownloadItemModelTest()63   ~DownloadItemModelTest() override {}
64 
65  protected:
66   // Sets up defaults for the download item and sets |model_| to a new
67   // DownloadItemModel that uses the mock download item.
SetupDownloadItemDefaults()68   void SetupDownloadItemDefaults() {
69     ON_CALL(item_, GetReceivedBytes()).WillByDefault(Return(1));
70     ON_CALL(item_, GetTotalBytes()).WillByDefault(Return(2));
71     ON_CALL(item_, TimeRemaining(_)).WillByDefault(Return(false));
72     ON_CALL(item_, GetMimeType()).WillByDefault(Return("text/html"));
73     ON_CALL(item_, AllDataSaved()).WillByDefault(Return(false));
74     ON_CALL(item_, GetOpenWhenComplete()).WillByDefault(Return(false));
75     ON_CALL(item_, GetFileExternallyRemoved()).WillByDefault(Return(false));
76     ON_CALL(item_, GetState())
77         .WillByDefault(Return(DownloadItem::IN_PROGRESS));
78     ON_CALL(item_, GetURL())
79         .WillByDefault(ReturnRefOfCopy(GURL(kDefaultURL)));
80     ON_CALL(item_, GetFileNameToReportUser())
81         .WillByDefault(Return(base::FilePath(kDefaultDisplayFileName)));
82     ON_CALL(item_, GetTargetFilePath())
83         .WillByDefault(ReturnRefOfCopy(base::FilePath(kDefaultTargetFilePath)));
84     ON_CALL(item_, GetTargetDisposition())
85         .WillByDefault(
86             Return(DownloadItem::TARGET_DISPOSITION_OVERWRITE));
87     ON_CALL(item_, IsPaused()).WillByDefault(Return(false));
88   }
89 
SetupInterruptedDownloadItem(download::DownloadInterruptReason reason)90   void SetupInterruptedDownloadItem(download::DownloadInterruptReason reason) {
91     EXPECT_CALL(item_, GetLastReason()).WillRepeatedly(Return(reason));
92     EXPECT_CALL(item_, GetState())
93         .WillRepeatedly(
94             Return((reason == download::DOWNLOAD_INTERRUPT_REASON_NONE)
95                        ? DownloadItem::IN_PROGRESS
96                        : DownloadItem::INTERRUPTED));
97   }
98 
item()99   download::MockDownloadItem& item() { return item_; }
100 
model()101   DownloadItemModel& model() {
102     return model_;
103   }
104 
105  private:
106   NiceMock<download::MockDownloadItem> item_;
107   DownloadItemModel model_;
108 };
109 
110 }  // namespace
111 
TEST_F(DownloadItemModelTest,InterruptedStatus)112 TEST_F(DownloadItemModelTest, InterruptedStatus) {
113   // Test that we have the correct interrupt status message for downloads that
114   // are in the INTERRUPTED state.
115   const struct TestCase {
116     // The reason.
117     download::DownloadInterruptReason reason;
118 
119     // Expected status string. This will include the progress as well.
120     const char* expected_status;
121   } kTestCases[] = {
122       {download::DOWNLOAD_INTERRUPT_REASON_NONE, "1/2 B"},
123       {download::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED,
124        "Failed - Download error"},
125       {download::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED,
126        "Failed - Insufficient permissions"},
127       {download::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, "Failed - Disk full"},
128       {download::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG,
129        "Failed - Path too long"},
130       {download::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE,
131        "Failed - File too large"},
132       {download::DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED,
133        "Failed - Virus detected"},
134       {download::DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED, "Failed - Blocked"},
135       {download::DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED,
136        "Failed - Virus scan failed"},
137       {download::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT,
138        "Failed - File truncated"},
139       {download::DOWNLOAD_INTERRUPT_REASON_FILE_SAME_AS_SOURCE,
140        "Failed - Already downloaded"},
141       {download::DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR,
142        "Failed - System busy"},
143       {download::DOWNLOAD_INTERRUPT_REASON_FILE_HASH_MISMATCH,
144        "Failed - Download error"},
145       {download::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED,
146        "Failed - Network error"},
147       {download::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT,
148        "Failed - Network timeout"},
149       {download::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED,
150        "Failed - Network disconnected"},
151       {download::DOWNLOAD_INTERRUPT_REASON_NETWORK_SERVER_DOWN,
152        "Failed - Server unavailable"},
153       {download::DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST,
154        "Failed - Network error"},
155       {download::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED,
156        "Failed - Server problem"},
157       {download::DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE,
158        "Failed - Download error"},
159       {download::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT,
160        "Failed - No file"},
161       {download::DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED,
162        "Failed - Needs authorization"},
163       {download::DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM,
164        "Failed - Bad certificate"},
165       {download::DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN,
166        "Failed - Forbidden"},
167       {download::DOWNLOAD_INTERRUPT_REASON_SERVER_UNREACHABLE,
168        "Failed - Server unreachable"},
169       {download::DOWNLOAD_INTERRUPT_REASON_SERVER_CONTENT_LENGTH_MISMATCH,
170        "Failed - File incomplete"},
171       {download::DOWNLOAD_INTERRUPT_REASON_SERVER_CROSS_ORIGIN_REDIRECT,
172        "Failed - Download error"},
173       {download::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED, "Canceled"},
174       {download::DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN, "Failed - Shutdown"},
175       {download::DOWNLOAD_INTERRUPT_REASON_CRASH, "Failed - Crash"},
176   };
177   static_assert(kInterruptReasonCount == base::size(kTestCases),
178                 "interrupt reason mismatch");
179 
180   SetupDownloadItemDefaults();
181   for (const auto& test_case : kTestCases) {
182     SetupInterruptedDownloadItem(test_case.reason);
183     EXPECT_EQ(test_case.expected_status,
184               base::UTF16ToUTF8(model().GetStatusText()));
185   }
186 }
187 
TEST_F(DownloadItemModelTest,InterruptTooltip)188 TEST_F(DownloadItemModelTest, InterruptTooltip) {
189   // Test that we have the correct interrupt tooltip for downloads that are in
190   // the INTERRUPTED state.
191   const struct TestCase {
192     // The reason.
193     download::DownloadInterruptReason reason;
194 
195     // Expected tooltip text. The tooltip text for interrupted downloads
196     // typically consist of two lines. One for the filename and one for the
197     // interrupt reason. The returned string contains a newline.
198     const char* expected_tooltip;
199   } kTestCases[] = {
200       {download::DOWNLOAD_INTERRUPT_REASON_NONE, "foo.bar"},
201       {download::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED,
202        "foo.bar\nDownload error"},
203       {download::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED,
204        "foo.bar\nInsufficient permissions"},
205       {download::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, "foo.bar\nDisk full"},
206       {download::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG,
207        "foo.bar\nPath too long"},
208       {download::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE,
209        "foo.bar\nFile too large"},
210       {download::DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED,
211        "foo.bar\nVirus detected"},
212       {download::DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED, "foo.bar\nBlocked"},
213       {download::DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED,
214        "foo.bar\nVirus scan failed"},
215       {download::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT,
216        "foo.bar\nFile truncated"},
217       {download::DOWNLOAD_INTERRUPT_REASON_FILE_SAME_AS_SOURCE,
218        "foo.bar\nAlready downloaded"},
219       {download::DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR,
220        "foo.bar\nSystem busy"},
221       {download::DOWNLOAD_INTERRUPT_REASON_FILE_HASH_MISMATCH,
222        "foo.bar\nDownload error"},
223       {download::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED,
224        "foo.bar\nNetwork error"},
225       {download::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT,
226        "foo.bar\nNetwork timeout"},
227       {download::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED,
228        "foo.bar\nNetwork disconnected"},
229       {download::DOWNLOAD_INTERRUPT_REASON_NETWORK_SERVER_DOWN,
230        "foo.bar\nServer unavailable"},
231       {download::DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST,
232        "foo.bar\nNetwork error"},
233       {download::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED,
234        "foo.bar\nServer problem"},
235       {download::DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE,
236        "foo.bar\nDownload error"},
237       {download::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT,
238        "foo.bar\nNo file"},
239       {download::DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED,
240        "foo.bar\nNeeds authorization"},
241       {download::DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM,
242        "foo.bar\nBad certificate"},
243       {download::DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN,
244        "foo.bar\nForbidden"},
245       {download::DOWNLOAD_INTERRUPT_REASON_SERVER_UNREACHABLE,
246        "foo.bar\nServer unreachable"},
247       {download::DOWNLOAD_INTERRUPT_REASON_SERVER_CONTENT_LENGTH_MISMATCH,
248        "foo.bar\nFile incomplete"},
249       {download::DOWNLOAD_INTERRUPT_REASON_SERVER_CROSS_ORIGIN_REDIRECT,
250        "foo.bar\nDownload error"},
251       {download::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED, "foo.bar"},
252       {download::DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN, "foo.bar\nShutdown"},
253       {download::DOWNLOAD_INTERRUPT_REASON_CRASH, "foo.bar\nCrash"},
254   };
255   static_assert(kInterruptReasonCount == base::size(kTestCases),
256                 "interrupt reason mismatch");
257 
258   SetupDownloadItemDefaults();
259   for (const auto& test_case : kTestCases) {
260     SetupInterruptedDownloadItem(test_case.reason);
261     EXPECT_EQ(test_case.expected_tooltip,
262               base::UTF16ToUTF8(model().GetTooltipText()));
263   }
264 }
265 
TEST_F(DownloadItemModelTest,InProgressStatus)266 TEST_F(DownloadItemModelTest, InProgressStatus) {
267   const struct TestCase {
268     int64_t received_bytes;             // Return value of GetReceivedBytes().
269     int64_t total_bytes;                // Return value of GetTotalBytes().
270     bool  time_remaining_known;         // If TimeRemaining() is known.
271     bool  open_when_complete;           // GetOpenWhenComplete().
272     bool  is_paused;                    // IsPaused().
273     const char* expected_status;        // Expected status text.
274   } kTestCases[] = {
275     // These are all the valid combinations of the above fields for a download
276     // that is in IN_PROGRESS state. Go through all of them and check the return
277     // value of DownloadItemModel::GetStatusText(). The point isn't to lock down
278     // the status strings, but to make sure we end up with something sane for
279     // all the circumstances we care about.
280     //
281     // For GetReceivedBytes()/GetTotalBytes(), we only check whether each is
282     // non-zero. In addition, if |total_bytes| is zero, then
283     // |time_remaining_known| is also false.
284     //
285     //         .-- .TimeRemaining() is known.
286     //        |       .-- .GetOpenWhenComplete()
287     //        |      |      .---- .IsPaused()
288     { 0, 0, false, false, false, "Starting\xE2\x80\xA6" },
289     { 1, 0, false, false, false, "1 B" },
290     { 0, 2, false, false, false, "Starting\xE2\x80\xA6"},
291     { 1, 2, false, false, false, "1/2 B" },
292     { 0, 2, true,  false, false, "0/2 B, 10 secs left" },
293     { 1, 2, true,  false, false, "1/2 B, 10 secs left" },
294     { 0, 0, false, true,  false, "Opening when complete" },
295     { 1, 0, false, true,  false, "Opening when complete" },
296     { 0, 2, false, true,  false, "Opening when complete" },
297     { 1, 2, false, true,  false, "Opening when complete" },
298     { 0, 2, true,  true,  false, "Opening in 10 secs\xE2\x80\xA6"},
299     { 1, 2, true,  true,  false, "Opening in 10 secs\xE2\x80\xA6"},
300     { 0, 0, false, false, true,  "0 B, Paused" },
301     { 1, 0, false, false, true,  "1 B, Paused" },
302     { 0, 2, false, false, true,  "0/2 B, Paused" },
303     { 1, 2, false, false, true,  "1/2 B, Paused" },
304     { 0, 2, true,  false, true,  "0/2 B, Paused" },
305     { 1, 2, true,  false, true,  "1/2 B, Paused" },
306     { 0, 0, false, true,  true,  "0 B, Paused" },
307     { 1, 0, false, true,  true,  "1 B, Paused" },
308     { 0, 2, false, true,  true,  "0/2 B, Paused" },
309     { 1, 2, false, true,  true,  "1/2 B, Paused" },
310     { 0, 2, true,  true,  true,  "0/2 B, Paused" },
311     { 1, 2, true,  true,  true,  "1/2 B, Paused" },
312   };
313 
314   SetupDownloadItemDefaults();
315 
316   for (const auto& test_case : kTestCases) {
317     Mock::VerifyAndClearExpectations(&item());
318     Mock::VerifyAndClearExpectations(&model());
319     EXPECT_CALL(item(), GetReceivedBytes())
320         .WillRepeatedly(Return(test_case.received_bytes));
321     EXPECT_CALL(item(), GetTotalBytes())
322         .WillRepeatedly(Return(test_case.total_bytes));
323     EXPECT_CALL(item(), TimeRemaining(_))
324         .WillRepeatedly(testing::DoAll(
325             testing::SetArgPointee<0>(base::TimeDelta::FromSeconds(10)),
326             Return(test_case.time_remaining_known)));
327     EXPECT_CALL(item(), GetOpenWhenComplete())
328         .WillRepeatedly(Return(test_case.open_when_complete));
329     EXPECT_CALL(item(), IsPaused())
330         .WillRepeatedly(Return(test_case.is_paused));
331 
332     EXPECT_EQ(test_case.expected_status,
333               base::UTF16ToUTF8(model().GetStatusText()));
334   }
335 }
336 
TEST_F(DownloadItemModelTest,ShouldShowInShelf)337 TEST_F(DownloadItemModelTest, ShouldShowInShelf) {
338   SetupDownloadItemDefaults();
339 
340   // By default the download item should be displayable on the shelf when it is
341   // not a transient download.
342   EXPECT_CALL(item(), IsTransient()).WillOnce(Return(false));
343   EXPECT_TRUE(model().ShouldShowInShelf());
344 
345   EXPECT_CALL(item(), IsTransient()).WillOnce(Return(true));
346   EXPECT_FALSE(model().ShouldShowInShelf());
347 
348   // Once explicitly set, ShouldShowInShelf() should return the explicit value
349   // regardless of whether it's a transient download, which should no longer
350   // be considered by the model after initializing it.
351   EXPECT_CALL(item(), IsTransient()).Times(1);
352 
353   model().SetShouldShowInShelf(true);
354   EXPECT_TRUE(model().ShouldShowInShelf());
355 
356   model().SetShouldShowInShelf(false);
357   EXPECT_FALSE(model().ShouldShowInShelf());
358 }
359 
TEST_F(DownloadItemModelTest,DangerLevel)360 TEST_F(DownloadItemModelTest, DangerLevel) {
361   SetupDownloadItemDefaults();
362 
363   // Default danger level is NOT_DANGEROUS.
364   EXPECT_EQ(DownloadFileType::NOT_DANGEROUS, model().GetDangerLevel());
365 
366   model().SetDangerLevel(DownloadFileType::ALLOW_ON_USER_GESTURE);
367   EXPECT_EQ(DownloadFileType::ALLOW_ON_USER_GESTURE, model().GetDangerLevel());
368 }
369 
TEST_F(DownloadItemModelTest,HasSupportedImageMimeType)370 TEST_F(DownloadItemModelTest, HasSupportedImageMimeType) {
371   SetupDownloadItemDefaults();
372 
373   // When the item has a supported image MIME type, true should be returned.
374   ON_CALL(item(), GetMimeType()).WillByDefault(Return("image/png"));
375   EXPECT_TRUE(model().HasSupportedImageMimeType());
376 
377   // An unsupported MIME type should result in false being returned...
378   ON_CALL(item(), GetMimeType()).WillByDefault(Return("image/unsupported"));
379   EXPECT_FALSE(model().HasSupportedImageMimeType());
380 
381   // ... unless the target path has a well-known image extension.
382   const base::FilePath kImagePath(FILE_PATH_LITERAL("/foo/image.png"));
383   ON_CALL(item(), GetTargetFilePath()).WillByDefault(ReturnRef(kImagePath));
384   EXPECT_TRUE(model().HasSupportedImageMimeType());
385 
386   // .txt and missing extensions should also result in false being returned.
387   const base::FilePath kTextPath(FILE_PATH_LITERAL("/foo/image.txt"));
388   ON_CALL(item(), GetTargetFilePath()).WillByDefault(ReturnRef(kTextPath));
389   EXPECT_FALSE(model().HasSupportedImageMimeType());
390 
391   const base::FilePath kNoExtensionPath(FILE_PATH_LITERAL("/foo/image."));
392   ON_CALL(item(), GetTargetFilePath())
393       .WillByDefault(ReturnRef(kNoExtensionPath));
394   EXPECT_FALSE(model().HasSupportedImageMimeType());
395 }
396 
TEST_F(DownloadItemModelTest,ShouldRemoveFromShelfWhenComplete)397 TEST_F(DownloadItemModelTest, ShouldRemoveFromShelfWhenComplete) {
398   const struct TestCase {
399     DownloadItem::DownloadState state;
400     bool is_dangerous;  // Expectation for IsDangerous().
401     bool is_auto_open;  // Expectation for GetOpenWhenComplete().
402     bool auto_opened;   // Whether the download was successfully
403                         // auto-opened. Expecation for GetAutoOpened().
404     bool expected_result;
405   } kTestCases[] = {
406     // All the valid combinations of state, is_dangerous, is_auto_open and
407     // auto_opened.
408     //
409     //                              .--- Is dangerous.
410     //                             |       .--- Auto open or temporary.
411     //                             |      |      .--- Auto opened.
412     //                             |      |      |      .--- Expected result.
413     { DownloadItem::IN_PROGRESS, false, false, false, false},
414     { DownloadItem::IN_PROGRESS, false, true , false, true },
415     { DownloadItem::IN_PROGRESS, true , false, false, false},
416     { DownloadItem::IN_PROGRESS, true , true , false, false},
417     { DownloadItem::COMPLETE,    false, false, false, false},
418     { DownloadItem::COMPLETE,    false, true , false, false},
419     { DownloadItem::COMPLETE,    false, false, true , true },
420     { DownloadItem::COMPLETE,    false, true , true , true },
421     { DownloadItem::CANCELLED,   false, false, false, false},
422     { DownloadItem::CANCELLED,   false, true , false, false},
423     { DownloadItem::CANCELLED,   true , false, false, false},
424     { DownloadItem::CANCELLED,   true , true , false, false},
425     { DownloadItem::INTERRUPTED, false, false, false, false},
426     { DownloadItem::INTERRUPTED, false, true , false, false},
427     { DownloadItem::INTERRUPTED, true , false, false, false},
428     { DownloadItem::INTERRUPTED, true , true , false, false}
429   };
430 
431   SetupDownloadItemDefaults();
432 
433   for (const auto& test_case : kTestCases) {
434     EXPECT_CALL(item(), GetOpenWhenComplete())
435         .WillRepeatedly(Return(test_case.is_auto_open));
436     EXPECT_CALL(item(), GetState())
437         .WillRepeatedly(Return(test_case.state));
438     EXPECT_CALL(item(), IsDangerous())
439         .WillRepeatedly(Return(test_case.is_dangerous));
440     EXPECT_CALL(item(), GetAutoOpened())
441         .WillRepeatedly(Return(test_case.auto_opened));
442 
443     EXPECT_EQ(test_case.expected_result,
444               model().ShouldRemoveFromShelfWhenComplete());
445     Mock::VerifyAndClearExpectations(&item());
446     Mock::VerifyAndClearExpectations(&model());
447   }
448 }
449 
TEST_F(DownloadItemModelTest,ShouldShowDropdown)450 TEST_F(DownloadItemModelTest, ShouldShowDropdown) {
451   // A few aliases for DownloadDangerTypes since the full names are fairly
452   // verbose.
453   download::DownloadDangerType safe =
454       download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS;
455   download::DownloadDangerType dangerous_file =
456       download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE;
457   download::DownloadDangerType dangerous_content =
458       download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT;
459   download::DownloadDangerType blocked_encrypted =
460       download::DOWNLOAD_DANGER_TYPE_BLOCKED_PASSWORD_PROTECTED;
461   download::DownloadDangerType blocked_too_large =
462       download::DOWNLOAD_DANGER_TYPE_BLOCKED_TOO_LARGE;
463   download::DownloadDangerType blocked_sensitive =
464       download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_BLOCK;
465   download::DownloadDangerType blocked_filetype =
466       download::DOWNLOAD_DANGER_TYPE_BLOCKED_UNSUPPORTED_FILETYPE;
467 
468   const struct TestCase {
469     DownloadItem::DownloadState state;        // Expectation for GetState()
470     download::DownloadDangerType danger_type; // Expectation for GetDangerType()
471     bool is_dangerous;        // Expectation for IsDangerous()
472     bool expected_result;
473   } kTestCases[] = {
474     //                                            .--- Is dangerous.
475     // Download state         Danger type         |      .--- Expected result.
476     {DownloadItem::COMPLETE,  safe,              false, true},
477     {DownloadItem::COMPLETE,  dangerous_file,    true,  false},
478     {DownloadItem::CANCELLED, dangerous_file,    true,  true},
479     {DownloadItem::COMPLETE,  dangerous_content, true,  true},
480     {DownloadItem::COMPLETE,  blocked_encrypted, true,  false},
481     {DownloadItem::COMPLETE,  blocked_too_large, true,  false},
482     {DownloadItem::COMPLETE,  blocked_sensitive, true,  false},
483     {DownloadItem::COMPLETE,  blocked_filetype,  true,  false},
484   };
485 
486   SetupDownloadItemDefaults();
487 
488   for (const auto& test_case : kTestCases) {
489     EXPECT_CALL(item(), GetState()).WillRepeatedly(Return(test_case.state));
490     EXPECT_CALL(item(), GetDangerType())
491         .WillRepeatedly(Return(test_case.danger_type));
492     EXPECT_CALL(item(), IsDangerous())
493         .WillRepeatedly(Return(test_case.is_dangerous));
494 
495     EXPECT_EQ(test_case.expected_result, model().ShouldShowDropdown());
496     Mock::VerifyAndClearExpectations(&item());
497     Mock::VerifyAndClearExpectations(&model());
498   }
499 }
500