Index: content/test/test_file_error_injector.cc |
diff --git a/content/test/test_file_error_injector.cc b/content/test/test_file_error_injector.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d4bbfa2fab576c99a83e2225c36bc251a34c85e1 |
--- /dev/null |
+++ b/content/test/test_file_error_injector.cc |
@@ -0,0 +1,435 @@ |
+// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "content/test/test_file_error_injector.h" |
+ |
+#include <map> |
+#include <vector> |
+ |
+#include "base/compiler_specific.h" |
+#include "base/logging.h" |
+#include "content/public/browser/browser_thread.h" |
+#include "content/browser/download/download_create_info.h" |
+#include "content/browser/download/download_file_impl.h" |
+#include "content/browser/download/download_file_manager.h" |
+#include "content/browser/renderer_host/resource_dispatcher_host.h" |
+#include "googleurl/src/gurl.h" |
+ |
+namespace { |
+ |
+class DownloadFileWithErrors; |
+class DownloadFileWithErrorsFactory; |
+ |
+typedef std::map<std::string, content::TestFileErrorInjector::FileErrorInfo> |
+ ErrorMap; |
+ |
+DownloadFileManager* GetDownloadFileManager() { |
+ ResourceDispatcherHost* rdh = ResourceDispatcherHost::Get(); |
+ DCHECK(rdh != NULL); |
+ return rdh->download_file_manager(); |
+} |
+ |
+class TestFileErrorInjectorImpl : public content::TestFileErrorInjector { |
+ public: |
+ typedef base::Callback<void(GURL url, content::DownloadId id)> |
+ ConstructionCallback; |
+ typedef base::Callback<void(GURL url)> DestructionCallback; |
+ |
+ TestFileErrorInjectorImpl(); |
+ |
+ // content::TestFileErrorInjector interface. |
+ virtual bool AddError(const FileErrorInfo& error_info) OVERRIDE; |
+ virtual void ClearErrors() OVERRIDE; |
+ virtual bool InjectErrors() OVERRIDE; |
+ virtual size_t CurrentFileCount() const OVERRIDE; |
+ virtual size_t TotalFileCount() const OVERRIDE; |
+ virtual bool HadFile(const GURL& url) const OVERRIDE; |
+ virtual const content::DownloadId GetId(const GURL& url) const OVERRIDE; |
+ virtual void ClearFoundFiles() OVERRIDE; |
+ |
+ private: |
+ typedef std::map<GURL, content::DownloadId> FileMap; |
+ |
+ virtual ~TestFileErrorInjectorImpl(); |
+ |
+ void RecordDownloadFileConstruction(GURL url, content::DownloadId id); |
+ void RecordDownloadFileDestruction(GURL url); |
+ |
+ // Callbacks from the download file, to record lifetimes. |
+ void DownloadFileCreated(GURL url, content::DownloadId id); |
+ void DestroyingDownloadFile(GURL url); |
+ |
+ // Our injected error list, mapped by file index. One per file. |
+ ErrorMap injected_errors_; |
+ |
+ // Keep track of active DownloadFiles. |
+ FileMap files_; |
+ |
+ // Keep track of found DownloadFiles. |
+ FileMap found_files_; |
+ |
+ // We have created a factory. Used for validation. |
+ bool created_factory_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(TestFileErrorInjectorImpl); |
+}; |
+ |
+// A class that performs file operations and injects errors. |
+class DownloadFileWithErrors: public DownloadFileImpl { |
+ public: |
+ DownloadFileWithErrors( |
+ const DownloadCreateInfo* info, |
+ DownloadRequestHandleInterface* request_handle, |
+ content::DownloadManager* download_manager, |
+ bool calculate_hash, |
+ const net::BoundNetLog& bound_net_log, |
+ const content::TestFileErrorInjector::FileErrorInfo& error_info, |
+ const TestFileErrorInjectorImpl::ConstructionCallback& ctor_callback, |
+ const TestFileErrorInjectorImpl::DestructionCallback& dtor_callback); |
+ |
+ ~DownloadFileWithErrors(); |
+ |
+ // DownloadFile interface. |
+ virtual net::Error Initialize() OVERRIDE; |
+ virtual net::Error AppendDataToFile(const char* data, |
+ size_t data_len) OVERRIDE; |
+ virtual net::Error Rename(const FilePath& full_path) OVERRIDE; |
+ |
+ private: |
+ // Error generating helper. |
+ net::Error ShouldReturnError( |
+ content::TestFileErrorInjector::FileOperationCode code, |
+ net::Error original_net_error); |
+ |
+ // Source URL for the file being downloaded. |
+ GURL source_url_; |
+ |
+ // Our injected error. Only one per file. |
+ content::TestFileErrorInjector::FileErrorInfo error_info_; |
+ |
+ // Count per operation. 0-based. |
+ std::map<content::TestFileErrorInjector::FileOperationCode, int> |
+ operation_counter_; |
+ |
+ // Callback for destruction. |
+ TestFileErrorInjectorImpl::DestructionCallback destruction_callback_; |
+}; |
+ |
+// A factory for constructing DownloadFiles that inject errors. |
+class DownloadFileWithErrorsFactory |
+ : public DownloadFileManager::DownloadFileFactory { |
+ public: |
+ |
+ DownloadFileWithErrorsFactory( |
+ const TestFileErrorInjectorImpl::ConstructionCallback& ctor_callback, |
+ const TestFileErrorInjectorImpl::DestructionCallback& dtor_callback); |
+ virtual ~DownloadFileWithErrorsFactory(); |
+ |
+ // DownloadFileFactory interface. |
+ virtual content::DownloadFile* CreateFile( |
+ DownloadCreateInfo* info, |
+ const DownloadRequestHandle& request_handle, |
+ content::DownloadManager* download_manager, |
+ bool calculate_hash, |
+ const net::BoundNetLog& bound_net_log); |
+ |
+ bool AddError( |
+ const content::TestFileErrorInjector::FileErrorInfo& error_info); |
+ |
+ void ClearErrors(); |
+ |
+ private: |
+ // Our injected error list, mapped by file index. One per file. |
+ ErrorMap injected_errors_; |
+ |
+ // Callback for creation and destruction. |
+ TestFileErrorInjectorImpl::ConstructionCallback construction_callback_; |
+ TestFileErrorInjectorImpl::DestructionCallback destruction_callback_; |
+}; |
+ |
+// Implementations. |
+ |
+DownloadFileWithErrors::DownloadFileWithErrors( |
+ const DownloadCreateInfo* info, |
+ DownloadRequestHandleInterface* request_handle, |
+ content::DownloadManager* download_manager, |
+ bool calculate_hash, |
+ const net::BoundNetLog& bound_net_log, |
+ const content::TestFileErrorInjector::FileErrorInfo& error_info, |
+ const TestFileErrorInjectorImpl::ConstructionCallback& ctor_callback, |
+ const TestFileErrorInjectorImpl::DestructionCallback& dtor_callback) |
+ : DownloadFileImpl(info, |
+ request_handle, |
+ download_manager, |
+ calculate_hash, |
+ bound_net_log), |
+ source_url_(info->url()), |
+ error_info_(error_info), |
+ destruction_callback_(dtor_callback) { |
+ ctor_callback.Run(source_url_, info->download_id); |
+} |
+ |
+DownloadFileWithErrors::~DownloadFileWithErrors() { |
+ destruction_callback_.Run(source_url_); |
+} |
+ |
+ |
+net::Error DownloadFileWithErrors::Initialize() { |
+ return ShouldReturnError( |
+ content::TestFileErrorInjector::FILE_OPERATION_INITIALIZE, |
+ DownloadFileImpl::Initialize()); |
+} |
+ |
+net::Error DownloadFileWithErrors::AppendDataToFile(const char* data, |
+ size_t data_len) { |
+ return ShouldReturnError( |
+ content::TestFileErrorInjector::FILE_OPERATION_WRITE, |
+ DownloadFileImpl::AppendDataToFile(data, data_len)); |
+} |
+ |
+net::Error DownloadFileWithErrors::Rename(const FilePath& full_path) { |
+ return ShouldReturnError( |
+ content::TestFileErrorInjector::FILE_OPERATION_RENAME, |
+ DownloadFileImpl::Rename(full_path)); |
+} |
+ |
+net::Error DownloadFileWithErrors::ShouldReturnError( |
+ content::TestFileErrorInjector::FileOperationCode code, |
+ net::Error original_net_error) { |
+ int counter = operation_counter_[code]; |
+ ++operation_counter_[code]; |
+ |
+ if (code != error_info_.code) |
+ return original_net_error; |
+ |
+ if (counter != error_info_.operation_instance) |
+ return original_net_error; |
+ |
+ VLOG(1) << " " << __FUNCTION__ << "()" |
+ << " url = '" << source_url_.spec() << "'" |
+ << " code = " << content::TestFileErrorInjector::DebugString(code) |
+ << " (" << code << ")" |
+ << " counter = " << counter |
+ << " original_error = " << net::ErrorToString(original_net_error) |
+ << " (" << original_net_error << ")" |
+ << " new error = " << net::ErrorToString(error_info_.net_error) |
+ << " (" << error_info_.net_error << ")"; |
+ |
+ return error_info_.net_error; |
+} |
+ |
+DownloadFileWithErrorsFactory::DownloadFileWithErrorsFactory( |
+ const TestFileErrorInjectorImpl::ConstructionCallback& ctor_callback, |
+ const TestFileErrorInjectorImpl::DestructionCallback& dtor_callback) |
+ : construction_callback_(ctor_callback), |
+ destruction_callback_(dtor_callback) { |
+} |
+ |
+DownloadFileWithErrorsFactory::~DownloadFileWithErrorsFactory() { |
+} |
+ |
+content::DownloadFile* DownloadFileWithErrorsFactory::CreateFile( |
+ DownloadCreateInfo* info, |
+ const DownloadRequestHandle& request_handle, |
+ content::DownloadManager* download_manager, |
+ bool calculate_hash, |
+ const net::BoundNetLog& bound_net_log) { |
+ std::string url = info->url().spec(); |
+ |
+ if (injected_errors_.find(url) == injected_errors_.end()) { |
+ // Have to create entry, because FileErrorInfo is not a POD type. |
+ content::TestFileErrorInjector::FileErrorInfo err_info = { |
+ url, |
+ content::TestFileErrorInjector::FILE_OPERATION_INITIALIZE, |
+ -1, |
+ net::OK |
+ }; |
+ injected_errors_[url] = err_info; |
+ } |
+ |
+ return new DownloadFileWithErrors(info, |
+ new DownloadRequestHandle(request_handle), |
+ download_manager, |
+ calculate_hash, |
+ bound_net_log, |
+ injected_errors_[url], |
+ construction_callback_, |
+ destruction_callback_); |
+} |
+ |
+bool DownloadFileWithErrorsFactory::AddError( |
+ const content::TestFileErrorInjector::FileErrorInfo& error_info) { |
+ // Creates an empty entry if necessary. Duplicate entries overwrite. |
+ injected_errors_[error_info.url] = error_info; |
+ |
+ return true; |
+} |
+ |
+void DownloadFileWithErrorsFactory::ClearErrors() { |
+ injected_errors_.clear(); |
+} |
+ |
+TestFileErrorInjectorImpl::TestFileErrorInjectorImpl() |
+ : created_factory_(false) { |
+} |
+ |
+TestFileErrorInjectorImpl::~TestFileErrorInjectorImpl() { |
+} |
+ |
+bool TestFileErrorInjectorImpl::AddError(const FileErrorInfo& error_info) { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ DCHECK_LE(0, error_info.operation_instance); |
+ DCHECK(injected_errors_.find(error_info.url) == injected_errors_.end()); |
+ |
+ // Creates an empty entry if necessary. |
+ injected_errors_[error_info.url] = error_info; |
+ |
+ return true; |
+} |
+ |
+void TestFileErrorInjectorImpl::ClearErrors() { |
+ injected_errors_.clear(); |
+} |
+ |
+bool TestFileErrorInjectorImpl::InjectErrors() { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ |
+ DownloadFileManager* download_file_manager = GetDownloadFileManager(); |
+ DCHECK(download_file_manager); |
+ |
+ ClearFoundFiles(); |
+ |
+ if (!created_factory_) { |
+ scoped_ptr<DownloadFileManager::DownloadFileFactory> download_file_factory( |
+ new DownloadFileWithErrorsFactory( |
+ base::Bind(&TestFileErrorInjectorImpl:: |
+ RecordDownloadFileConstruction, |
+ this), |
+ base::Bind(&TestFileErrorInjectorImpl:: |
+ RecordDownloadFileDestruction, |
+ this))); |
+ |
+ download_file_manager->SetFileFactoryForTesting( |
+ download_file_factory.Pass()); |
+ |
+ created_factory_ = true; |
+ } |
+ |
+ DownloadFileWithErrorsFactory* file_factory = |
+ static_cast<DownloadFileWithErrorsFactory*>( |
+ download_file_manager->GetFileFactoryForTesting()); |
+ |
+ file_factory->ClearErrors(); |
+ |
+ for (ErrorMap::const_iterator it = injected_errors_.begin(); |
+ it != injected_errors_.end(); |
+ ++it) { |
+ file_factory->AddError(it->second); |
+ } |
+ |
+ return true; |
+} |
+ |
+size_t TestFileErrorInjectorImpl::CurrentFileCount() const { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ return files_.size(); |
+} |
+ |
+size_t TestFileErrorInjectorImpl::TotalFileCount() const { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ return found_files_.size(); |
+} |
+ |
+ |
+bool TestFileErrorInjectorImpl::HadFile(const GURL& url) const { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ |
+ return (found_files_.find(url) != found_files_.end()); |
+} |
+ |
+const content::DownloadId TestFileErrorInjectorImpl::GetId( |
+ const GURL& url) const { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ |
+ FileMap::const_iterator it = found_files_.find(url); |
+ if (it == found_files_.end()) |
+ return content::DownloadId::Invalid(); |
+ |
+ return it->second; |
+} |
+ |
+void TestFileErrorInjectorImpl::ClearFoundFiles() { |
+ found_files_.clear(); |
+} |
+ |
+void TestFileErrorInjectorImpl::DownloadFileCreated(GURL url, |
+ content::DownloadId id) { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ DCHECK(files_.find(url) == files_.end()); |
+ |
+ files_[url] = id; |
+ found_files_[url] = id; |
+} |
+ |
+void TestFileErrorInjectorImpl::DestroyingDownloadFile(GURL url) { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ DCHECK(files_.find(url) != files_.end()); |
+ |
+ files_.erase(url); |
+} |
+ |
+void TestFileErrorInjectorImpl::RecordDownloadFileConstruction( |
+ GURL url, content::DownloadId id) { |
+ content::BrowserThread::PostTask( |
+ content::BrowserThread::UI, |
+ FROM_HERE, |
+ base::Bind(&TestFileErrorInjectorImpl::DownloadFileCreated, |
+ this, |
+ url, |
+ id)); |
+} |
+ |
+void TestFileErrorInjectorImpl::RecordDownloadFileDestruction(GURL url) { |
+ content::BrowserThread::PostTask( |
+ content::BrowserThread::UI, |
+ FROM_HERE, |
+ base::Bind(&TestFileErrorInjectorImpl::DestroyingDownloadFile, |
+ this, |
+ url)); |
+} |
+ |
+} // namespace |
+ |
+namespace content { |
+ |
+TestFileErrorInjector::~TestFileErrorInjector() { |
+} |
+ |
+// static |
+scoped_refptr<TestFileErrorInjector> TestFileErrorInjector::Get() { |
+ scoped_refptr<TestFileErrorInjector> single_injector_; |
Randy Smith (Not in Mondays)
2012/03/01 20:28:25
I think this needs to be static.
ahendrickson
2012/03/02 21:58:47
Changed this function to simply create a new injec
|
+ if (single_injector_.get() == NULL) |
+ single_injector_ = new TestFileErrorInjectorImpl; |
+ return single_injector_; |
+} |
+ |
+std::string TestFileErrorInjector::DebugString(FileOperationCode code) { |
+ |
+#define TO_STRING(code) \ |
+ case TestFileErrorInjector::FILE_OPERATION_##code: return #code |
+ |
+ switch (code) { |
+ TO_STRING(INITIALIZE); |
+ TO_STRING(WRITE); |
+ TO_STRING(RENAME); |
+ default: |
+ break; |
+ } |
+ |
+#undef TO_STRING |
+ |
+ return "Unknown"; |
+} |
+ |
+} // namespace content |