- add sources.
[platform/framework/web/crosswalk.git] / src / content / public / test / test_file_error_injector.cc
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/public/test/test_file_error_injector.h"
6
7 #include <vector>
8
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"
19
20 namespace content {
21 class ByteStreamReader;
22
23 namespace {
24
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;
30
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);
44
45   virtual ~DownloadFileWithErrors();
46
47   virtual void Initialize(const InitializeCallback& callback) OVERRIDE;
48
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;
58
59  private:
60   // Error generating helper.
61   DownloadInterruptReason ShouldReturnError(
62       TestFileErrorInjector::FileOperationCode code,
63       DownloadInterruptReason original_error);
64
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);
74
75   // Source URL for the file being downloaded.
76   GURL source_url_;
77
78   // Our injected error.  Only one per file.
79   TestFileErrorInjector::FileErrorInfo error_info_;
80
81   // Count per operation.  0-based.
82   std::map<TestFileErrorInjector::FileOperationCode, int> operation_counter_;
83
84   // Callback for destruction.
85   DestructionCallback destruction_callback_;
86 };
87
88 static void InitializeErrorCallback(
89     const DownloadFile::InitializeCallback original_callback,
90     DownloadInterruptReason overwrite_error,
91     DownloadInterruptReason original_error) {
92   original_callback.Run(overwrite_error);
93 }
94
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());
104 }
105
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_));
136 }
137
138 DownloadFileWithErrors::~DownloadFileWithErrors() {
139   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
140   destruction_callback_.Run(source_url_);
141 }
142
143 void DownloadFileWithErrors::Initialize(
144     const InitializeCallback& callback) {
145   DownloadInterruptReason error_to_return = DOWNLOAD_INTERRUPT_REASON_NONE;
146   InitializeCallback callback_to_use = callback;
147
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;
159     }
160
161     // Otherwise, just wrap the return.
162     callback_to_use = base::Bind(&InitializeErrorCallback, callback,
163                                  error_to_return);
164   }
165
166   DownloadFileImpl::Initialize(callback_to_use);
167 }
168
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));
174 }
175
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;
181
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;
193     }
194
195     // Otherwise, just wrap the return.
196     callback_to_use = base::Bind(&RenameErrorCallback, callback,
197                                  error_to_return);
198   }
199
200   DownloadFileImpl::RenameAndUniquify(full_path, callback_to_use);
201 }
202
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;
208
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;
220     }
221
222     // Otherwise, just wrap the return.
223     callback_to_use = base::Bind(&RenameErrorCallback, callback,
224                                  error_to_return);
225   }
226
227   DownloadFileImpl::RenameAndAnnotate(full_path, callback_to_use);
228 }
229
230 bool DownloadFileWithErrors::OverwriteError(
231     TestFileErrorInjector::FileOperationCode code,
232     DownloadInterruptReason* output_error) {
233   int counter = operation_counter_[code]++;
234
235   if (code != error_info_.code)
236     return false;
237
238   if (counter != error_info_.operation_instance)
239     return false;
240
241   *output_error = error_info_.error;
242   return true;
243 }
244
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;
251 }
252
253 }  // namespace
254
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();
262
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;
273
274   bool AddError(
275       const TestFileErrorInjector::FileErrorInfo& error_info);
276
277   void ClearErrors();
278
279  private:
280   // Our injected error list, mapped by URL.  One per file.
281    TestFileErrorInjector::ErrorMap injected_errors_;
282
283   // Callback for creation and destruction.
284   DownloadFileWithErrors::ConstructionCallback construction_callback_;
285   DownloadFileWithErrors::DestructionCallback destruction_callback_;
286 };
287
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) {
293 }
294
295 DownloadFileWithErrorsFactory::~DownloadFileWithErrorsFactory() {
296 }
297
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,
312       -1,
313       DOWNLOAD_INTERRUPT_REASON_NONE
314     };
315     injected_errors_[url.spec()] = err_info;
316   }
317
318   scoped_ptr<PowerSaveBlocker> psb(
319       PowerSaveBlocker::Create(
320           PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension,
321           "Download in progress"));
322
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_);
336 }
337
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;
342
343   return true;
344 }
345
346 void DownloadFileWithErrorsFactory::ClearErrors() {
347   injected_errors_.clear();
348 }
349
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));
363
364   // We will transfer ownership of the factory to the download manager.
365   scoped_ptr<DownloadFileFactory> download_file_factory(
366       created_factory_);
367
368   download_manager_->SetDownloadFileFactoryForTesting(
369       download_file_factory.Pass());
370 }
371
372 TestFileErrorInjector::~TestFileErrorInjector() {
373 }
374
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());
379
380   // Creates an empty entry if necessary.
381   injected_errors_[error_info.url] = error_info;
382
383   return true;
384 }
385
386 void TestFileErrorInjector::ClearErrors() {
387   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
388   injected_errors_.clear();
389 }
390
391 bool TestFileErrorInjector::InjectErrors() {
392   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
393
394   ClearFoundFiles();
395
396   DCHECK_EQ(static_cast<DownloadFileFactory*>(created_factory_),
397             download_manager_->GetDownloadFileFactoryForTesting());
398
399   created_factory_->ClearErrors();
400
401   for (ErrorMap::const_iterator it = injected_errors_.begin();
402        it != injected_errors_.end(); ++it)
403     created_factory_->AddError(it->second);
404
405   return true;
406 }
407
408 size_t TestFileErrorInjector::CurrentFileCount() const {
409   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
410   return files_.size();
411 }
412
413 size_t TestFileErrorInjector::TotalFileCount() const {
414   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
415   return found_files_.size();
416 }
417
418
419 bool TestFileErrorInjector::HadFile(const GURL& url) const {
420   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
421
422   return (found_files_.find(url) != found_files_.end());
423 }
424
425 void TestFileErrorInjector::ClearFoundFiles() {
426   found_files_.clear();
427 }
428
429 void TestFileErrorInjector::DownloadFileCreated(GURL url) {
430   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
431   DCHECK(files_.find(url) == files_.end());
432
433   files_.insert(url);
434   found_files_.insert(url);
435 }
436
437 void TestFileErrorInjector::DestroyingDownloadFile(GURL url) {
438   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
439   DCHECK(files_.find(url) != files_.end());
440
441   files_.erase(url);
442 }
443
444 void TestFileErrorInjector::RecordDownloadFileConstruction(const GURL& url) {
445   BrowserThread::PostTask(
446       BrowserThread::UI,
447       FROM_HERE,
448       base::Bind(&TestFileErrorInjector::DownloadFileCreated, this, url));
449 }
450
451 void TestFileErrorInjector::RecordDownloadFileDestruction(const GURL& url) {
452   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
453       base::Bind(&TestFileErrorInjector::DestroyingDownloadFile, this, url));
454 }
455
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;
462
463   scoped_refptr<TestFileErrorInjector> single_injector(
464       new TestFileErrorInjector(download_manager));
465
466   return single_injector;
467 }
468
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;
482   }
483
484   return "Unknown";
485 }
486
487 }  // namespace content