Chromium Code Reviews| 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..e4204e6ade4c7ca5b6597ee7a2fc47ed54d04cd4 |
| --- /dev/null |
| +++ b/content/test/test_file_error_injector.cc |
| @@ -0,0 +1,355 @@ |
| +// Copyright (c) 2011 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/browser/download/download_file_impl.h" |
| +#include "content/browser/download/download_file_manager.h" |
| +#include "content/browser/renderer_host/resource_dispatcher_host.h" |
| + |
| +class DownloadFileWithErrors; |
| + |
| +namespace { |
| + |
| +// Structure that encapsulates the information needed to inject a file error. |
| +struct FileErrorInfo { |
| + FileErrorInfo() |
| + : file_index(-1), |
| + code(FILE_OPERATION_INITIALIZE), |
| + operation_instance(-1), |
| + net_error(net::OK) { |
| + } |
| + |
| + int file_index; // 0-based. |
| + FileOperationCode code; |
| + int operation_instance; // 0-based. |
| + net::Error net_error; |
| +}; |
| + |
| +// List of errors. |
| +typedef std::vector<FileErrorInfo> ErrorList; |
| + |
| +// Structure that associates a file with a set of errors. |
| +struct FileErrors { |
| + FileErrors() : file(NULL) {} |
| + |
| + DownloadFileWithErrors* file; |
| + ErrorList error_list; |
| +}; |
| + |
| +DownloadFileManager* GetDownloadFileManager() { |
| + ResourceDispatcherHost* rdh = ResourceDispatcherHost::Get(); |
| + DCHECK(rdh != NULL); |
| + return rdh->download_file_manager(); |
| +} |
| + |
| +std::string DebugString(FileOperationCode code) { |
| + |
| +#define TO_STRING(code) case FILE_OPERATION_##code: return #code |
| + |
| + switch (code) { |
| + TO_STRING(INITIALIZE); |
| + TO_STRING(WRITE); |
| + TO_STRING(RENAME); |
| + default: |
| + break; |
| + } |
| + |
| +#undef TO_STRING |
| + |
| + return "Unknown"; |
| +} |
| + |
| +} // namespace |
| + |
| +class TestFileErrorInjectorImpl : public TestFileErrorInjector { |
| + public: |
| + TestFileErrorInjectorImpl(); |
| + |
| + // TestFileErrorInjector interface. |
| + virtual bool InjectError(int file_index, |
| + FileOperationCode operation, |
| + int operation_instance, |
| + net::Error net_error) OVERRIDE; |
| + virtual size_t CurrentFileCount() const OVERRIDE; |
| + virtual size_t FileCreationCount() const OVERRIDE { return file_counter_; } |
| + |
| + // Callbacks from the download file. |
| + virtual int DownloadFileCreated(DownloadFileWithErrors* download_file); |
| + virtual void DestroyingDownloadFile(DownloadFileWithErrors* download_file); |
| + |
| + private: |
| + virtual ~TestFileErrorInjectorImpl(); |
| + |
| + // The number of files we've created. |
| + int file_counter_; |
| + |
| + // Our injected error list, mapped by file index. |
| + std::map<int, FileErrors> injected_errors_; |
| + |
| + 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, |
| + scoped_refptr<TestFileErrorInjectorImpl> injector); |
| + |
| + ~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; |
| + |
| + // Error generating helpers. |
| + net::Error ShouldReturnError(FileOperationCode code, |
| + net::Error original_net_error); |
| + |
| + void AddError(const FileErrorInfo& info); |
| + void SetErrorList(int index, |
| + const ErrorList& error_list); |
| + int index() const { return index_; } |
| + |
| + private: |
| + // Map from instance number to file error information. |
| + typedef std::map<int, FileErrorInfo> InstanceMap; |
| + |
| + // Map of errors by operation code |
| + typedef std::map<FileOperationCode, |
| + InstanceMap> ErrorMap; |
| + |
| + // The class that manages errors. |
| + scoped_refptr<TestFileErrorInjectorImpl> injector_; |
| + |
| + // Our file index. |
| + int index_; |
| + |
| + // Our injected error list. |
| + ErrorMap error_map_; |
| + |
| + // Count per operation. 0-based. |
| + std::map<FileOperationCode, int> operation_counter_; |
| +}; |
| + |
| +// A factory for constructing DownloadFiles that inject errors. |
| +class DownloadFileWithErrorsFactory |
| + : public DownloadFileManager::DownloadFileFactory { |
| + public: |
| + |
| + explicit DownloadFileWithErrorsFactory( |
| + scoped_refptr<TestFileErrorInjectorImpl> injector); |
| + |
| + virtual ~DownloadFileWithErrorsFactory(); |
| + |
| + virtual content::DownloadFile* CreateFile( |
| + DownloadCreateInfo* info, |
| + const DownloadRequestHandle& request_handle, |
| + content::DownloadManager* download_manager, |
| + bool calculate_hash, |
| + const net::BoundNetLog& bound_net_log); |
| + |
| + private: |
| + |
| + scoped_refptr<TestFileErrorInjectorImpl> injector_; |
| +}; |
| + |
| + |
| +// Implementations. |
| + |
| +// static |
| +TestFileErrorInjector* TestFileErrorInjector::Create() { |
| + return new TestFileErrorInjectorImpl; |
| +} |
| + |
| +TestFileErrorInjectorImpl::TestFileErrorInjectorImpl() : file_counter_(0) { |
| + DownloadFileWithErrorsFactory* download_file_factory = |
| + new DownloadFileWithErrorsFactory(this); |
| + DownloadFileManager* download_file_manager = GetDownloadFileManager(); |
| + DCHECK(download_file_manager); |
| + // Transfers ownership. |
| + download_file_manager->SetFileFactory(download_file_factory); |
| +} |
| + |
| +TestFileErrorInjectorImpl::~TestFileErrorInjectorImpl() { |
| +} |
| + |
| +bool TestFileErrorInjectorImpl::InjectError(int file_index, |
| + FileOperationCode operation, |
| + int operation_instance, |
| + net::Error net_error) { |
| + DCHECK_LE(0, file_index); |
| + DCHECK_LE(0, operation_instance); |
| + |
| + // Fill in a new entry. |
| + FileErrorInfo info; |
| + info.file_index = file_index; |
| + info.code = operation; |
| + info.operation_instance = operation_instance; |
| + info.net_error = net_error; |
| + |
| + // Creates an empty entry if necessary. |
| + FileErrors& file_errors = injected_errors_[file_index]; |
| + file_errors.error_list.push_back(info); |
| + |
| + // If the file already exists, simply add this entry to the error list. |
|
cbentzel
2012/02/24 02:25:12
Do you need to handle this case?
ahendrickson
2012/02/24 22:48:39
Removed.
|
| + if (file_errors.file != NULL) |
| + file_errors.file->AddError(info); |
| + |
| + return true; |
| +} |
| + |
| +size_t TestFileErrorInjectorImpl::CurrentFileCount() const { |
| + size_t count = 0; |
| + |
| + // Count the map entries with a non-NULL file pointer. |
| + for (std::map<int, FileErrors>::const_iterator it = injected_errors_.begin(); |
| + it != injected_errors_.end(); |
| + ++it) { |
| + if (it->second.file != NULL) |
| + count++; |
| + } |
| + |
| + return count; |
| +} |
| + |
| +int TestFileErrorInjectorImpl::DownloadFileCreated( |
| + DownloadFileWithErrors* download_file) { |
| + DCHECK(download_file != NULL); |
| + |
| + // Creates an empty instance if necessary. |
| + FileErrors& file_errors = injected_errors_[file_counter_]; |
| + DCHECK(file_errors.file == NULL); |
| + file_errors.file = download_file; // Set the file. |
| + |
| + download_file->SetErrorList(file_counter_, file_errors.error_list); |
| + |
| + return file_counter_++; |
| +} |
| + |
| +void TestFileErrorInjectorImpl::DestroyingDownloadFile( |
| + DownloadFileWithErrors* download_file) { |
| + DCHECK(download_file != NULL); |
| + |
| + // Mark as gone by making the file NULL. |
| + injected_errors_[download_file->index()].file = NULL; |
| +} |
| + |
| +DownloadFileWithErrors::DownloadFileWithErrors( |
| + const DownloadCreateInfo* info, |
| + DownloadRequestHandleInterface* request_handle, |
| + content::DownloadManager* download_manager, |
| + bool calculate_hash, |
| + const net::BoundNetLog& bound_net_log, |
| + scoped_refptr<TestFileErrorInjectorImpl> injector) |
| + : DownloadFileImpl(info, |
| + request_handle, |
| + download_manager, |
| + calculate_hash, |
| + bound_net_log), |
| + injector_(injector), |
| + index_(-1) { |
| + if (injector_) |
| + injector_->DownloadFileCreated(this); |
| +} |
| + |
| +DownloadFileWithErrors::~DownloadFileWithErrors() { |
| + if (injector_) |
| + injector_->DestroyingDownloadFile(this); |
| +} |
| + |
| + |
| +net::Error DownloadFileWithErrors::Initialize() { |
| + return ShouldReturnError(FILE_OPERATION_INITIALIZE, |
| + DownloadFileImpl::Initialize()); |
| +} |
| + |
| +net::Error DownloadFileWithErrors::AppendDataToFile(const char* data, |
| + size_t data_len) { |
| + return ShouldReturnError(FILE_OPERATION_WRITE, |
| + DownloadFileImpl::AppendDataToFile(data, data_len)); |
| +} |
| + |
| +net::Error DownloadFileWithErrors::Rename(const FilePath& full_path) { |
| + return ShouldReturnError(FILE_OPERATION_RENAME, |
| + DownloadFileImpl::Rename(full_path)); |
| +} |
| + |
| +net::Error DownloadFileWithErrors::ShouldReturnError( |
| + FileOperationCode code, |
| + net::Error original_net_error) { |
| + int counter = operation_counter_[code]; |
| + ++operation_counter_[code]; |
| + |
| + if (error_map_.find(code) == error_map_.end()) |
| + return original_net_error; // No errors for this operation. |
| + |
| + InstanceMap& instance_map = error_map_[code]; |
| + |
| + if (instance_map.find(counter) == instance_map.end()) |
| + return original_net_error; // No error for this instance of the operation. |
| + |
| + DVLOG(20) << " " << __FUNCTION__ << "()" |
| + << " code = " << ::DebugString(code) << " (" << code << ")" |
| + << " counter = " << counter |
| + << " original_error = " << original_net_error |
| + << " new error = " << instance_map[counter].net_error; |
| + |
| + return instance_map[counter].net_error; // Error! |
| +} |
| + |
| +void DownloadFileWithErrors::AddError( |
| + const FileErrorInfo& info) { |
| + // Get the instance map for the operation code. |
| + InstanceMap& instance_map = error_map_[info.code]; |
| + |
| + // Duplicate entries are an error. |
| + DCHECK(instance_map.end() == instance_map.find(info.operation_instance)); |
| + |
| + // Set the error information in the map. |
| + instance_map[info.operation_instance] = info; |
| +} |
| + |
| +void DownloadFileWithErrors::SetErrorList( |
| + int index, const ErrorList& error_list) { |
| + DCHECK_EQ(-1, index_); |
| + DCHECK_LE(0, index); |
| + index_ = index; |
| + |
| + // Parcel out the error list by operation code and instance. |
| + for (size_t i = 0; i < error_list.size(); ++i) |
| + AddError(error_list[i]); |
| +} |
| + |
| +DownloadFileWithErrorsFactory::DownloadFileWithErrorsFactory( |
| + scoped_refptr<TestFileErrorInjectorImpl> injector) : injector_(injector) { |
| +} |
| + |
| +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) { |
| + return new DownloadFileWithErrors(info, |
| + new DownloadRequestHandle(request_handle), |
| + download_manager, |
| + calculate_hash, |
| + bound_net_log, |
| + injector_); |
| +} |