IndexedDBFactory now ForceCloses databases.
[chromium-blink-merge.git] / content / public / test / test_file_error_injector.cc
blob04e9acc3deda5477eefbe22890550805f85199a7
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 "content/public/browser/power_save_blocker.h"
18 #include "url/gurl.h"
20 namespace content {
21 class ByteStreamReader;
23 namespace {
25 // A class that performs file operations and injects errors.
26 class DownloadFileWithErrors: public DownloadFileImpl {
27 public:
28 typedef base::Callback<void(const GURL& url)> ConstructionCallback;
29 typedef base::Callback<void(const GURL& url)> DestructionCallback;
31 DownloadFileWithErrors(
32 scoped_ptr<DownloadSaveInfo> save_info,
33 const base::FilePath& default_download_directory,
34 const GURL& url,
35 const GURL& referrer_url,
36 bool calculate_hash,
37 scoped_ptr<ByteStreamReader> stream,
38 const net::BoundNetLog& bound_net_log,
39 scoped_ptr<PowerSaveBlocker> power_save_blocker,
40 base::WeakPtr<DownloadDestinationObserver> observer,
41 const TestFileErrorInjector::FileErrorInfo& error_info,
42 const ConstructionCallback& ctor_callback,
43 const DestructionCallback& dtor_callback);
45 virtual ~DownloadFileWithErrors();
47 virtual void Initialize(const InitializeCallback& callback) OVERRIDE;
49 // DownloadFile interface.
50 virtual DownloadInterruptReason AppendDataToFile(
51 const char* data, size_t data_len) OVERRIDE;
52 virtual void RenameAndUniquify(
53 const base::FilePath& full_path,
54 const RenameCompletionCallback& callback) OVERRIDE;
55 virtual void RenameAndAnnotate(
56 const base::FilePath& full_path,
57 const RenameCompletionCallback& callback) OVERRIDE;
59 private:
60 // Error generating helper.
61 DownloadInterruptReason ShouldReturnError(
62 TestFileErrorInjector::FileOperationCode code,
63 DownloadInterruptReason original_error);
65 // Determine whether to overwrite an operation with the given code
66 // with a substitute error; if returns true, |*original_error| is
67 // written with the error to use for overwriting.
68 // NOTE: This routine changes state; specifically, it increases the
69 // operations counts for the specified code. It should only be called
70 // once per operation.
71 bool OverwriteError(
72 TestFileErrorInjector::FileOperationCode code,
73 DownloadInterruptReason* output_error);
75 // Source URL for the file being downloaded.
76 GURL source_url_;
78 // Our injected error. Only one per file.
79 TestFileErrorInjector::FileErrorInfo error_info_;
81 // Count per operation. 0-based.
82 std::map<TestFileErrorInjector::FileOperationCode, int> operation_counter_;
84 // Callback for destruction.
85 DestructionCallback destruction_callback_;
88 static void InitializeErrorCallback(
89 const DownloadFile::InitializeCallback original_callback,
90 DownloadInterruptReason overwrite_error,
91 DownloadInterruptReason original_error) {
92 original_callback.Run(overwrite_error);
95 static void RenameErrorCallback(
96 const DownloadFile::RenameCompletionCallback original_callback,
97 DownloadInterruptReason overwrite_error,
98 DownloadInterruptReason original_error,
99 const base::FilePath& path_result) {
100 original_callback.Run(
101 overwrite_error,
102 overwrite_error == DOWNLOAD_INTERRUPT_REASON_NONE ?
103 path_result : base::FilePath());
106 DownloadFileWithErrors::DownloadFileWithErrors(
107 scoped_ptr<DownloadSaveInfo> save_info,
108 const base::FilePath& default_download_directory,
109 const GURL& url,
110 const GURL& referrer_url,
111 bool calculate_hash,
112 scoped_ptr<ByteStreamReader> stream,
113 const net::BoundNetLog& bound_net_log,
114 scoped_ptr<PowerSaveBlocker> power_save_blocker,
115 base::WeakPtr<DownloadDestinationObserver> observer,
116 const TestFileErrorInjector::FileErrorInfo& error_info,
117 const ConstructionCallback& ctor_callback,
118 const DestructionCallback& dtor_callback)
119 : DownloadFileImpl(
120 save_info.Pass(), default_download_directory, url, referrer_url,
121 calculate_hash, stream.Pass(), bound_net_log,
122 power_save_blocker.Pass(), observer),
123 source_url_(url),
124 error_info_(error_info),
125 destruction_callback_(dtor_callback) {
126 // DownloadFiles are created on the UI thread and are destroyed on the FILE
127 // thread. Schedule the ConstructionCallback on the FILE thread so that if a
128 // DownloadItem schedules a DownloadFile to be destroyed and creates another
129 // one (as happens during download resumption), then the DestructionCallback
130 // for the old DownloadFile is run before the ConstructionCallback for the
131 // next DownloadFile.
132 BrowserThread::PostTask(
133 BrowserThread::FILE,
134 FROM_HERE,
135 base::Bind(ctor_callback, source_url_));
138 DownloadFileWithErrors::~DownloadFileWithErrors() {
139 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
140 destruction_callback_.Run(source_url_);
143 void DownloadFileWithErrors::Initialize(
144 const InitializeCallback& callback) {
145 DownloadInterruptReason error_to_return = DOWNLOAD_INTERRUPT_REASON_NONE;
146 InitializeCallback callback_to_use = callback;
148 // Replace callback if the error needs to be overwritten.
149 if (OverwriteError(
150 TestFileErrorInjector::FILE_OPERATION_INITIALIZE,
151 &error_to_return)) {
152 if (DOWNLOAD_INTERRUPT_REASON_NONE != error_to_return) {
153 // Don't execute a, probably successful, Initialize; just
154 // return the error.
155 BrowserThread::PostTask(
156 BrowserThread::UI, FROM_HERE, base::Bind(
157 callback, error_to_return));
158 return;
161 // Otherwise, just wrap the return.
162 callback_to_use = base::Bind(&InitializeErrorCallback, callback,
163 error_to_return);
166 DownloadFileImpl::Initialize(callback_to_use);
169 DownloadInterruptReason DownloadFileWithErrors::AppendDataToFile(
170 const char* data, size_t data_len) {
171 return ShouldReturnError(
172 TestFileErrorInjector::FILE_OPERATION_WRITE,
173 DownloadFileImpl::AppendDataToFile(data, data_len));
176 void DownloadFileWithErrors::RenameAndUniquify(
177 const base::FilePath& full_path,
178 const RenameCompletionCallback& callback) {
179 DownloadInterruptReason error_to_return = DOWNLOAD_INTERRUPT_REASON_NONE;
180 RenameCompletionCallback callback_to_use = callback;
182 // Replace callback if the error needs to be overwritten.
183 if (OverwriteError(
184 TestFileErrorInjector::FILE_OPERATION_RENAME_UNIQUIFY,
185 &error_to_return)) {
186 if (DOWNLOAD_INTERRUPT_REASON_NONE != error_to_return) {
187 // Don't execute a, probably successful, RenameAndUniquify; just
188 // return the error.
189 BrowserThread::PostTask(
190 BrowserThread::UI, FROM_HERE, base::Bind(
191 callback, error_to_return, base::FilePath()));
192 return;
195 // Otherwise, just wrap the return.
196 callback_to_use = base::Bind(&RenameErrorCallback, callback,
197 error_to_return);
200 DownloadFileImpl::RenameAndUniquify(full_path, callback_to_use);
203 void DownloadFileWithErrors::RenameAndAnnotate(
204 const base::FilePath& full_path,
205 const RenameCompletionCallback& callback) {
206 DownloadInterruptReason error_to_return = DOWNLOAD_INTERRUPT_REASON_NONE;
207 RenameCompletionCallback callback_to_use = callback;
209 // Replace callback if the error needs to be overwritten.
210 if (OverwriteError(
211 TestFileErrorInjector::FILE_OPERATION_RENAME_ANNOTATE,
212 &error_to_return)) {
213 if (DOWNLOAD_INTERRUPT_REASON_NONE != error_to_return) {
214 // Don't execute a, probably successful, RenameAndAnnotate; just
215 // return the error.
216 BrowserThread::PostTask(
217 BrowserThread::UI, FROM_HERE, base::Bind(
218 callback, error_to_return, base::FilePath()));
219 return;
222 // Otherwise, just wrap the return.
223 callback_to_use = base::Bind(&RenameErrorCallback, callback,
224 error_to_return);
227 DownloadFileImpl::RenameAndAnnotate(full_path, callback_to_use);
230 bool DownloadFileWithErrors::OverwriteError(
231 TestFileErrorInjector::FileOperationCode code,
232 DownloadInterruptReason* output_error) {
233 int counter = operation_counter_[code]++;
235 if (code != error_info_.code)
236 return false;
238 if (counter != error_info_.operation_instance)
239 return false;
241 *output_error = error_info_.error;
242 return true;
245 DownloadInterruptReason DownloadFileWithErrors::ShouldReturnError(
246 TestFileErrorInjector::FileOperationCode code,
247 DownloadInterruptReason original_error) {
248 DownloadInterruptReason output_error = original_error;
249 OverwriteError(code, &output_error);
250 return output_error;
253 } // namespace
255 // A factory for constructing DownloadFiles that inject errors.
256 class DownloadFileWithErrorsFactory : public DownloadFileFactory {
257 public:
258 DownloadFileWithErrorsFactory(
259 const DownloadFileWithErrors::ConstructionCallback& ctor_callback,
260 const DownloadFileWithErrors::DestructionCallback& dtor_callback);
261 virtual ~DownloadFileWithErrorsFactory();
263 // DownloadFileFactory interface.
264 virtual DownloadFile* CreateFile(
265 scoped_ptr<DownloadSaveInfo> save_info,
266 const base::FilePath& default_download_directory,
267 const GURL& url,
268 const GURL& referrer_url,
269 bool calculate_hash,
270 scoped_ptr<ByteStreamReader> stream,
271 const net::BoundNetLog& bound_net_log,
272 base::WeakPtr<DownloadDestinationObserver> observer) OVERRIDE;
274 bool AddError(
275 const TestFileErrorInjector::FileErrorInfo& error_info);
277 void ClearErrors();
279 private:
280 // Our injected error list, mapped by URL. One per file.
281 TestFileErrorInjector::ErrorMap injected_errors_;
283 // Callback for creation and destruction.
284 DownloadFileWithErrors::ConstructionCallback construction_callback_;
285 DownloadFileWithErrors::DestructionCallback destruction_callback_;
288 DownloadFileWithErrorsFactory::DownloadFileWithErrorsFactory(
289 const DownloadFileWithErrors::ConstructionCallback& ctor_callback,
290 const DownloadFileWithErrors::DestructionCallback& dtor_callback)
291 : construction_callback_(ctor_callback),
292 destruction_callback_(dtor_callback) {
295 DownloadFileWithErrorsFactory::~DownloadFileWithErrorsFactory() {
298 DownloadFile* DownloadFileWithErrorsFactory::CreateFile(
299 scoped_ptr<DownloadSaveInfo> save_info,
300 const base::FilePath& default_download_directory,
301 const GURL& url,
302 const GURL& referrer_url,
303 bool calculate_hash,
304 scoped_ptr<ByteStreamReader> stream,
305 const net::BoundNetLog& bound_net_log,
306 base::WeakPtr<DownloadDestinationObserver> observer) {
307 if (injected_errors_.find(url.spec()) == injected_errors_.end()) {
308 // Have to create entry, because FileErrorInfo is not a POD type.
309 TestFileErrorInjector::FileErrorInfo err_info = {
310 url.spec(),
311 TestFileErrorInjector::FILE_OPERATION_INITIALIZE,
313 DOWNLOAD_INTERRUPT_REASON_NONE
315 injected_errors_[url.spec()] = err_info;
318 scoped_ptr<PowerSaveBlocker> psb(
319 PowerSaveBlocker::Create(
320 PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension,
321 "Download in progress"));
323 return new DownloadFileWithErrors(
324 save_info.Pass(),
325 default_download_directory,
326 url,
327 referrer_url,
328 calculate_hash,
329 stream.Pass(),
330 bound_net_log,
331 psb.Pass(),
332 observer,
333 injected_errors_[url.spec()],
334 construction_callback_,
335 destruction_callback_);
338 bool DownloadFileWithErrorsFactory::AddError(
339 const TestFileErrorInjector::FileErrorInfo& error_info) {
340 // Creates an empty entry if necessary. Duplicate entries overwrite.
341 injected_errors_[error_info.url] = error_info;
343 return true;
346 void DownloadFileWithErrorsFactory::ClearErrors() {
347 injected_errors_.clear();
350 TestFileErrorInjector::TestFileErrorInjector(
351 DownloadManager* download_manager)
352 : created_factory_(NULL),
353 // This code is only used for browser_tests, so a
354 // DownloadManager is always a DownloadManagerImpl.
355 download_manager_(static_cast<DownloadManagerImpl*>(download_manager)) {
356 // Record the value of the pointer, for later validation.
357 created_factory_ =
358 new DownloadFileWithErrorsFactory(
359 base::Bind(&TestFileErrorInjector::RecordDownloadFileConstruction,
360 this),
361 base::Bind(&TestFileErrorInjector::RecordDownloadFileDestruction,
362 this));
364 // We will transfer ownership of the factory to the download manager.
365 scoped_ptr<DownloadFileFactory> download_file_factory(
366 created_factory_);
368 download_manager_->SetDownloadFileFactoryForTesting(
369 download_file_factory.Pass());
372 TestFileErrorInjector::~TestFileErrorInjector() {
375 bool TestFileErrorInjector::AddError(const FileErrorInfo& error_info) {
376 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
377 DCHECK_LE(0, error_info.operation_instance);
378 DCHECK(injected_errors_.find(error_info.url) == injected_errors_.end());
380 // Creates an empty entry if necessary.
381 injected_errors_[error_info.url] = error_info;
383 return true;
386 void TestFileErrorInjector::ClearErrors() {
387 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
388 injected_errors_.clear();
391 bool TestFileErrorInjector::InjectErrors() {
392 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
394 ClearFoundFiles();
396 DCHECK_EQ(static_cast<DownloadFileFactory*>(created_factory_),
397 download_manager_->GetDownloadFileFactoryForTesting());
399 created_factory_->ClearErrors();
401 for (ErrorMap::const_iterator it = injected_errors_.begin();
402 it != injected_errors_.end(); ++it)
403 created_factory_->AddError(it->second);
405 return true;
408 size_t TestFileErrorInjector::CurrentFileCount() const {
409 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
410 return files_.size();
413 size_t TestFileErrorInjector::TotalFileCount() const {
414 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
415 return found_files_.size();
419 bool TestFileErrorInjector::HadFile(const GURL& url) const {
420 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
422 return (found_files_.find(url) != found_files_.end());
425 void TestFileErrorInjector::ClearFoundFiles() {
426 found_files_.clear();
429 void TestFileErrorInjector::DownloadFileCreated(GURL url) {
430 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
431 DCHECK(files_.find(url) == files_.end());
433 files_.insert(url);
434 found_files_.insert(url);
437 void TestFileErrorInjector::DestroyingDownloadFile(GURL url) {
438 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
439 DCHECK(files_.find(url) != files_.end());
441 files_.erase(url);
444 void TestFileErrorInjector::RecordDownloadFileConstruction(const GURL& url) {
445 BrowserThread::PostTask(
446 BrowserThread::UI,
447 FROM_HERE,
448 base::Bind(&TestFileErrorInjector::DownloadFileCreated, this, url));
451 void TestFileErrorInjector::RecordDownloadFileDestruction(const GURL& url) {
452 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
453 base::Bind(&TestFileErrorInjector::DestroyingDownloadFile, this, url));
456 // static
457 scoped_refptr<TestFileErrorInjector> TestFileErrorInjector::Create(
458 DownloadManager* download_manager) {
459 static bool visited = false;
460 DCHECK(!visited); // Only allowed to be called once.
461 visited = true;
463 scoped_refptr<TestFileErrorInjector> single_injector(
464 new TestFileErrorInjector(download_manager));
466 return single_injector;
469 // static
470 std::string TestFileErrorInjector::DebugString(FileOperationCode code) {
471 switch (code) {
472 case FILE_OPERATION_INITIALIZE:
473 return "INITIALIZE";
474 case FILE_OPERATION_WRITE:
475 return "WRITE";
476 case FILE_OPERATION_RENAME_UNIQUIFY:
477 return "RENAME_UNIQUIFY";
478 case FILE_OPERATION_RENAME_ANNOTATE:
479 return "RENAME_ANNOTATE";
480 default:
481 break;
484 return "Unknown";
487 } // namespace content