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 ~DownloadFileWithErrors() override
;
45 void Initialize(const InitializeCallback
& callback
) override
;
47 // DownloadFile interface.
48 DownloadInterruptReason
AppendDataToFile(const char* data
,
49 size_t data_len
) override
;
50 void RenameAndUniquify(const base::FilePath
& full_path
,
51 const RenameCompletionCallback
& callback
) override
;
52 void RenameAndAnnotate(const base::FilePath
& full_path
,
53 const RenameCompletionCallback
& callback
) override
;
56 // Error generating helper.
57 DownloadInterruptReason
ShouldReturnError(
58 TestFileErrorInjector::FileOperationCode code
,
59 DownloadInterruptReason original_error
);
61 // Determine whether to overwrite an operation with the given code
62 // with a substitute error; if returns true, |*original_error| is
63 // written with the error to use for overwriting.
64 // NOTE: This routine changes state; specifically, it increases the
65 // operations counts for the specified code. It should only be called
66 // once per operation.
68 TestFileErrorInjector::FileOperationCode code
,
69 DownloadInterruptReason
* output_error
);
71 // Source URL for the file being downloaded.
74 // Our injected error. Only one per file.
75 TestFileErrorInjector::FileErrorInfo error_info_
;
77 // Count per operation. 0-based.
78 std::map
<TestFileErrorInjector::FileOperationCode
, int> operation_counter_
;
80 // Callback for destruction.
81 DestructionCallback destruction_callback_
;
84 static void InitializeErrorCallback(
85 const DownloadFile::InitializeCallback original_callback
,
86 DownloadInterruptReason overwrite_error
,
87 DownloadInterruptReason original_error
) {
88 original_callback
.Run(overwrite_error
);
91 static void RenameErrorCallback(
92 const DownloadFile::RenameCompletionCallback original_callback
,
93 DownloadInterruptReason overwrite_error
,
94 DownloadInterruptReason original_error
,
95 const base::FilePath
& path_result
) {
96 original_callback
.Run(
98 overwrite_error
== DOWNLOAD_INTERRUPT_REASON_NONE
?
99 path_result
: base::FilePath());
102 DownloadFileWithErrors::DownloadFileWithErrors(
103 scoped_ptr
<DownloadSaveInfo
> save_info
,
104 const base::FilePath
& default_download_directory
,
106 const GURL
& referrer_url
,
108 scoped_ptr
<ByteStreamReader
> stream
,
109 const net::BoundNetLog
& bound_net_log
,
110 base::WeakPtr
<DownloadDestinationObserver
> observer
,
111 const TestFileErrorInjector::FileErrorInfo
& error_info
,
112 const ConstructionCallback
& ctor_callback
,
113 const DestructionCallback
& dtor_callback
)
115 save_info
.Pass(), default_download_directory
, url
, referrer_url
,
116 calculate_hash
, stream
.Pass(), bound_net_log
, observer
),
118 error_info_(error_info
),
119 destruction_callback_(dtor_callback
) {
120 // DownloadFiles are created on the UI thread and are destroyed on the FILE
121 // thread. Schedule the ConstructionCallback on the FILE thread so that if a
122 // DownloadItem schedules a DownloadFile to be destroyed and creates another
123 // one (as happens during download resumption), then the DestructionCallback
124 // for the old DownloadFile is run before the ConstructionCallback for the
125 // next DownloadFile.
126 BrowserThread::PostTask(
129 base::Bind(ctor_callback
, source_url_
));
132 DownloadFileWithErrors::~DownloadFileWithErrors() {
133 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
134 destruction_callback_
.Run(source_url_
);
137 void DownloadFileWithErrors::Initialize(
138 const InitializeCallback
& callback
) {
139 DownloadInterruptReason error_to_return
= DOWNLOAD_INTERRUPT_REASON_NONE
;
140 InitializeCallback callback_to_use
= callback
;
142 // Replace callback if the error needs to be overwritten.
144 TestFileErrorInjector::FILE_OPERATION_INITIALIZE
,
146 if (DOWNLOAD_INTERRUPT_REASON_NONE
!= error_to_return
) {
147 // Don't execute a, probably successful, Initialize; just
149 BrowserThread::PostTask(
150 BrowserThread::UI
, FROM_HERE
, base::Bind(
151 callback
, error_to_return
));
155 // Otherwise, just wrap the return.
156 callback_to_use
= base::Bind(&InitializeErrorCallback
, callback
,
160 DownloadFileImpl::Initialize(callback_to_use
);
163 DownloadInterruptReason
DownloadFileWithErrors::AppendDataToFile(
164 const char* data
, size_t data_len
) {
165 return ShouldReturnError(
166 TestFileErrorInjector::FILE_OPERATION_WRITE
,
167 DownloadFileImpl::AppendDataToFile(data
, data_len
));
170 void DownloadFileWithErrors::RenameAndUniquify(
171 const base::FilePath
& full_path
,
172 const RenameCompletionCallback
& callback
) {
173 DownloadInterruptReason error_to_return
= DOWNLOAD_INTERRUPT_REASON_NONE
;
174 RenameCompletionCallback callback_to_use
= callback
;
176 // Replace callback if the error needs to be overwritten.
178 TestFileErrorInjector::FILE_OPERATION_RENAME_UNIQUIFY
,
180 if (DOWNLOAD_INTERRUPT_REASON_NONE
!= error_to_return
) {
181 // Don't execute a, probably successful, RenameAndUniquify; just
183 BrowserThread::PostTask(
184 BrowserThread::UI
, FROM_HERE
, base::Bind(
185 callback
, error_to_return
, base::FilePath()));
189 // Otherwise, just wrap the return.
190 callback_to_use
= base::Bind(&RenameErrorCallback
, callback
,
194 DownloadFileImpl::RenameAndUniquify(full_path
, callback_to_use
);
197 void DownloadFileWithErrors::RenameAndAnnotate(
198 const base::FilePath
& full_path
,
199 const RenameCompletionCallback
& callback
) {
200 DownloadInterruptReason error_to_return
= DOWNLOAD_INTERRUPT_REASON_NONE
;
201 RenameCompletionCallback callback_to_use
= callback
;
203 // Replace callback if the error needs to be overwritten.
205 TestFileErrorInjector::FILE_OPERATION_RENAME_ANNOTATE
,
207 if (DOWNLOAD_INTERRUPT_REASON_NONE
!= error_to_return
) {
208 // Don't execute a, probably successful, RenameAndAnnotate; just
210 BrowserThread::PostTask(
211 BrowserThread::UI
, FROM_HERE
, base::Bind(
212 callback
, error_to_return
, base::FilePath()));
216 // Otherwise, just wrap the return.
217 callback_to_use
= base::Bind(&RenameErrorCallback
, callback
,
221 DownloadFileImpl::RenameAndAnnotate(full_path
, callback_to_use
);
224 bool DownloadFileWithErrors::OverwriteError(
225 TestFileErrorInjector::FileOperationCode code
,
226 DownloadInterruptReason
* output_error
) {
227 int counter
= operation_counter_
[code
]++;
229 if (code
!= error_info_
.code
)
232 if (counter
!= error_info_
.operation_instance
)
235 *output_error
= error_info_
.error
;
239 DownloadInterruptReason
DownloadFileWithErrors::ShouldReturnError(
240 TestFileErrorInjector::FileOperationCode code
,
241 DownloadInterruptReason original_error
) {
242 DownloadInterruptReason output_error
= original_error
;
243 OverwriteError(code
, &output_error
);
249 // A factory for constructing DownloadFiles that inject errors.
250 class DownloadFileWithErrorsFactory
: public DownloadFileFactory
{
252 DownloadFileWithErrorsFactory(
253 const DownloadFileWithErrors::ConstructionCallback
& ctor_callback
,
254 const DownloadFileWithErrors::DestructionCallback
& dtor_callback
);
255 ~DownloadFileWithErrorsFactory() override
;
257 // DownloadFileFactory interface.
258 DownloadFile
* CreateFile(
259 scoped_ptr
<DownloadSaveInfo
> save_info
,
260 const base::FilePath
& default_download_directory
,
262 const GURL
& referrer_url
,
264 scoped_ptr
<ByteStreamReader
> stream
,
265 const net::BoundNetLog
& bound_net_log
,
266 base::WeakPtr
<DownloadDestinationObserver
> observer
) override
;
269 const TestFileErrorInjector::FileErrorInfo
& error_info
);
274 // Our injected error list, mapped by URL. One per file.
275 TestFileErrorInjector::ErrorMap injected_errors_
;
277 // Callback for creation and destruction.
278 DownloadFileWithErrors::ConstructionCallback construction_callback_
;
279 DownloadFileWithErrors::DestructionCallback destruction_callback_
;
282 DownloadFileWithErrorsFactory::DownloadFileWithErrorsFactory(
283 const DownloadFileWithErrors::ConstructionCallback
& ctor_callback
,
284 const DownloadFileWithErrors::DestructionCallback
& dtor_callback
)
285 : construction_callback_(ctor_callback
),
286 destruction_callback_(dtor_callback
) {
289 DownloadFileWithErrorsFactory::~DownloadFileWithErrorsFactory() {
292 DownloadFile
* DownloadFileWithErrorsFactory::CreateFile(
293 scoped_ptr
<DownloadSaveInfo
> save_info
,
294 const base::FilePath
& default_download_directory
,
296 const GURL
& referrer_url
,
298 scoped_ptr
<ByteStreamReader
> stream
,
299 const net::BoundNetLog
& bound_net_log
,
300 base::WeakPtr
<DownloadDestinationObserver
> observer
) {
301 if (injected_errors_
.find(url
.spec()) == injected_errors_
.end()) {
302 // Have to create entry, because FileErrorInfo is not a POD type.
303 TestFileErrorInjector::FileErrorInfo err_info
= {
305 TestFileErrorInjector::FILE_OPERATION_INITIALIZE
,
307 DOWNLOAD_INTERRUPT_REASON_NONE
309 injected_errors_
[url
.spec()] = err_info
;
312 return new DownloadFileWithErrors(
314 default_download_directory
,
321 injected_errors_
[url
.spec()],
322 construction_callback_
,
323 destruction_callback_
);
326 bool DownloadFileWithErrorsFactory::AddError(
327 const TestFileErrorInjector::FileErrorInfo
& error_info
) {
328 // Creates an empty entry if necessary. Duplicate entries overwrite.
329 injected_errors_
[error_info
.url
] = error_info
;
334 void DownloadFileWithErrorsFactory::ClearErrors() {
335 injected_errors_
.clear();
338 TestFileErrorInjector::TestFileErrorInjector(
339 DownloadManager
* download_manager
)
340 : created_factory_(NULL
),
341 // This code is only used for browser_tests, so a
342 // DownloadManager is always a DownloadManagerImpl.
343 download_manager_(static_cast<DownloadManagerImpl
*>(download_manager
)) {
344 // Record the value of the pointer, for later validation.
346 new DownloadFileWithErrorsFactory(
347 base::Bind(&TestFileErrorInjector::RecordDownloadFileConstruction
,
349 base::Bind(&TestFileErrorInjector::RecordDownloadFileDestruction
,
352 // We will transfer ownership of the factory to the download manager.
353 scoped_ptr
<DownloadFileFactory
> download_file_factory(
356 download_manager_
->SetDownloadFileFactoryForTesting(
357 download_file_factory
.Pass());
360 TestFileErrorInjector::~TestFileErrorInjector() {
363 bool TestFileErrorInjector::AddError(const FileErrorInfo
& error_info
) {
364 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
365 DCHECK_LE(0, error_info
.operation_instance
);
366 DCHECK(injected_errors_
.find(error_info
.url
) == injected_errors_
.end());
368 // Creates an empty entry if necessary.
369 injected_errors_
[error_info
.url
] = error_info
;
374 void TestFileErrorInjector::ClearErrors() {
375 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
376 injected_errors_
.clear();
379 bool TestFileErrorInjector::InjectErrors() {
380 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
384 DCHECK_EQ(static_cast<DownloadFileFactory
*>(created_factory_
),
385 download_manager_
->GetDownloadFileFactoryForTesting());
387 created_factory_
->ClearErrors();
389 for (ErrorMap::const_iterator it
= injected_errors_
.begin();
390 it
!= injected_errors_
.end(); ++it
)
391 created_factory_
->AddError(it
->second
);
396 size_t TestFileErrorInjector::CurrentFileCount() const {
397 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
398 return files_
.size();
401 size_t TestFileErrorInjector::TotalFileCount() const {
402 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
403 return found_files_
.size();
407 bool TestFileErrorInjector::HadFile(const GURL
& url
) const {
408 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
410 return (found_files_
.find(url
) != found_files_
.end());
413 void TestFileErrorInjector::ClearFoundFiles() {
414 found_files_
.clear();
417 void TestFileErrorInjector::DownloadFileCreated(GURL url
) {
418 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
419 DCHECK(files_
.find(url
) == files_
.end());
422 found_files_
.insert(url
);
425 void TestFileErrorInjector::DestroyingDownloadFile(GURL url
) {
426 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
427 DCHECK(files_
.find(url
) != files_
.end());
432 void TestFileErrorInjector::RecordDownloadFileConstruction(const GURL
& url
) {
433 BrowserThread::PostTask(
436 base::Bind(&TestFileErrorInjector::DownloadFileCreated
, this, url
));
439 void TestFileErrorInjector::RecordDownloadFileDestruction(const GURL
& url
) {
440 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
441 base::Bind(&TestFileErrorInjector::DestroyingDownloadFile
, this, url
));
445 scoped_refptr
<TestFileErrorInjector
> TestFileErrorInjector::Create(
446 DownloadManager
* download_manager
) {
447 static bool visited
= false;
448 DCHECK(!visited
); // Only allowed to be called once.
451 scoped_refptr
<TestFileErrorInjector
> single_injector(
452 new TestFileErrorInjector(download_manager
));
454 return single_injector
;
458 std::string
TestFileErrorInjector::DebugString(FileOperationCode code
) {
460 case FILE_OPERATION_INITIALIZE
:
462 case FILE_OPERATION_WRITE
:
464 case FILE_OPERATION_RENAME_UNIQUIFY
:
465 return "RENAME_UNIQUIFY";
466 case FILE_OPERATION_RENAME_ANNOTATE
:
467 return "RENAME_ANNOTATE";
475 } // namespace content