Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / public / test / test_file_error_injector.cc
bloba6421b16270dbd7c0f337ce95dc6b8fcac2b20ce
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"
7 #include <vector>
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"
17 #include "url/gurl.h"
19 namespace content {
20 class ByteStreamReader;
22 namespace {
24 // A class that performs file operations and injects errors.
25 class DownloadFileWithErrors: public DownloadFileImpl {
26 public:
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,
33 const GURL& url,
34 const GURL& referrer_url,
35 bool calculate_hash,
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;
55 private:
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.
67 bool OverwriteError(
68 TestFileErrorInjector::FileOperationCode code,
69 DownloadInterruptReason* output_error);
71 // Source URL for the file being downloaded.
72 GURL source_url_;
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(
97 overwrite_error,
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,
105 const GURL& url,
106 const GURL& referrer_url,
107 bool calculate_hash,
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)
114 : DownloadFileImpl(
115 save_info.Pass(), default_download_directory, url, referrer_url,
116 calculate_hash, stream.Pass(), bound_net_log, observer),
117 source_url_(url),
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(
127 BrowserThread::FILE,
128 FROM_HERE,
129 base::Bind(ctor_callback, source_url_));
132 DownloadFileWithErrors::~DownloadFileWithErrors() {
133 DCHECK_CURRENTLY_ON(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.
143 if (OverwriteError(
144 TestFileErrorInjector::FILE_OPERATION_INITIALIZE,
145 &error_to_return)) {
146 if (DOWNLOAD_INTERRUPT_REASON_NONE != error_to_return) {
147 // Don't execute a, probably successful, Initialize; just
148 // return the error.
149 BrowserThread::PostTask(
150 BrowserThread::UI, FROM_HERE, base::Bind(
151 callback, error_to_return));
152 return;
155 // Otherwise, just wrap the return.
156 callback_to_use = base::Bind(&InitializeErrorCallback, callback,
157 error_to_return);
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.
177 if (OverwriteError(
178 TestFileErrorInjector::FILE_OPERATION_RENAME_UNIQUIFY,
179 &error_to_return)) {
180 if (DOWNLOAD_INTERRUPT_REASON_NONE != error_to_return) {
181 // Don't execute a, probably successful, RenameAndUniquify; just
182 // return the error.
183 BrowserThread::PostTask(
184 BrowserThread::UI, FROM_HERE, base::Bind(
185 callback, error_to_return, base::FilePath()));
186 return;
189 // Otherwise, just wrap the return.
190 callback_to_use = base::Bind(&RenameErrorCallback, callback,
191 error_to_return);
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.
204 if (OverwriteError(
205 TestFileErrorInjector::FILE_OPERATION_RENAME_ANNOTATE,
206 &error_to_return)) {
207 if (DOWNLOAD_INTERRUPT_REASON_NONE != error_to_return) {
208 // Don't execute a, probably successful, RenameAndAnnotate; just
209 // return the error.
210 BrowserThread::PostTask(
211 BrowserThread::UI, FROM_HERE, base::Bind(
212 callback, error_to_return, base::FilePath()));
213 return;
216 // Otherwise, just wrap the return.
217 callback_to_use = base::Bind(&RenameErrorCallback, callback,
218 error_to_return);
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)
230 return false;
232 if (counter != error_info_.operation_instance)
233 return false;
235 *output_error = error_info_.error;
236 return true;
239 DownloadInterruptReason DownloadFileWithErrors::ShouldReturnError(
240 TestFileErrorInjector::FileOperationCode code,
241 DownloadInterruptReason original_error) {
242 DownloadInterruptReason output_error = original_error;
243 OverwriteError(code, &output_error);
244 return output_error;
247 } // namespace
249 // A factory for constructing DownloadFiles that inject errors.
250 class DownloadFileWithErrorsFactory : public DownloadFileFactory {
251 public:
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,
261 const GURL& url,
262 const GURL& referrer_url,
263 bool calculate_hash,
264 scoped_ptr<ByteStreamReader> stream,
265 const net::BoundNetLog& bound_net_log,
266 base::WeakPtr<DownloadDestinationObserver> observer) override;
268 bool AddError(
269 const TestFileErrorInjector::FileErrorInfo& error_info);
271 void ClearErrors();
273 private:
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,
295 const GURL& url,
296 const GURL& referrer_url,
297 bool calculate_hash,
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 = {
304 url.spec(),
305 TestFileErrorInjector::FILE_OPERATION_INITIALIZE,
307 DOWNLOAD_INTERRUPT_REASON_NONE
309 injected_errors_[url.spec()] = err_info;
312 return new DownloadFileWithErrors(
313 save_info.Pass(),
314 default_download_directory,
315 url,
316 referrer_url,
317 calculate_hash,
318 stream.Pass(),
319 bound_net_log,
320 observer,
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;
331 return true;
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.
345 created_factory_ =
346 new DownloadFileWithErrorsFactory(
347 base::Bind(&TestFileErrorInjector::RecordDownloadFileConstruction,
348 this),
349 base::Bind(&TestFileErrorInjector::RecordDownloadFileDestruction,
350 this));
352 // We will transfer ownership of the factory to the download manager.
353 scoped_ptr<DownloadFileFactory> download_file_factory(
354 created_factory_);
356 download_manager_->SetDownloadFileFactoryForTesting(
357 download_file_factory.Pass());
360 TestFileErrorInjector::~TestFileErrorInjector() {
363 bool TestFileErrorInjector::AddError(const FileErrorInfo& error_info) {
364 DCHECK_CURRENTLY_ON(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;
371 return true;
374 void TestFileErrorInjector::ClearErrors() {
375 DCHECK_CURRENTLY_ON(BrowserThread::UI);
376 injected_errors_.clear();
379 bool TestFileErrorInjector::InjectErrors() {
380 DCHECK_CURRENTLY_ON(BrowserThread::UI);
382 ClearFoundFiles();
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);
393 return true;
396 size_t TestFileErrorInjector::CurrentFileCount() const {
397 DCHECK_CURRENTLY_ON(BrowserThread::UI);
398 return files_.size();
401 size_t TestFileErrorInjector::TotalFileCount() const {
402 DCHECK_CURRENTLY_ON(BrowserThread::UI);
403 return found_files_.size();
407 bool TestFileErrorInjector::HadFile(const GURL& url) const {
408 DCHECK_CURRENTLY_ON(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_CURRENTLY_ON(BrowserThread::UI);
419 DCHECK(files_.find(url) == files_.end());
421 files_.insert(url);
422 found_files_.insert(url);
425 void TestFileErrorInjector::DestroyingDownloadFile(GURL url) {
426 DCHECK_CURRENTLY_ON(BrowserThread::UI);
427 DCHECK(files_.find(url) != files_.end());
429 files_.erase(url);
432 void TestFileErrorInjector::RecordDownloadFileConstruction(const GURL& url) {
433 BrowserThread::PostTask(
434 BrowserThread::UI,
435 FROM_HERE,
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));
444 // static
445 scoped_refptr<TestFileErrorInjector> TestFileErrorInjector::Create(
446 DownloadManager* download_manager) {
447 static bool visited = false;
448 DCHECK(!visited); // Only allowed to be called once.
449 visited = true;
451 scoped_refptr<TestFileErrorInjector> single_injector(
452 new TestFileErrorInjector(download_manager));
454 return single_injector;
457 // static
458 std::string TestFileErrorInjector::DebugString(FileOperationCode code) {
459 switch (code) {
460 case FILE_OPERATION_INITIALIZE:
461 return "INITIALIZE";
462 case FILE_OPERATION_WRITE:
463 return "WRITE";
464 case FILE_OPERATION_RENAME_UNIQUIFY:
465 return "RENAME_UNIQUIFY";
466 case FILE_OPERATION_RENAME_ANNOTATE:
467 return "RENAME_ANNOTATE";
468 default:
469 break;
472 return "Unknown";
475 } // namespace content