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.
5 #include "content/public/test/test_file_error_injector.h"
9 #include "base/compiler_specific.h"
10 #include "base/logging.h"
11 #include "content/browser/download/download_file_factory.h"
12 #include "content/browser/download/download_file_impl.h"
13 #include "content/browser/download/download_interrupt_reasons_impl.h"
14 #include "content/browser/download/download_manager_impl.h"
15 #include "content/browser/loader/resource_dispatcher_host_impl.h"
16 #include "content/public/browser/browser_thread.h"
20 class ByteStreamReader
;
24 // A class that performs file operations and injects errors.
25 class DownloadFileWithErrors
: public DownloadFileImpl
{
27 typedef base::Callback
<void(const GURL
& url
)> ConstructionCallback
;
28 typedef base::Callback
<void(const GURL
& url
)> DestructionCallback
;
30 DownloadFileWithErrors(
31 scoped_ptr
<DownloadSaveInfo
> save_info
,
32 const base::FilePath
& default_download_directory
,
34 const GURL
& referrer_url
,
36 scoped_ptr
<ByteStreamReader
> stream
,
37 const net::BoundNetLog
& bound_net_log
,
38 base::WeakPtr
<DownloadDestinationObserver
> observer
,
39 const TestFileErrorInjector::FileErrorInfo
& error_info
,
40 const ConstructionCallback
& ctor_callback
,
41 const DestructionCallback
& dtor_callback
);
43 virtual ~DownloadFileWithErrors();
45 virtual void Initialize(const InitializeCallback
& callback
) OVERRIDE
;
47 // DownloadFile interface.
48 virtual DownloadInterruptReason
AppendDataToFile(
49 const char* data
, size_t data_len
) OVERRIDE
;
50 virtual void RenameAndUniquify(
51 const base::FilePath
& full_path
,
52 const RenameCompletionCallback
& callback
) OVERRIDE
;
53 virtual void RenameAndAnnotate(
54 const base::FilePath
& full_path
,
55 const RenameCompletionCallback
& callback
) OVERRIDE
;
58 // Error generating helper.
59 DownloadInterruptReason
ShouldReturnError(
60 TestFileErrorInjector::FileOperationCode code
,
61 DownloadInterruptReason original_error
);
63 // Determine whether to overwrite an operation with the given code
64 // with a substitute error; if returns true, |*original_error| is
65 // written with the error to use for overwriting.
66 // NOTE: This routine changes state; specifically, it increases the
67 // operations counts for the specified code. It should only be called
68 // once per operation.
70 TestFileErrorInjector::FileOperationCode code
,
71 DownloadInterruptReason
* output_error
);
73 // Source URL for the file being downloaded.
76 // Our injected error. Only one per file.
77 TestFileErrorInjector::FileErrorInfo error_info_
;
79 // Count per operation. 0-based.
80 std::map
<TestFileErrorInjector::FileOperationCode
, int> operation_counter_
;
82 // Callback for destruction.
83 DestructionCallback destruction_callback_
;
86 static void InitializeErrorCallback(
87 const DownloadFile::InitializeCallback original_callback
,
88 DownloadInterruptReason overwrite_error
,
89 DownloadInterruptReason original_error
) {
90 original_callback
.Run(overwrite_error
);
93 static void RenameErrorCallback(
94 const DownloadFile::RenameCompletionCallback original_callback
,
95 DownloadInterruptReason overwrite_error
,
96 DownloadInterruptReason original_error
,
97 const base::FilePath
& path_result
) {
98 original_callback
.Run(
100 overwrite_error
== DOWNLOAD_INTERRUPT_REASON_NONE
?
101 path_result
: base::FilePath());
104 DownloadFileWithErrors::DownloadFileWithErrors(
105 scoped_ptr
<DownloadSaveInfo
> save_info
,
106 const base::FilePath
& default_download_directory
,
108 const GURL
& referrer_url
,
110 scoped_ptr
<ByteStreamReader
> stream
,
111 const net::BoundNetLog
& bound_net_log
,
112 base::WeakPtr
<DownloadDestinationObserver
> observer
,
113 const TestFileErrorInjector::FileErrorInfo
& error_info
,
114 const ConstructionCallback
& ctor_callback
,
115 const DestructionCallback
& dtor_callback
)
117 save_info
.Pass(), default_download_directory
, url
, referrer_url
,
118 calculate_hash
, stream
.Pass(), bound_net_log
, observer
),
120 error_info_(error_info
),
121 destruction_callback_(dtor_callback
) {
122 // DownloadFiles are created on the UI thread and are destroyed on the FILE
123 // thread. Schedule the ConstructionCallback on the FILE thread so that if a
124 // DownloadItem schedules a DownloadFile to be destroyed and creates another
125 // one (as happens during download resumption), then the DestructionCallback
126 // for the old DownloadFile is run before the ConstructionCallback for the
127 // next DownloadFile.
128 BrowserThread::PostTask(
131 base::Bind(ctor_callback
, source_url_
));
134 DownloadFileWithErrors::~DownloadFileWithErrors() {
135 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
136 destruction_callback_
.Run(source_url_
);
139 void DownloadFileWithErrors::Initialize(
140 const InitializeCallback
& callback
) {
141 DownloadInterruptReason error_to_return
= DOWNLOAD_INTERRUPT_REASON_NONE
;
142 InitializeCallback callback_to_use
= callback
;
144 // Replace callback if the error needs to be overwritten.
146 TestFileErrorInjector::FILE_OPERATION_INITIALIZE
,
148 if (DOWNLOAD_INTERRUPT_REASON_NONE
!= error_to_return
) {
149 // Don't execute a, probably successful, Initialize; just
151 BrowserThread::PostTask(
152 BrowserThread::UI
, FROM_HERE
, base::Bind(
153 callback
, error_to_return
));
157 // Otherwise, just wrap the return.
158 callback_to_use
= base::Bind(&InitializeErrorCallback
, callback
,
162 DownloadFileImpl::Initialize(callback_to_use
);
165 DownloadInterruptReason
DownloadFileWithErrors::AppendDataToFile(
166 const char* data
, size_t data_len
) {
167 return ShouldReturnError(
168 TestFileErrorInjector::FILE_OPERATION_WRITE
,
169 DownloadFileImpl::AppendDataToFile(data
, data_len
));
172 void DownloadFileWithErrors::RenameAndUniquify(
173 const base::FilePath
& full_path
,
174 const RenameCompletionCallback
& callback
) {
175 DownloadInterruptReason error_to_return
= DOWNLOAD_INTERRUPT_REASON_NONE
;
176 RenameCompletionCallback callback_to_use
= callback
;
178 // Replace callback if the error needs to be overwritten.
180 TestFileErrorInjector::FILE_OPERATION_RENAME_UNIQUIFY
,
182 if (DOWNLOAD_INTERRUPT_REASON_NONE
!= error_to_return
) {
183 // Don't execute a, probably successful, RenameAndUniquify; just
185 BrowserThread::PostTask(
186 BrowserThread::UI
, FROM_HERE
, base::Bind(
187 callback
, error_to_return
, base::FilePath()));
191 // Otherwise, just wrap the return.
192 callback_to_use
= base::Bind(&RenameErrorCallback
, callback
,
196 DownloadFileImpl::RenameAndUniquify(full_path
, callback_to_use
);
199 void DownloadFileWithErrors::RenameAndAnnotate(
200 const base::FilePath
& full_path
,
201 const RenameCompletionCallback
& callback
) {
202 DownloadInterruptReason error_to_return
= DOWNLOAD_INTERRUPT_REASON_NONE
;
203 RenameCompletionCallback callback_to_use
= callback
;
205 // Replace callback if the error needs to be overwritten.
207 TestFileErrorInjector::FILE_OPERATION_RENAME_ANNOTATE
,
209 if (DOWNLOAD_INTERRUPT_REASON_NONE
!= error_to_return
) {
210 // Don't execute a, probably successful, RenameAndAnnotate; just
212 BrowserThread::PostTask(
213 BrowserThread::UI
, FROM_HERE
, base::Bind(
214 callback
, error_to_return
, base::FilePath()));
218 // Otherwise, just wrap the return.
219 callback_to_use
= base::Bind(&RenameErrorCallback
, callback
,
223 DownloadFileImpl::RenameAndAnnotate(full_path
, callback_to_use
);
226 bool DownloadFileWithErrors::OverwriteError(
227 TestFileErrorInjector::FileOperationCode code
,
228 DownloadInterruptReason
* output_error
) {
229 int counter
= operation_counter_
[code
]++;
231 if (code
!= error_info_
.code
)
234 if (counter
!= error_info_
.operation_instance
)
237 *output_error
= error_info_
.error
;
241 DownloadInterruptReason
DownloadFileWithErrors::ShouldReturnError(
242 TestFileErrorInjector::FileOperationCode code
,
243 DownloadInterruptReason original_error
) {
244 DownloadInterruptReason output_error
= original_error
;
245 OverwriteError(code
, &output_error
);
251 // A factory for constructing DownloadFiles that inject errors.
252 class DownloadFileWithErrorsFactory
: public DownloadFileFactory
{
254 DownloadFileWithErrorsFactory(
255 const DownloadFileWithErrors::ConstructionCallback
& ctor_callback
,
256 const DownloadFileWithErrors::DestructionCallback
& dtor_callback
);
257 virtual ~DownloadFileWithErrorsFactory();
259 // DownloadFileFactory interface.
260 virtual DownloadFile
* CreateFile(
261 scoped_ptr
<DownloadSaveInfo
> save_info
,
262 const base::FilePath
& default_download_directory
,
264 const GURL
& referrer_url
,
266 scoped_ptr
<ByteStreamReader
> stream
,
267 const net::BoundNetLog
& bound_net_log
,
268 base::WeakPtr
<DownloadDestinationObserver
> observer
) OVERRIDE
;
271 const TestFileErrorInjector::FileErrorInfo
& error_info
);
276 // Our injected error list, mapped by URL. One per file.
277 TestFileErrorInjector::ErrorMap injected_errors_
;
279 // Callback for creation and destruction.
280 DownloadFileWithErrors::ConstructionCallback construction_callback_
;
281 DownloadFileWithErrors::DestructionCallback destruction_callback_
;
284 DownloadFileWithErrorsFactory::DownloadFileWithErrorsFactory(
285 const DownloadFileWithErrors::ConstructionCallback
& ctor_callback
,
286 const DownloadFileWithErrors::DestructionCallback
& dtor_callback
)
287 : construction_callback_(ctor_callback
),
288 destruction_callback_(dtor_callback
) {
291 DownloadFileWithErrorsFactory::~DownloadFileWithErrorsFactory() {
294 DownloadFile
* DownloadFileWithErrorsFactory::CreateFile(
295 scoped_ptr
<DownloadSaveInfo
> save_info
,
296 const base::FilePath
& default_download_directory
,
298 const GURL
& referrer_url
,
300 scoped_ptr
<ByteStreamReader
> stream
,
301 const net::BoundNetLog
& bound_net_log
,
302 base::WeakPtr
<DownloadDestinationObserver
> observer
) {
303 if (injected_errors_
.find(url
.spec()) == injected_errors_
.end()) {
304 // Have to create entry, because FileErrorInfo is not a POD type.
305 TestFileErrorInjector::FileErrorInfo err_info
= {
307 TestFileErrorInjector::FILE_OPERATION_INITIALIZE
,
309 DOWNLOAD_INTERRUPT_REASON_NONE
311 injected_errors_
[url
.spec()] = err_info
;
314 return new DownloadFileWithErrors(
316 default_download_directory
,
323 injected_errors_
[url
.spec()],
324 construction_callback_
,
325 destruction_callback_
);
328 bool DownloadFileWithErrorsFactory::AddError(
329 const TestFileErrorInjector::FileErrorInfo
& error_info
) {
330 // Creates an empty entry if necessary. Duplicate entries overwrite.
331 injected_errors_
[error_info
.url
] = error_info
;
336 void DownloadFileWithErrorsFactory::ClearErrors() {
337 injected_errors_
.clear();
340 TestFileErrorInjector::TestFileErrorInjector(
341 DownloadManager
* download_manager
)
342 : created_factory_(NULL
),
343 // This code is only used for browser_tests, so a
344 // DownloadManager is always a DownloadManagerImpl.
345 download_manager_(static_cast<DownloadManagerImpl
*>(download_manager
)) {
346 // Record the value of the pointer, for later validation.
348 new DownloadFileWithErrorsFactory(
349 base::Bind(&TestFileErrorInjector::RecordDownloadFileConstruction
,
351 base::Bind(&TestFileErrorInjector::RecordDownloadFileDestruction
,
354 // We will transfer ownership of the factory to the download manager.
355 scoped_ptr
<DownloadFileFactory
> download_file_factory(
358 download_manager_
->SetDownloadFileFactoryForTesting(
359 download_file_factory
.Pass());
362 TestFileErrorInjector::~TestFileErrorInjector() {
365 bool TestFileErrorInjector::AddError(const FileErrorInfo
& error_info
) {
366 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
367 DCHECK_LE(0, error_info
.operation_instance
);
368 DCHECK(injected_errors_
.find(error_info
.url
) == injected_errors_
.end());
370 // Creates an empty entry if necessary.
371 injected_errors_
[error_info
.url
] = error_info
;
376 void TestFileErrorInjector::ClearErrors() {
377 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
378 injected_errors_
.clear();
381 bool TestFileErrorInjector::InjectErrors() {
382 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
386 DCHECK_EQ(static_cast<DownloadFileFactory
*>(created_factory_
),
387 download_manager_
->GetDownloadFileFactoryForTesting());
389 created_factory_
->ClearErrors();
391 for (ErrorMap::const_iterator it
= injected_errors_
.begin();
392 it
!= injected_errors_
.end(); ++it
)
393 created_factory_
->AddError(it
->second
);
398 size_t TestFileErrorInjector::CurrentFileCount() const {
399 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
400 return files_
.size();
403 size_t TestFileErrorInjector::TotalFileCount() const {
404 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
405 return found_files_
.size();
409 bool TestFileErrorInjector::HadFile(const GURL
& url
) const {
410 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
412 return (found_files_
.find(url
) != found_files_
.end());
415 void TestFileErrorInjector::ClearFoundFiles() {
416 found_files_
.clear();
419 void TestFileErrorInjector::DownloadFileCreated(GURL url
) {
420 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
421 DCHECK(files_
.find(url
) == files_
.end());
424 found_files_
.insert(url
);
427 void TestFileErrorInjector::DestroyingDownloadFile(GURL url
) {
428 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
429 DCHECK(files_
.find(url
) != files_
.end());
434 void TestFileErrorInjector::RecordDownloadFileConstruction(const GURL
& url
) {
435 BrowserThread::PostTask(
438 base::Bind(&TestFileErrorInjector::DownloadFileCreated
, this, url
));
441 void TestFileErrorInjector::RecordDownloadFileDestruction(const GURL
& url
) {
442 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
443 base::Bind(&TestFileErrorInjector::DestroyingDownloadFile
, this, url
));
447 scoped_refptr
<TestFileErrorInjector
> TestFileErrorInjector::Create(
448 DownloadManager
* download_manager
) {
449 static bool visited
= false;
450 DCHECK(!visited
); // Only allowed to be called once.
453 scoped_refptr
<TestFileErrorInjector
> single_injector(
454 new TestFileErrorInjector(download_manager
));
456 return single_injector
;
460 std::string
TestFileErrorInjector::DebugString(FileOperationCode code
) {
462 case FILE_OPERATION_INITIALIZE
:
464 case FILE_OPERATION_WRITE
:
466 case FILE_OPERATION_RENAME_UNIQUIFY
:
467 return "RENAME_UNIQUIFY";
468 case FILE_OPERATION_RENAME_ANNOTATE
:
469 return "RENAME_ANNOTATE";
477 } // namespace content