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..184d8bc64ab3f4d02f93cab3f7ef954f5b9223c0 |
| --- /dev/null |
| +++ b/content/test/test_file_error_injector.cc |
| @@ -0,0 +1,338 @@ |
| +// 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 TestFileErrorInjectorImpl; |
| + |
| +namespace { |
| + |
| +// Structure that encapsulates the information needed to inject a file error. |
| +struct FileErrorInfo { |
|
cbentzel
2012/02/27 02:18:54
This is kind of duplicated in the browsertest. Per
ahendrickson
2012/02/28 03:28:56
Done.
|
| + FileErrorInfo() |
| + : file_index(-1), |
| + code(content::FILE_OPERATION_INITIALIZE), |
| + operation_instance(-1), |
| + net_error(net::OK) { |
| + } |
| + |
| + int file_index; // 0-based index into created DownloadFile instances. |
| + content::FileOperationCode code; // Operation to affect. |
| + int operation_instance; // 0-based count of operation calls. |
| + net::Error net_error; // Error to inject. |
| +}; |
| + |
| +DownloadFileManager* GetDownloadFileManager() { |
| + ResourceDispatcherHost* rdh = ResourceDispatcherHost::Get(); |
| + DCHECK(rdh != NULL); |
|
cbentzel
2012/02/27 02:18:54
What cases could it be NULL?
ahendrickson
2012/02/28 03:28:56
There are tests (perhaps just unit tests) where RD
|
| + return rdh->download_file_manager(); |
| +} |
| + |
| +std::string DebugString(content::FileOperationCode code) { |
|
cbentzel
2012/02/27 02:18:54
Is this needed?
ahendrickson
2012/02/28 03:28:56
It's used below. I think it's useful to trace err
|
| + |
| +#define TO_STRING(code) case content::FILE_OPERATION_##code: return #code |
| + |
| + switch (code) { |
| + TO_STRING(INITIALIZE); |
| + TO_STRING(WRITE); |
| + TO_STRING(RENAME); |
| + default: |
| + break; |
| + } |
| + |
| +#undef TO_STRING |
| + |
| + return "Unknown"; |
| +} |
| + |
| +} // namespace |
| + |
| +namespace content { |
| + |
| +// A class that performs file operations and injects errors. |
| +class DownloadFileWithErrors: public DownloadFileImpl { |
|
cbentzel
2012/02/27 02:18:54
Could this be in an anonymous nested namespace in
ahendrickson
2012/02/28 03:28:56
Done.
|
| + public: |
| + DownloadFileWithErrors(const DownloadCreateInfo* info, |
| + DownloadRequestHandleInterface* request_handle, |
| + content::DownloadManager* download_manager, |
| + bool calculate_hash, |
| + const net::BoundNetLog& bound_net_log, |
| + const FileErrorInfo error_info, |
| + 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; |
| + |
| + // For |TestFileErrorInjectorImpl| to record lifetime. |
| + int index() const { return error_info_.file_index; } |
| + |
| + private: |
| + // Error generating helper. |
| + net::Error ShouldReturnError(FileOperationCode code, |
| + net::Error original_net_error); |
| + |
| + // The class that injects errors. |
| + TestFileErrorInjectorImpl* injector_; // Needed for destructor. |
| + |
| + // Our injected error. Only one per file. |
| + FileErrorInfo error_info_; |
| + |
| + // 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(TestFileErrorInjectorImpl* injector); |
| + 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 InjectError(int file_index, |
| + content::FileOperationCode operation, |
| + int operation_instance, |
| + net::Error net_error); |
| + |
| + size_t files_created() const { return file_counter_; } |
| + |
| + private: |
| + // The number of files we've created. |
| + int file_counter_; |
| + |
| + // We pass this to DownloadFileWithErrors, for lifetime tracking. |
| + // We own this. |
| + TestFileErrorInjectorImpl* injector_; |
|
cbentzel
2012/02/26 15:01:48
scoped_ptr is much clearer declaration of ownershi
ahendrickson
2012/02/28 03:28:56
Now using RefCountedThreadSafe.
|
| + |
| + // Our injected error list, mapped by file index. One per file. |
| + std::map<int, FileErrorInfo> injected_errors_; |
| +}; |
| + |
| +} // namespace content |
| + |
| +class TestFileErrorInjectorImpl : public content::TestFileErrorInjector { |
| + public: |
| + TestFileErrorInjectorImpl(); |
| + virtual ~TestFileErrorInjectorImpl(); |
| + |
| + // content::TestFileErrorInjector interface. |
| + virtual bool InjectError(int file_index, |
| + content::FileOperationCode operation, |
| + int operation_instance, |
| + net::Error net_error) OVERRIDE; |
| + virtual size_t CurrentFileCount() const OVERRIDE; |
| + virtual size_t FileCreationCount() const OVERRIDE; |
| + virtual bool HasFile(int file_index) const OVERRIDE; |
| + |
| + // Callbacks from the download file, to record lifetimes. |
| + virtual void DownloadFileCreated( |
| + content::DownloadFileWithErrors* download_file); |
| + virtual void DestroyingDownloadFile( |
| + content::DownloadFileWithErrors* download_file); |
| + |
| + private: |
| + typedef std::map<int, content::DownloadFileWithErrors*> FileMap; |
| + |
| + // Keep track of active DownloadFiles. |
| + FileMap files_; |
| + |
| + content::DownloadFileWithErrorsFactory* download_file_factory_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(TestFileErrorInjectorImpl); |
| +}; |
| + |
| + |
| +// Implementations. |
| +namespace content { |
| + |
| +DownloadFileWithErrors::DownloadFileWithErrors( |
| + const DownloadCreateInfo* info, |
| + DownloadRequestHandleInterface* request_handle, |
| + content::DownloadManager* download_manager, |
| + bool calculate_hash, |
| + const net::BoundNetLog& bound_net_log, |
| + const FileErrorInfo error_info, |
| + TestFileErrorInjectorImpl* injector) |
| + : DownloadFileImpl(info, |
| + request_handle, |
| + download_manager, |
| + calculate_hash, |
| + bound_net_log), |
| + injector_(injector), |
| + error_info_(error_info) { |
| + // Record creation. |
| + injector_->DownloadFileCreated(this); |
|
cbentzel
2012/02/27 02:18:54
Are you worried at all about thread-safety issues
ahendrickson
2012/02/28 03:28:56
Switched to using RefCountedThreadSafe for the inj
|
| +} |
| + |
| +DownloadFileWithErrors::~DownloadFileWithErrors() { |
| + // Record destruction. |
| + 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]; |
|
cbentzel
2012/02/27 02:18:54
Looks like the operation_instance is always 0 when
ahendrickson
2012/02/28 03:28:56
Would prefer not to, as I have a use for this in m
|
| + ++operation_counter_[code]; |
| + |
| + if (code != error_info_.code) |
| + return original_net_error; // No error for this operation. |
|
cbentzel
2012/02/27 02:18:54
Comments not really needed and a bit unclear actua
ahendrickson
2012/02/28 03:28:56
Removed.
|
| + |
| + if (counter != error_info_.operation_instance) |
| + 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 = " << error_info_.net_error; |
| + |
| + return error_info_.net_error; // Error! |
| +} |
| + |
| +DownloadFileWithErrorsFactory::DownloadFileWithErrorsFactory( |
| + TestFileErrorInjectorImpl* injector) |
|
cbentzel
2012/02/27 02:18:54
should be passed in via scoped_ptr if the Factory
ahendrickson
2012/02/28 03:28:56
Now using RefCountedThreadSafe for the injector.
|
| + : file_counter_(0), injector_(injector) { |
| +} |
| + |
| +DownloadFileWithErrorsFactory::~DownloadFileWithErrorsFactory() { |
| + delete injector_; |
| + injector_ = NULL; |
| +} |
| + |
| +content::DownloadFile* DownloadFileWithErrorsFactory::CreateFile( |
| + DownloadCreateInfo* info, |
| + const DownloadRequestHandle& request_handle, |
| + content::DownloadManager* download_manager, |
| + bool calculate_hash, |
| + const net::BoundNetLog& bound_net_log) { |
| + // Creates entry if it doesn't exist. |
| + injected_errors_[file_counter_].file_index = file_counter_; // Set index. |
| + return new DownloadFileWithErrors(info, |
| + new DownloadRequestHandle(request_handle), |
| + download_manager, |
| + calculate_hash, |
| + bound_net_log, |
| + injected_errors_[file_counter_++], |
| + injector_); |
| +} |
| + |
| +bool DownloadFileWithErrorsFactory::InjectError( |
| + int file_index, |
| + content::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. |
| + injected_errors_[file_index] = info; |
| + |
| + return true; |
| +} |
| + |
| +} // namespace content |
| + |
| + |
| +// static |
| +content::TestFileErrorInjector* content::TestFileErrorInjector::Create() { |
| + return new TestFileErrorInjectorImpl; |
| +} |
| + |
| +TestFileErrorInjectorImpl::TestFileErrorInjectorImpl() |
| + : download_file_factory_(new content::DownloadFileWithErrorsFactory(this)) { |
| + // |DownloadFileWithErrorsFactory| now owns |TestFileErrorInjectorImpl|. |
| + DownloadFileManager* download_file_manager = |
| + GetDownloadFileManager(); |
| + DCHECK(download_file_manager); |
| + // Transfers ownership to |DownloadFileManager|. |
| + download_file_manager->SetFileFactory(download_file_factory_); |
| +} |
| + |
| +TestFileErrorInjectorImpl::~TestFileErrorInjectorImpl() { |
| +} |
| + |
| +bool TestFileErrorInjectorImpl::InjectError( |
| + int file_index, |
| + content::FileOperationCode operation, |
| + int operation_instance, |
| + net::Error net_error) { |
| + return download_file_factory_->InjectError(file_index, |
| + operation, |
| + operation_instance, |
| + net_error); |
| +} |
| + |
| +size_t TestFileErrorInjectorImpl::CurrentFileCount() const { |
| + return files_.size(); |
|
cbentzel
2012/02/27 02:18:54
What is the thread-safety here?
ahendrickson
2012/02/28 03:28:56
All operations on files_ now happen on the UI thre
|
| +} |
| + |
| +size_t TestFileErrorInjectorImpl::FileCreationCount() const { |
| + return download_file_factory_->files_created(); |
| +} |
| + |
| + |
| +bool TestFileErrorInjectorImpl::HasFile(int file_index) const { |
|
cbentzel
2012/02/27 02:18:54
You aren't using this. Would also remove the need
ahendrickson
2012/02/28 03:28:56
Now using it.
|
| + return (files_.find(file_index) != files_.end()); |
| +} |
| + |
| +void TestFileErrorInjectorImpl::DownloadFileCreated( |
| + content::DownloadFileWithErrors* download_file) { |
| + DCHECK(download_file != NULL); |
| + |
| + files_[download_file->index()] = download_file; |
| +} |
| + |
| +void TestFileErrorInjectorImpl::DestroyingDownloadFile( |
| + content::DownloadFileWithErrors* download_file) { |
| + DCHECK(download_file != NULL); |
| + |
| + files_.erase(download_file->index()); |
| +} |