Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / download / download_item_model_unittest.cc
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 <vector>
8
9 #include "base/i18n/rtl.h"
10 #include "base/logging.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/strings/string16.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "content/public/test/mock_download_item.h"
16 #include "extensions/common/extension.h"
17 #include "testing/gmock/include/gmock/gmock.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "ui/base/resource/resource_bundle.h"
20 #include "ui/base/text/bytes_formatting.h"
21 #include "ui/gfx/font_list.h"
22 #include "ui/gfx/text_utils.h"
23
24 using content::DownloadItem;
25 using ::testing::Mock;
26 using ::testing::NiceMock;
27 using ::testing::Return;
28 using ::testing::ReturnRefOfCopy;
29 using ::testing::SetArgPointee;
30 using ::testing::_;
31
32 namespace {
33
34 // Create a char array that has as many elements as there are download
35 // interrupt reasons. We can then use that in a COMPILE_ASSERT to make sure
36 // that all the interrupt reason codes are accounted for. The reason codes are
37 // unfortunately sparse, making this necessary.
38 char kInterruptReasonCounter[] = {
39   0,                                // content::DOWNLOAD_INTERRUPT_REASON_NONE
40 #define INTERRUPT_REASON(name,value) 0,
41 #include "content/public/browser/download_interrupt_reason_values.h"
42 #undef INTERRUPT_REASON
43 };
44 const size_t kInterruptReasonCount = ARRAYSIZE_UNSAFE(kInterruptReasonCounter);
45
46 // Default target path for a mock download item in DownloadItemModelTest.
47 const base::FilePath::CharType kDefaultTargetFilePath[] =
48     FILE_PATH_LITERAL("/foo/bar/foo.bar");
49
50 const base::FilePath::CharType kDefaultDisplayFileName[] =
51     FILE_PATH_LITERAL("foo.bar");
52
53 // Default URL for a mock download item in DownloadItemModelTest.
54 const char kDefaultURL[] = "http://example.com/foo.bar";
55
56 class DownloadItemModelTest : public testing::Test {
57  public:
58   DownloadItemModelTest()
59       : model_(&item_) {}
60
61   virtual ~DownloadItemModelTest() {
62   }
63
64  protected:
65   // Sets up defaults for the download item and sets |model_| to a new
66   // DownloadItemModel that uses the mock download item.
67   void SetupDownloadItemDefaults() {
68     ON_CALL(item_, GetReceivedBytes()).WillByDefault(Return(1));
69     ON_CALL(item_, GetTotalBytes()).WillByDefault(Return(2));
70     ON_CALL(item_, TimeRemaining(_)).WillByDefault(Return(false));
71     ON_CALL(item_, GetMimeType()).WillByDefault(Return("text/html"));
72     ON_CALL(item_, AllDataSaved()).WillByDefault(Return(false));
73     ON_CALL(item_, GetOpenWhenComplete()).WillByDefault(Return(false));
74     ON_CALL(item_, GetFileExternallyRemoved()).WillByDefault(Return(false));
75     ON_CALL(item_, GetState())
76         .WillByDefault(Return(DownloadItem::IN_PROGRESS));
77     ON_CALL(item_, GetURL())
78         .WillByDefault(ReturnRefOfCopy(GURL(kDefaultURL)));
79     ON_CALL(item_, GetFileNameToReportUser())
80         .WillByDefault(Return(base::FilePath(kDefaultDisplayFileName)));
81     ON_CALL(item_, GetTargetFilePath())
82         .WillByDefault(ReturnRefOfCopy(base::FilePath(kDefaultTargetFilePath)));
83     ON_CALL(item_, GetTargetDisposition())
84         .WillByDefault(
85             Return(DownloadItem::TARGET_DISPOSITION_OVERWRITE));
86     ON_CALL(item_, IsPaused()).WillByDefault(Return(false));
87   }
88
89   void SetupInterruptedDownloadItem(content::DownloadInterruptReason reason) {
90     EXPECT_CALL(item_, GetLastReason()).WillRepeatedly(Return(reason));
91     EXPECT_CALL(item_, GetState())
92         .WillRepeatedly(Return(
93             (reason == content::DOWNLOAD_INTERRUPT_REASON_NONE) ?
94                 DownloadItem::IN_PROGRESS :
95                 DownloadItem::INTERRUPTED));
96   }
97
98   content::MockDownloadItem& item() {
99     return item_;
100   }
101
102   DownloadItemModel& model() {
103     return model_;
104   }
105
106  private:
107   NiceMock<content::MockDownloadItem> item_;
108   DownloadItemModel model_;
109 };
110
111 }  // namespace
112
113 TEST_F(DownloadItemModelTest, InterruptedStatus) {
114   // Test that we have the correct interrupt status message for downloads that
115   // are in the INTERRUPTED state.
116   const struct TestCase {
117     // The reason.
118     content::DownloadInterruptReason reason;
119
120     // Expected status string. This will include the progress as well.
121     const char* expected_status;
122   } kTestCases[] = {
123     { content::DOWNLOAD_INTERRUPT_REASON_NONE,
124       "1/2 B" },
125     { content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED,
126       "Failed - Download error" },
127     { content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED,
128       "Failed - Insufficient permissions" },
129     { content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE,
130       "Failed - Disk full" },
131     { content::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG,
132       "Failed - Path too long" },
133     { content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE,
134       "Failed - File too large" },
135     { content::DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED,
136       "Failed - Virus detected" },
137     { content::DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED,
138       "Failed - Blocked" },
139     { content::DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED,
140       "Failed - Virus scan failed" },
141     { content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT,
142       "Failed - File truncated" },
143     { content::DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR,
144       "Failed - System busy" },
145     { content::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED,
146       "Failed - Network error" },
147     { content::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT,
148       "Failed - Network timeout" },
149     { content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED,
150       "Failed - Network disconnected" },
151     { content::DOWNLOAD_INTERRUPT_REASON_NETWORK_SERVER_DOWN,
152       "Failed - Server unavailable" },
153     { content::DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST,
154       "Failed - Network error" },
155     { content::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED,
156       "Failed - Server problem" },
157     { content::DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE,
158       "Failed - Download error" },
159     { content::DOWNLOAD_INTERRUPT_REASON_SERVER_PRECONDITION,
160       "Failed - Download error" },
161     { content::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT,
162       "Failed - No file" },
163     { content::DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED,
164       "Failed - Needs authorization" },
165     { content::DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM,
166       "Failed - Bad certificate" },
167     { content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED,
168       "Cancelled" },
169     { content::DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN,
170       "Failed - Shutdown" },
171     { content::DOWNLOAD_INTERRUPT_REASON_CRASH,
172       "Failed - Crash" },
173   };
174   COMPILE_ASSERT(kInterruptReasonCount == ARRAYSIZE_UNSAFE(kTestCases),
175                  interrupt_reason_mismatch);
176
177   SetupDownloadItemDefaults();
178   for (unsigned i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) {
179     const TestCase& test_case = kTestCases[i];
180     SetupInterruptedDownloadItem(test_case.reason);
181     EXPECT_STREQ(test_case.expected_status,
182                  base::UTF16ToUTF8(model().GetStatusText()).c_str());
183   }
184 }
185
186 // Note: This test is currently skipped on Android. See http://crbug.com/139398
187 TEST_F(DownloadItemModelTest, InterruptTooltip) {
188   // Test that we have the correct interrupt tooltip for downloads that are in
189   // the INTERRUPTED state.
190   const struct TestCase {
191     // The reason.
192     content::DownloadInterruptReason reason;
193
194     // Expected tooltip text. The tooltip text for interrupted downloads
195     // typically consist of two lines. One for the filename and one for the
196     // interrupt reason. The returned string contains a newline.
197     const char* expected_tooltip;
198   } kTestCases[] = {
199     { content::DOWNLOAD_INTERRUPT_REASON_NONE,
200       "foo.bar" },
201     { content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED,
202       "foo.bar\nDownload error" },
203     { content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED,
204       "foo.bar\nInsufficient permissions" },
205     { content::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE,
206       "foo.bar\nDisk full" },
207     { content::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG,
208       "foo.bar\nPath too long" },
209     { content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE,
210       "foo.bar\nFile too large" },
211     { content::DOWNLOAD_INTERRUPT_REASON_FILE_VIRUS_INFECTED,
212       "foo.bar\nVirus detected" },
213     { content::DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED,
214       "foo.bar\nBlocked" },
215     { content::DOWNLOAD_INTERRUPT_REASON_FILE_SECURITY_CHECK_FAILED,
216       "foo.bar\nVirus scan failed" },
217     { content::DOWNLOAD_INTERRUPT_REASON_FILE_TOO_SHORT,
218       "foo.bar\nFile truncated" },
219     { content::DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR,
220       "foo.bar\nSystem busy" },
221     { content::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED,
222       "foo.bar\nNetwork error" },
223     { content::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT,
224       "foo.bar\nNetwork timeout" },
225     { content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED,
226       "foo.bar\nNetwork disconnected" },
227     { content::DOWNLOAD_INTERRUPT_REASON_NETWORK_SERVER_DOWN,
228       "foo.bar\nServer unavailable" },
229     { content::DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST,
230       "foo.bar\nNetwork error" },
231     { content::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED,
232       "foo.bar\nServer problem" },
233     { content::DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE,
234       "foo.bar\nDownload error" },
235     { content::DOWNLOAD_INTERRUPT_REASON_SERVER_PRECONDITION,
236       "foo.bar\nDownload error" },
237     { content::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT,
238       "foo.bar\nNo file" },
239     { content::DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED,
240       "foo.bar\nNeeds authorization" },
241     { content::DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM,
242       "foo.bar\nBad certificate" },
243     { content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED,
244       "foo.bar" },
245     { content::DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN,
246       "foo.bar\nShutdown" },
247     { content::DOWNLOAD_INTERRUPT_REASON_CRASH,
248       "foo.bar\nCrash" },
249   };
250   COMPILE_ASSERT(kInterruptReasonCount == ARRAYSIZE_UNSAFE(kTestCases),
251                  interrupt_reason_mismatch);
252
253   // Large tooltip width. Should be large enough to accommodate the entire
254   // tooltip without truncation.
255   const int kLargeTooltipWidth = 1000;
256
257   // Small tooltip width. Small enough to require truncation of most
258   // tooltips. Used to test eliding logic.
259   const int kSmallTooltipWidth = 40;
260
261   const gfx::FontList& font_list =
262       ui::ResourceBundle::GetSharedInstance().GetFontList(
263           ui::ResourceBundle::BaseFont);
264   SetupDownloadItemDefaults();
265   for (unsigned i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) {
266     const TestCase& test_case = kTestCases[i];
267     SetupInterruptedDownloadItem(test_case.reason);
268
269     // GetTooltipText() elides the tooltip so that the text would fit within a
270     // given width. The following test would fail if kLargeTooltipWidth isn't
271     // large enough to accomodate all the strings.
272     EXPECT_STREQ(
273         test_case.expected_tooltip,
274         base::UTF16ToUTF8(model().GetTooltipText(font_list,
275                                                  kLargeTooltipWidth)).c_str());
276
277     // Check that if the width is small, the returned tooltip only contains
278     // lines of the given width or smaller.
279     std::vector<base::string16> lines;
280     base::string16 truncated_tooltip =
281         model().GetTooltipText(font_list, kSmallTooltipWidth);
282     Tokenize(truncated_tooltip, base::ASCIIToUTF16("\n"), &lines);
283     for (unsigned i = 0; i < lines.size(); ++i)
284       EXPECT_GE(kSmallTooltipWidth, gfx::GetStringWidth(lines[i], font_list));
285   }
286 }
287
288 TEST_F(DownloadItemModelTest, InProgressStatus) {
289   const struct TestCase {
290     int64 received_bytes;               // Return value of GetReceivedBytes().
291     int64 total_bytes;                  // Return value of GetTotalBytes().
292     bool  time_remaining_known;         // If TimeRemaining() is known.
293     bool  open_when_complete;           // GetOpenWhenComplete().
294     bool  is_paused;                    // IsPaused().
295     const char* expected_status;        // Expected status text.
296   } kTestCases[] = {
297     // These are all the valid combinations of the above fields for a download
298     // that is in IN_PROGRESS state. Go through all of them and check the return
299     // value of DownloadItemModel::GetStatusText(). The point isn't to lock down
300     // the status strings, but to make sure we end up with something sane for
301     // all the circumstances we care about.
302     //
303     // For GetReceivedBytes()/GetTotalBytes(), we only check whether each is
304     // non-zero. In addition, if |total_bytes| is zero, then
305     // |time_remaining_known| is also false.
306     //
307     //         .-- .TimeRemaining() is known.
308     //        |       .-- .GetOpenWhenComplete()
309     //        |      |      .---- .IsPaused()
310     { 0, 0, false, false, false, "Starting..." },
311     { 1, 0, false, false, false, "1 B" },
312     { 0, 2, false, false, false, "Starting..." },
313     { 1, 2, false, false, false, "1/2 B" },
314     { 0, 2, true,  false, false, "0/2 B, 10 secs left" },
315     { 1, 2, true,  false, false, "1/2 B, 10 secs left" },
316     { 0, 0, false, true,  false, "Opening when complete" },
317     { 1, 0, false, true,  false, "Opening when complete" },
318     { 0, 2, false, true,  false, "Opening when complete" },
319     { 1, 2, false, true,  false, "Opening when complete" },
320     { 0, 2, true,  true,  false, "Opening in 10 secs..." },
321     { 1, 2, true,  true,  false, "Opening in 10 secs..." },
322     { 0, 0, false, false, true,  "0 B, Paused" },
323     { 1, 0, false, false, true,  "1 B, Paused" },
324     { 0, 2, false, false, true,  "0/2 B, Paused" },
325     { 1, 2, false, false, true,  "1/2 B, Paused" },
326     { 0, 2, true,  false, true,  "0/2 B, Paused" },
327     { 1, 2, true,  false, true,  "1/2 B, Paused" },
328     { 0, 0, false, true,  true,  "0 B, Paused" },
329     { 1, 0, false, true,  true,  "1 B, Paused" },
330     { 0, 2, false, true,  true,  "0/2 B, Paused" },
331     { 1, 2, false, true,  true,  "1/2 B, Paused" },
332     { 0, 2, true,  true,  true,  "0/2 B, Paused" },
333     { 1, 2, true,  true,  true,  "1/2 B, Paused" },
334   };
335
336   SetupDownloadItemDefaults();
337
338   for (unsigned i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); i++) {
339     const TestCase& test_case = kTestCases[i];
340     Mock::VerifyAndClearExpectations(&item());
341     Mock::VerifyAndClearExpectations(&model());
342     EXPECT_CALL(item(), GetReceivedBytes())
343         .WillRepeatedly(Return(test_case.received_bytes));
344     EXPECT_CALL(item(), GetTotalBytes())
345         .WillRepeatedly(Return(test_case.total_bytes));
346     EXPECT_CALL(item(), TimeRemaining(_))
347         .WillRepeatedly(testing::DoAll(
348             testing::SetArgPointee<0>(base::TimeDelta::FromSeconds(10)),
349             Return(test_case.time_remaining_known)));
350     EXPECT_CALL(item(), GetOpenWhenComplete())
351         .WillRepeatedly(Return(test_case.open_when_complete));
352     EXPECT_CALL(item(), IsPaused())
353         .WillRepeatedly(Return(test_case.is_paused));
354
355     EXPECT_STREQ(test_case.expected_status,
356                  base::UTF16ToUTF8(model().GetStatusText()).c_str());
357   }
358 }
359
360 TEST_F(DownloadItemModelTest, ShouldShowInShelf) {
361   SetupDownloadItemDefaults();
362
363   // By default the download item should be displayable on the shelf.
364   EXPECT_TRUE(model().ShouldShowInShelf());
365
366   // Once explicitly set, ShouldShowInShelf() should return the explicit value.
367   model().SetShouldShowInShelf(false);
368   EXPECT_FALSE(model().ShouldShowInShelf());
369
370   model().SetShouldShowInShelf(true);
371   EXPECT_TRUE(model().ShouldShowInShelf());
372 }
373
374 TEST_F(DownloadItemModelTest, ShouldRemoveFromShelfWhenComplete) {
375   const struct TestCase {
376     DownloadItem::DownloadState state;
377     bool is_dangerous;  // Expectation for IsDangerous().
378     bool is_auto_open;  // Expectation for GetOpenWhenComplete().
379     bool auto_opened;   // Whether the download was successfully
380                         // auto-opened. Expecation for GetAutoOpened().
381     bool expected_result;
382   } kTestCases[] = {
383     // All the valid combinations of state, is_dangerous, is_auto_open and
384     // auto_opened.
385     //
386     //                              .--- Is dangerous.
387     //                             |       .--- Auto open or temporary.
388     //                             |      |      .--- Auto opened.
389     //                             |      |      |      .--- Expected result.
390     { DownloadItem::IN_PROGRESS, false, false, false, false},
391     { DownloadItem::IN_PROGRESS, false, true , false, true },
392     { DownloadItem::IN_PROGRESS, true , false, false, false},
393     { DownloadItem::IN_PROGRESS, true , true , false, false},
394     { DownloadItem::COMPLETE,    false, false, false, false},
395     { DownloadItem::COMPLETE,    false, true , false, false},
396     { DownloadItem::COMPLETE,    false, false, true , true },
397     { DownloadItem::COMPLETE,    false, true , true , true },
398     { DownloadItem::CANCELLED,   false, false, false, false},
399     { DownloadItem::CANCELLED,   false, true , false, false},
400     { DownloadItem::CANCELLED,   true , false, false, false},
401     { DownloadItem::CANCELLED,   true , true , false, false},
402     { DownloadItem::INTERRUPTED, false, false, false, false},
403     { DownloadItem::INTERRUPTED, false, true , false, false},
404     { DownloadItem::INTERRUPTED, true , false, false, false},
405     { DownloadItem::INTERRUPTED, true , true , false, false}
406   };
407
408   SetupDownloadItemDefaults();
409
410   for (unsigned i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); i++) {
411     const TestCase& test_case = kTestCases[i];
412     EXPECT_CALL(item(), GetOpenWhenComplete())
413         .WillRepeatedly(Return(test_case.is_auto_open));
414     EXPECT_CALL(item(), GetState())
415         .WillRepeatedly(Return(test_case.state));
416     EXPECT_CALL(item(), IsDangerous())
417         .WillRepeatedly(Return(test_case.is_dangerous));
418     EXPECT_CALL(item(), GetAutoOpened())
419         .WillRepeatedly(Return(test_case.auto_opened));
420
421     EXPECT_EQ(test_case.expected_result,
422               model().ShouldRemoveFromShelfWhenComplete())
423         << "Test case: " << i;
424     Mock::VerifyAndClearExpectations(&item());
425     Mock::VerifyAndClearExpectations(&model());
426   }
427 }