OLD | NEW |
| (Empty) |
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 "content/browser/download/download_file_manager.h" | |
6 | |
7 #include "base/file_path.h" | |
8 #include "base/file_util.h" | |
9 #include "base/message_loop.h" | |
10 #include "base/scoped_temp_dir.h" | |
11 #include "base/string_number_conversions.h" | |
12 #include "content/browser/browser_thread_impl.h" | |
13 #include "content/browser/download/byte_stream.h" | |
14 #include "content/browser/download/download_create_info.h" | |
15 #include "content/browser/download/download_interrupt_reasons_impl.h" | |
16 #include "content/browser/download/download_request_handle.h" | |
17 #include "content/browser/download/mock_download_file.h" | |
18 #include "content/public/browser/download_id.h" | |
19 #include "content/public/test/mock_download_manager.h" | |
20 #include "net/base/io_buffer.h" | |
21 #include "net/base/net_errors.h" | |
22 #include "testing/gmock/include/gmock/gmock.h" | |
23 #include "testing/gtest/include/gtest/gtest.h" | |
24 | |
25 using content::BrowserThread; | |
26 using content::BrowserThreadImpl; | |
27 using content::DownloadId; | |
28 using content::MockDownloadManager; | |
29 | |
30 using ::testing::_; | |
31 using ::testing::AtLeast; | |
32 using ::testing::Mock; | |
33 using ::testing::Return; | |
34 using ::testing::SaveArg; | |
35 using ::testing::StrictMock; | |
36 using ::testing::StrEq; | |
37 | |
38 namespace { | |
39 | |
40 // MockDownloadManager with the addition of a mock callback used for testing. | |
41 class TestDownloadManager : public MockDownloadManager { | |
42 public: | |
43 MOCK_METHOD3(OnDownloadRenamed, | |
44 void(int download_id, | |
45 content::DownloadInterruptReason reason, | |
46 const FilePath& full_path)); | |
47 private: | |
48 ~TestDownloadManager() {} | |
49 }; | |
50 | |
51 class MockDownloadFileFactory : | |
52 public DownloadFileManager::DownloadFileFactory { | |
53 | |
54 public: | |
55 MockDownloadFileFactory() {} | |
56 virtual ~MockDownloadFileFactory() {} | |
57 | |
58 virtual content::DownloadFile* CreateFile( | |
59 DownloadCreateInfo* info, | |
60 scoped_ptr<content::ByteStreamReader> stream, | |
61 content::DownloadManager* download_manager, | |
62 bool calculate_hash, | |
63 const net::BoundNetLog& bound_net_log) OVERRIDE; | |
64 | |
65 MockDownloadFile* GetExistingFile(const DownloadId& id); | |
66 | |
67 private: | |
68 std::map<DownloadId, MockDownloadFile*> files_; | |
69 }; | |
70 | |
71 content::DownloadFile* MockDownloadFileFactory::CreateFile( | |
72 DownloadCreateInfo* info, | |
73 scoped_ptr<content::ByteStreamReader> stream, | |
74 content::DownloadManager* download_manager, | |
75 bool calculate_hash, | |
76 const net::BoundNetLog& bound_net_log) { | |
77 DCHECK(files_.end() == files_.find(info->download_id)); | |
78 MockDownloadFile* created_file = new StrictMock<MockDownloadFile>(); | |
79 files_[info->download_id] = created_file; | |
80 | |
81 ON_CALL(*created_file, GetDownloadManager()) | |
82 .WillByDefault(Return(download_manager)); | |
83 EXPECT_CALL(*created_file, Initialize()); | |
84 | |
85 return created_file; | |
86 } | |
87 | |
88 MockDownloadFile* MockDownloadFileFactory::GetExistingFile( | |
89 const DownloadId& id) { | |
90 DCHECK(files_.find(id) != files_.end()); | |
91 return files_[id]; | |
92 } | |
93 | |
94 class MockDownloadRequestHandle : public DownloadRequestHandle { | |
95 public: | |
96 MockDownloadRequestHandle(content::DownloadManager* manager) | |
97 : manager_(manager) {} | |
98 | |
99 virtual content::DownloadManager* GetDownloadManager() const OVERRIDE; | |
100 | |
101 private: | |
102 | |
103 content::DownloadManager* manager_; | |
104 }; | |
105 | |
106 content::DownloadManager* MockDownloadRequestHandle::GetDownloadManager() | |
107 const { | |
108 return manager_; | |
109 } | |
110 | |
111 void NullCallback() { } | |
112 | |
113 } // namespace | |
114 | |
115 class DownloadFileManagerTest : public testing::Test { | |
116 public: | |
117 // State of renamed file. Used with RenameFile(). | |
118 enum RenameFileState { | |
119 IN_PROGRESS, | |
120 COMPLETE | |
121 }; | |
122 | |
123 // Whether to overwrite the target filename in RenameFile(). | |
124 enum RenameFileOverwrite { | |
125 OVERWRITE, | |
126 DONT_OVERWRITE | |
127 }; | |
128 | |
129 static const char* kTestData1; | |
130 static const char* kTestData2; | |
131 static const char* kTestData3; | |
132 static const char* kTestData4; | |
133 static const char* kTestData5; | |
134 static const char* kTestData6; | |
135 static const int32 kDummyDownloadId; | |
136 static const int32 kDummyDownloadId2; | |
137 static const int kDummyChildId; | |
138 static const int kDummyRequestId; | |
139 | |
140 // We need a UI |BrowserThread| in order to destruct |download_manager_|, | |
141 // which has trait |BrowserThread::DeleteOnUIThread|. Without this, | |
142 // calling Release() on |download_manager_| won't ever result in its | |
143 // destructor being called and we get a leak. | |
144 DownloadFileManagerTest() | |
145 : last_reason_(content::DOWNLOAD_INTERRUPT_REASON_NONE), | |
146 ui_thread_(BrowserThread::UI, &loop_), | |
147 file_thread_(BrowserThread::FILE, &loop_) { | |
148 } | |
149 | |
150 ~DownloadFileManagerTest() { | |
151 } | |
152 | |
153 virtual void SetUp() { | |
154 download_manager_ = new TestDownloadManager(); | |
155 request_handle_.reset(new MockDownloadRequestHandle(download_manager_)); | |
156 download_file_factory_ = new MockDownloadFileFactory; | |
157 download_file_manager_ = new DownloadFileManager(download_file_factory_); | |
158 } | |
159 | |
160 virtual void TearDown() { | |
161 // When a DownloadManager's reference count drops to 0, it is not | |
162 // deleted immediately. Instead, a task is posted to the UI thread's | |
163 // message loop to delete it. | |
164 // So, drop the reference count to 0 and run the message loop once | |
165 // to ensure that all resources are cleaned up before the test exits. | |
166 download_manager_ = NULL; | |
167 ui_thread_.message_loop()->RunAllPending(); | |
168 } | |
169 | |
170 void ProcessAllPendingMessages() { | |
171 loop_.RunAllPending(); | |
172 } | |
173 | |
174 // Clears all gmock expectations for the download file |id| and the manager. | |
175 void ClearExpectations(DownloadId id) { | |
176 MockDownloadFile* file = download_file_factory_->GetExistingFile(id); | |
177 Mock::VerifyAndClearExpectations(file); | |
178 Mock::VerifyAndClearExpectations(download_manager_); | |
179 } | |
180 | |
181 void OnDownloadFileCreated(content::DownloadInterruptReason reason) { | |
182 last_reason_ = reason; | |
183 } | |
184 | |
185 // Create a download item on the DFM. | |
186 // |info| is the information needed to create a new download file. | |
187 // |id| is the download ID of the new download file. | |
188 void CreateDownloadFile(scoped_ptr<DownloadCreateInfo> info) { | |
189 // Mostly null out args; they'll be passed to MockDownloadFileFactory | |
190 // to be ignored anyway. | |
191 download_file_manager_->CreateDownloadFile( | |
192 info.Pass(), scoped_ptr<content::ByteStreamReader>(), | |
193 download_manager_, true, net::BoundNetLog(), | |
194 base::Bind(&DownloadFileManagerTest::OnDownloadFileCreated, | |
195 // The test jig will outlive all download files. | |
196 base::Unretained(this))); | |
197 | |
198 // Anything that isn't DOWNLOAD_INTERRUPT_REASON_NONE. | |
199 last_reason_ = content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED; | |
200 ProcessAllPendingMessages(); | |
201 EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE, last_reason_); | |
202 } | |
203 | |
204 // Renames the download file. | |
205 // |id| is the download ID of the download file. | |
206 // |new_path| is the new file path. | |
207 // |unique_path| is the actual path that the download file will be | |
208 // renamed to. If there is an existing file at | |
209 // |new_path| and |replace| is false, then |new_path| | |
210 // will be uniquified. | |
211 // |rename_error| is the error to inject. For no error, | |
212 // use content::DOWNLOAD_INTERRUPT_REASON_NONE. | |
213 // |state| whether we are renaming an in-progress download or a | |
214 // completed download. | |
215 // |should_overwrite| indicates whether to replace or uniquify the file. | |
216 void RenameFile(const DownloadId& id, | |
217 const FilePath& new_path, | |
218 bool should_overwrite) { | |
219 MockDownloadFile* file = download_file_factory_->GetExistingFile(id); | |
220 ASSERT_TRUE(file != NULL); | |
221 content::DownloadFile::RenameCompletionCallback rename_callback; | |
222 | |
223 EXPECT_CALL(*file, Rename(new_path, should_overwrite, _)) | |
224 .WillOnce(SaveArg<2>(&rename_callback)); | |
225 | |
226 content::DownloadFile::RenameCompletionCallback passed_callback( | |
227 base::Bind(&TestDownloadManager::OnDownloadRenamed, | |
228 download_manager_, id.local())); | |
229 | |
230 download_file_manager_->RenameDownloadFile( | |
231 id, new_path, should_overwrite, passed_callback); | |
232 | |
233 EXPECT_TRUE(rename_callback.Equals(passed_callback)); | |
234 } | |
235 | |
236 void Complete(DownloadId id) { | |
237 MockDownloadFile* file = download_file_factory_->GetExistingFile(id); | |
238 ASSERT_TRUE(file != NULL); | |
239 | |
240 EXPECT_CALL(*file, AnnotateWithSourceInformation()) | |
241 .WillOnce(Return()); | |
242 EXPECT_CALL(*file, Detach()) | |
243 .WillOnce(Return()); | |
244 int num_downloads = download_file_manager_->NumberOfActiveDownloads(); | |
245 EXPECT_LT(0, num_downloads); | |
246 download_file_manager_->CompleteDownload(id, base::Bind(NullCallback)); | |
247 EXPECT_EQ(num_downloads - 1, | |
248 download_file_manager_->NumberOfActiveDownloads()); | |
249 EXPECT_EQ(NULL, download_file_manager_->GetDownloadFile(id)); | |
250 } | |
251 | |
252 void CleanUp(DownloadId id) { | |
253 // Expected calls: | |
254 // DownloadFileManager::CancelDownload | |
255 // DownloadFile::Cancel | |
256 // DownloadFileManager::EraseDownload | |
257 // if no more downloads: | |
258 // DownloadFileManager::StopUpdateTimer | |
259 MockDownloadFile* file = download_file_factory_->GetExistingFile(id); | |
260 ASSERT_TRUE(file != NULL); | |
261 | |
262 EXPECT_CALL(*file, Cancel()); | |
263 | |
264 download_file_manager_->CancelDownload(id); | |
265 | |
266 EXPECT_EQ(NULL, download_file_manager_->GetDownloadFile(id)); | |
267 } | |
268 | |
269 protected: | |
270 scoped_refptr<TestDownloadManager> download_manager_; | |
271 scoped_ptr<MockDownloadRequestHandle> request_handle_; | |
272 MockDownloadFileFactory* download_file_factory_; | |
273 scoped_refptr<DownloadFileManager> download_file_manager_; | |
274 | |
275 // Error from creating download file. | |
276 content::DownloadInterruptReason last_reason_; | |
277 | |
278 // Per-download statistics. | |
279 std::map<DownloadId, int64> byte_count_; | |
280 std::map<DownloadId, int> error_count_; | |
281 | |
282 private: | |
283 MessageLoop loop_; | |
284 | |
285 // UI thread. | |
286 BrowserThreadImpl ui_thread_; | |
287 | |
288 // File thread to satisfy debug checks in DownloadFile. | |
289 BrowserThreadImpl file_thread_; | |
290 }; | |
291 | |
292 const char* DownloadFileManagerTest::kTestData1 = | |
293 "Let's write some data to the file!\n"; | |
294 const char* DownloadFileManagerTest::kTestData2 = "Writing more data.\n"; | |
295 const char* DownloadFileManagerTest::kTestData3 = "Final line."; | |
296 const char* DownloadFileManagerTest::kTestData4 = "Writing, writing, writing\n"; | |
297 const char* DownloadFileManagerTest::kTestData5 = "All we do is writing,\n"; | |
298 const char* DownloadFileManagerTest::kTestData6 = "Rawhide!"; | |
299 | |
300 const int32 DownloadFileManagerTest::kDummyDownloadId = 23; | |
301 const int32 DownloadFileManagerTest::kDummyDownloadId2 = 77; | |
302 const int DownloadFileManagerTest::kDummyChildId = 3; | |
303 const int DownloadFileManagerTest::kDummyRequestId = 67; | |
304 | |
305 TEST_F(DownloadFileManagerTest, Cancel) { | |
306 scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo); | |
307 DownloadId dummy_id(download_manager_.get(), kDummyDownloadId); | |
308 info->download_id = dummy_id; | |
309 | |
310 CreateDownloadFile(info.Pass()); | |
311 | |
312 CleanUp(dummy_id); | |
313 } | |
314 | |
315 TEST_F(DownloadFileManagerTest, Complete) { | |
316 scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo); | |
317 DownloadId dummy_id(download_manager_.get(), kDummyDownloadId); | |
318 info->download_id = dummy_id; | |
319 | |
320 CreateDownloadFile(info.Pass()); | |
321 | |
322 Complete(dummy_id); | |
323 } | |
324 | |
325 TEST_F(DownloadFileManagerTest, Rename) { | |
326 scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo); | |
327 DownloadId dummy_id(download_manager_.get(), kDummyDownloadId); | |
328 info->download_id = dummy_id; | |
329 ScopedTempDir download_dir; | |
330 ASSERT_TRUE(download_dir.CreateUniqueTempDir()); | |
331 | |
332 CreateDownloadFile(info.Pass()); | |
333 | |
334 FilePath foo(download_dir.path().Append(FILE_PATH_LITERAL("foo.txt"))); | |
335 RenameFile(dummy_id, foo, true); | |
336 | |
337 CleanUp(dummy_id); | |
338 } | |
339 | |
340 TEST_F(DownloadFileManagerTest, RenameNoOverwrite) { | |
341 scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo); | |
342 DownloadId dummy_id(download_manager_.get(), kDummyDownloadId); | |
343 info->download_id = dummy_id; | |
344 ScopedTempDir download_dir; | |
345 ASSERT_TRUE(download_dir.CreateUniqueTempDir()); | |
346 | |
347 CreateDownloadFile(info.Pass()); | |
348 | |
349 FilePath foo(download_dir.path().Append(FILE_PATH_LITERAL("foo.txt"))); | |
350 RenameFile(dummy_id, foo, false); | |
351 | |
352 CleanUp(dummy_id); | |
353 } | |
354 | |
355 TEST_F(DownloadFileManagerTest, RenameTwice) { | |
356 scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo); | |
357 DownloadId dummy_id(download_manager_.get(), kDummyDownloadId); | |
358 info->download_id = dummy_id; | |
359 ScopedTempDir download_dir; | |
360 ASSERT_TRUE(download_dir.CreateUniqueTempDir()); | |
361 | |
362 CreateDownloadFile(info.Pass()); | |
363 | |
364 FilePath crfoo(download_dir.path().Append( | |
365 FILE_PATH_LITERAL("foo.txt.crdownload"))); | |
366 RenameFile(dummy_id, crfoo, true); | |
367 | |
368 FilePath foo(download_dir.path().Append(FILE_PATH_LITERAL("foo.txt"))); | |
369 RenameFile(dummy_id, foo, true); | |
370 | |
371 CleanUp(dummy_id); | |
372 } | |
373 | |
374 TEST_F(DownloadFileManagerTest, TwoDownloads) { | |
375 // Same as StartDownload, at first. | |
376 scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo); | |
377 DownloadId dummy_id(download_manager_.get(), kDummyDownloadId); | |
378 info->download_id = dummy_id; | |
379 scoped_ptr<DownloadCreateInfo> info2(new DownloadCreateInfo); | |
380 DownloadId dummy_id2(download_manager_.get(), kDummyDownloadId2); | |
381 info2->download_id = dummy_id2; | |
382 ScopedTempDir download_dir; | |
383 ASSERT_TRUE(download_dir.CreateUniqueTempDir()); | |
384 | |
385 CreateDownloadFile(info.Pass()); | |
386 CreateDownloadFile(info2.Pass()); | |
387 | |
388 FilePath crbar(download_dir.path().Append( | |
389 FILE_PATH_LITERAL("bar.txt.crdownload"))); | |
390 RenameFile(dummy_id2, crbar, true); | |
391 | |
392 FilePath crfoo(download_dir.path().Append( | |
393 FILE_PATH_LITERAL("foo.txt.crdownload"))); | |
394 RenameFile(dummy_id, crfoo, true); | |
395 | |
396 | |
397 FilePath bar(download_dir.path().Append(FILE_PATH_LITERAL("bar.txt"))); | |
398 RenameFile(dummy_id2, bar, true); | |
399 | |
400 CleanUp(dummy_id2); | |
401 | |
402 FilePath foo(download_dir.path().Append(FILE_PATH_LITERAL("foo.txt"))); | |
403 RenameFile(dummy_id, foo, true); | |
404 | |
405 CleanUp(dummy_id); | |
406 } | |
407 | |
408 // TODO(ahendrickson) -- A test for download manager shutdown. | |
409 // Expected call sequence: | |
410 // OnDownloadManagerShutdown | |
411 // DownloadFile::GetDownloadManager | |
412 // DownloadFile::CancelDownloadRequest | |
413 // DownloadFile::~DownloadFile | |
OLD | NEW |