download_manager_unittest.cc revision dc0f95d653279beabeb9817299e2902918ba123e
1// Copyright (c) 2010 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 <string>
6
7#include "base/string_util.h"
8#include "build/build_config.h"
9#include "chrome/browser/download/download_file.h"
10#include "chrome/browser/download/download_file_manager.h"
11#include "chrome/browser/download/download_item.h"
12#include "chrome/browser/download/download_manager.h"
13#include "chrome/browser/download/download_prefs.h"
14#include "chrome/browser/download/download_status_updater.h"
15#include "chrome/browser/download/download_util.h"
16#include "chrome/browser/download/mock_download_manager.h"
17#include "chrome/browser/history/download_create_info.h"
18#include "chrome/browser/prefs/pref_service.h"
19#include "chrome/common/pref_names.h"
20#include "chrome/test/testing_profile.h"
21#include "content/browser/browser_thread.h"
22#include "testing/gmock/include/gmock/gmock.h"
23#include "testing/gmock_mutant.h"
24#include "testing/gtest/include/gtest/gtest.h"
25
26class DownloadManagerTest : public testing::Test {
27 public:
28  DownloadManagerTest()
29      : profile_(new TestingProfile()),
30        download_manager_(new MockDownloadManager(&download_status_updater_)),
31        ui_thread_(BrowserThread::UI, &message_loop_) {
32    download_manager_->Init(profile_.get());
33  }
34
35  ~DownloadManagerTest() {
36    download_manager_->Shutdown();
37    // profile_ must outlive download_manager_, so we explicitly delete
38    // download_manager_ first.
39    download_manager_ = NULL;
40    profile_.reset(NULL);
41    message_loop_.RunAllPending();
42  }
43
44  void AddDownloadToFileManager(int id, DownloadFile* download) {
45    file_manager()->downloads_[id] = download;
46  }
47
48 protected:
49  DownloadStatusUpdater download_status_updater_;
50  scoped_ptr<TestingProfile> profile_;
51  scoped_refptr<DownloadManager> download_manager_;
52  scoped_refptr<DownloadFileManager> file_manager_;
53  MessageLoopForUI message_loop_;
54  BrowserThread ui_thread_;
55
56  DownloadFileManager* file_manager() {
57    if (!file_manager_) {
58      file_manager_ = new DownloadFileManager(NULL);
59      download_manager_->file_manager_ = file_manager_;
60    }
61    return file_manager_;
62  }
63
64  // Make sure download item |id| was set with correct safety state for
65  // given |is_dangerous_file| and |is_dangerous_url|.
66  bool VerifySafetyState(bool is_dangerous_file,
67                         bool is_dangerous_url,
68                         int id) {
69    DownloadItem::SafetyState safety_state =
70        download_manager_->GetDownloadItem(id)->safety_state();
71    return (is_dangerous_file || is_dangerous_url) ?
72        safety_state != DownloadItem::SAFE : safety_state == DownloadItem::SAFE;
73  }
74
75  DISALLOW_COPY_AND_ASSIGN(DownloadManagerTest);
76};
77
78namespace {
79
80const struct {
81  const char* url;
82  const char* mime_type;
83  bool save_as;
84  bool prompt_for_download;
85  bool expected_save_as;
86} kStartDownloadCases[] = {
87  { "http://www.foo.com/dont-open.html",
88    "text/html",
89    false,
90    false,
91    false, },
92  { "http://www.foo.com/save-as.html",
93    "text/html",
94    true,
95    false,
96    true, },
97  { "http://www.foo.com/always-prompt.html",
98    "text/html",
99    false,
100    true,
101    true, },
102  { "http://www.foo.com/user-script-text-html-mimetype.user.js",
103    "text/html",
104    false,
105    false,
106    false, },
107  { "http://www.foo.com/extensionless-extension",
108    "application/x-chrome-extension",
109    true,
110    false,
111    true, },
112  { "http://www.foo.com/save-as.pdf",
113    "application/pdf",
114    true,
115    false,
116    true, },
117  { "http://www.foo.com/sometimes_prompt.pdf",
118    "application/pdf",
119    false,
120    true,
121    false, },
122  { "http://www.foo.com/always_prompt.jar",
123    "application/jar",
124    false,
125    true,
126    true, },
127};
128
129}  // namespace
130
131TEST_F(DownloadManagerTest, StartDownload) {
132  BrowserThread io_thread(BrowserThread::IO, &message_loop_);
133  PrefService* prefs = profile_->GetPrefs();
134  prefs->SetFilePath(prefs::kDownloadDefaultDirectory, FilePath());
135  download_manager_->download_prefs()->EnableAutoOpenBasedOnExtension(
136      FilePath(FILE_PATH_LITERAL("example.pdf")));
137
138  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kStartDownloadCases); ++i) {
139    prefs->SetBoolean(prefs::kPromptForDownload,
140                      kStartDownloadCases[i].prompt_for_download);
141
142    DownloadCreateInfo info;
143    info.prompt_user_for_save_location = kStartDownloadCases[i].save_as;
144    info.url = GURL(kStartDownloadCases[i].url);
145    info.mime_type = kStartDownloadCases[i].mime_type;
146
147    download_manager_->StartDownload(&info);
148    message_loop_.RunAllPending();
149
150    EXPECT_EQ(kStartDownloadCases[i].expected_save_as,
151        info.prompt_user_for_save_location);
152  }
153}
154
155namespace {
156
157const struct {
158  FilePath::StringType suggested_path;
159  bool is_dangerous_file;
160  bool is_dangerous_url;
161  bool finish_before_rename;
162  int expected_rename_count;
163} kDownloadRenameCases[] = {
164  // Safe download, download finishes BEFORE file name determined.
165  // Renamed twice (linear path through UI).  Crdownload file does not need
166  // to be deleted.
167  { FILE_PATH_LITERAL("foo.zip"),
168    false, false, true, 2, },
169  // Dangerous download (file is dangerous or download URL is not safe or both),
170  // download finishes BEFORE file name determined. Needs to be renamed only
171  // once.
172  { FILE_PATH_LITERAL("Unconfirmed xxx.crdownload"),
173    true, false, true, 1, },
174  { FILE_PATH_LITERAL("Unconfirmed xxx.crdownload"),
175    false, true, true, 1, },
176  { FILE_PATH_LITERAL("Unconfirmed xxx.crdownload"),
177    true, true, true, 1, },
178  // Safe download, download finishes AFTER file name determined.
179  // Needs to be renamed twice.
180  { FILE_PATH_LITERAL("foo.zip"),
181    false, false, false, 2, },
182  // Dangerous download, download finishes AFTER file name determined.
183  // Needs to be renamed only once.
184  { FILE_PATH_LITERAL("Unconfirmed xxx.crdownload"),
185    true, false, false, 1, },
186  { FILE_PATH_LITERAL("Unconfirmed xxx.crdownload"),
187    false, true, false, 1, },
188  { FILE_PATH_LITERAL("Unconfirmed xxx.crdownload"),
189    true, true, false, 1, },
190};
191
192class MockDownloadFile : public DownloadFile {
193 public:
194  explicit MockDownloadFile(DownloadCreateInfo* info)
195      : DownloadFile(info, NULL), renamed_count_(0) { }
196  virtual ~MockDownloadFile() { Destructed(); }
197  MOCK_METHOD1(Rename, bool(const FilePath&));
198  MOCK_METHOD0(Destructed, void());
199
200  bool TestMultipleRename(
201      int expected_count, const FilePath& expected,
202      const FilePath& path) {
203    ++renamed_count_;
204    EXPECT_EQ(expected_count, renamed_count_);
205    EXPECT_EQ(expected.value(), path.value());
206    return true;
207  }
208
209 private:
210  int renamed_count_;
211};
212
213}  // namespace
214
215TEST_F(DownloadManagerTest, DownloadRenameTest) {
216  using ::testing::_;
217  using ::testing::CreateFunctor;
218  using ::testing::Invoke;
219  using ::testing::Return;
220
221  BrowserThread file_thread(BrowserThread::FILE, &message_loop_);
222
223  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kDownloadRenameCases); ++i) {
224    // |info| will be destroyed in download_manager_.
225    DownloadCreateInfo* info(new DownloadCreateInfo);
226    info->download_id = static_cast<int>(i);
227    info->prompt_user_for_save_location = false;
228    info->is_dangerous_file = kDownloadRenameCases[i].is_dangerous_file;
229    info->is_dangerous_url = kDownloadRenameCases[i].is_dangerous_url;
230    FilePath new_path(kDownloadRenameCases[i].suggested_path);
231
232    MockDownloadFile* download(new MockDownloadFile(info));
233    AddDownloadToFileManager(info->download_id, download);
234
235    // |download| is owned by DownloadFileManager.
236    ::testing::Mock::AllowLeak(download);
237    EXPECT_CALL(*download, Destructed()).Times(1);
238
239    if (kDownloadRenameCases[i].expected_rename_count == 1) {
240      EXPECT_CALL(*download, Rename(new_path)).WillOnce(Return(true));
241    } else {
242      ASSERT_EQ(2, kDownloadRenameCases[i].expected_rename_count);
243      FilePath crdownload(download_util::GetCrDownloadPath(new_path));
244      EXPECT_CALL(*download, Rename(_))
245          .WillOnce(testing::WithArgs<0>(Invoke(CreateFunctor(
246              download, &MockDownloadFile::TestMultipleRename,
247              1, crdownload))))
248          .WillOnce(testing::WithArgs<0>(Invoke(CreateFunctor(
249              download, &MockDownloadFile::TestMultipleRename,
250              2, new_path))));
251    }
252    download_manager_->CreateDownloadItem(info);
253
254    if (kDownloadRenameCases[i].finish_before_rename) {
255      download_manager_->OnAllDataSaved(i, 1024);
256      download_manager_->FileSelected(new_path, i, info);
257    } else {
258      download_manager_->FileSelected(new_path, i, info);
259      download_manager_->OnAllDataSaved(i, 1024);
260    }
261
262    message_loop_.RunAllPending();
263    EXPECT_TRUE(VerifySafetyState(kDownloadRenameCases[i].is_dangerous_file,
264                                  kDownloadRenameCases[i].is_dangerous_url,
265                                  i));
266  }
267}
268