Update To 11.40.268.0
[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 "url/gurl.h"
18
19 namespace content {
20 class ByteStreamReader;
21
22 namespace {
23
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;
29
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);
42
43   ~DownloadFileWithErrors() override;
44
45   void Initialize(const InitializeCallback& callback) override;
46
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;
54
55  private:
56   // Error generating helper.
57   DownloadInterruptReason ShouldReturnError(
58       TestFileErrorInjector::FileOperationCode code,
59       DownloadInterruptReason original_error);
60
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);
70
71   // Source URL for the file being downloaded.
72   GURL source_url_;
73
74   // Our injected error.  Only one per file.
75   TestFileErrorInjector::FileErrorInfo error_info_;
76
77   // Count per operation.  0-based.
78   std::map<TestFileErrorInjector::FileOperationCode, int> operation_counter_;
79
80   // Callback for destruction.
81   DestructionCallback destruction_callback_;
82 };
83
84 static void InitializeErrorCallback(
85     const DownloadFile::InitializeCallback original_callback,
86     DownloadInterruptReason overwrite_error,
87     DownloadInterruptReason original_error) {
88   original_callback.Run(overwrite_error);
89 }
90
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());
100 }
101
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_));
130 }
131
132 DownloadFileWithErrors::~DownloadFileWithErrors() {
133   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
134   destruction_callback_.Run(source_url_);
135 }
136
137 void DownloadFileWithErrors::Initialize(
138     const InitializeCallback& callback) {
139   DownloadInterruptReason error_to_return = DOWNLOAD_INTERRUPT_REASON_NONE;
140   InitializeCallback callback_to_use = callback;
141
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;
153     }
154
155     // Otherwise, just wrap the return.
156     callback_to_use = base::Bind(&InitializeErrorCallback, callback,
157                                  error_to_return);
158   }
159
160   DownloadFileImpl::Initialize(callback_to_use);
161 }
162
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));
168 }
169
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;
175
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;
187     }
188
189     // Otherwise, just wrap the return.
190     callback_to_use = base::Bind(&RenameErrorCallback, callback,
191                                  error_to_return);
192   }
193
194   DownloadFileImpl::RenameAndUniquify(full_path, callback_to_use);
195 }
196
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;
202
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;
214     }
215
216     // Otherwise, just wrap the return.
217     callback_to_use = base::Bind(&RenameErrorCallback, callback,
218                                  error_to_return);
219   }
220
221   DownloadFileImpl::RenameAndAnnotate(full_path, callback_to_use);
222 }
223
224 bool DownloadFileWithErrors::OverwriteError(
225     TestFileErrorInjector::FileOperationCode code,
226     DownloadInterruptReason* output_error) {
227   int counter = operation_counter_[code]++;
228
229   if (code != error_info_.code)
230     return false;
231
232   if (counter != error_info_.operation_instance)
233     return false;
234
235   *output_error = error_info_.error;
236   return true;
237 }
238
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;
245 }
246
247 }  // namespace
248
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;
256
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;
267
268   bool AddError(
269       const TestFileErrorInjector::FileErrorInfo& error_info);
270
271   void ClearErrors();
272
273  private:
274   // Our injected error list, mapped by URL.  One per file.
275    TestFileErrorInjector::ErrorMap injected_errors_;
276
277   // Callback for creation and destruction.
278   DownloadFileWithErrors::ConstructionCallback construction_callback_;
279   DownloadFileWithErrors::DestructionCallback destruction_callback_;
280 };
281
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) {
287 }
288
289 DownloadFileWithErrorsFactory::~DownloadFileWithErrorsFactory() {
290 }
291
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,
306       -1,
307       DOWNLOAD_INTERRUPT_REASON_NONE
308     };
309     injected_errors_[url.spec()] = err_info;
310   }
311
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_);
324 }
325
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;
330
331   return true;
332 }
333
334 void DownloadFileWithErrorsFactory::ClearErrors() {
335   injected_errors_.clear();
336 }
337
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));
351
352   // We will transfer ownership of the factory to the download manager.
353   scoped_ptr<DownloadFileFactory> download_file_factory(
354       created_factory_);
355
356   download_manager_->SetDownloadFileFactoryForTesting(
357       download_file_factory.Pass());
358 }
359
360 TestFileErrorInjector::~TestFileErrorInjector() {
361 }
362
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());
367
368   // Creates an empty entry if necessary.
369   injected_errors_[error_info.url] = error_info;
370
371   return true;
372 }
373
374 void TestFileErrorInjector::ClearErrors() {
375   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
376   injected_errors_.clear();
377 }
378
379 bool TestFileErrorInjector::InjectErrors() {
380   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
381
382   ClearFoundFiles();
383
384   DCHECK_EQ(static_cast<DownloadFileFactory*>(created_factory_),
385             download_manager_->GetDownloadFileFactoryForTesting());
386
387   created_factory_->ClearErrors();
388
389   for (ErrorMap::const_iterator it = injected_errors_.begin();
390        it != injected_errors_.end(); ++it)
391     created_factory_->AddError(it->second);
392
393   return true;
394 }
395
396 size_t TestFileErrorInjector::CurrentFileCount() const {
397   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
398   return files_.size();
399 }
400
401 size_t TestFileErrorInjector::TotalFileCount() const {
402   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
403   return found_files_.size();
404 }
405
406
407 bool TestFileErrorInjector::HadFile(const GURL& url) const {
408   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
409
410   return (found_files_.find(url) != found_files_.end());
411 }
412
413 void TestFileErrorInjector::ClearFoundFiles() {
414   found_files_.clear();
415 }
416
417 void TestFileErrorInjector::DownloadFileCreated(GURL url) {
418   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
419   DCHECK(files_.find(url) == files_.end());
420
421   files_.insert(url);
422   found_files_.insert(url);
423 }
424
425 void TestFileErrorInjector::DestroyingDownloadFile(GURL url) {
426   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
427   DCHECK(files_.find(url) != files_.end());
428
429   files_.erase(url);
430 }
431
432 void TestFileErrorInjector::RecordDownloadFileConstruction(const GURL& url) {
433   BrowserThread::PostTask(
434       BrowserThread::UI,
435       FROM_HERE,
436       base::Bind(&TestFileErrorInjector::DownloadFileCreated, this, url));
437 }
438
439 void TestFileErrorInjector::RecordDownloadFileDestruction(const GURL& url) {
440   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
441       base::Bind(&TestFileErrorInjector::DestroyingDownloadFile, this, url));
442 }
443
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;
450
451   scoped_refptr<TestFileErrorInjector> single_injector(
452       new TestFileErrorInjector(download_manager));
453
454   return single_injector;
455 }
456
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;
470   }
471
472   return "Unknown";
473 }
474
475 }  // namespace content