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..b20e83c6aeacbdfcb706019b5e60fd118feed704 |
| --- /dev/null |
| +++ b/content/test/test_file_error_injector.cc |
| @@ -0,0 +1,358 @@ |
| +// 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/public/browser/browser_thread.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" |
| + |
| +namespace { |
| + |
| +class DownloadFileWithErrors; |
| +class DownloadFileWithErrorsFactory; |
| + |
| +typedef std::map<int, 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(int index, bool creating)> DownloadFileCallback; |
|
cbentzel
2012/02/28 14:51:41
Design nit: You typically want this on the Downloa
ahendrickson
2012/03/01 09:17:32
I wanted this class to be the first one declared.
|
| + |
| + TestFileErrorInjectorImpl(); |
| + |
| + // content::TestFileErrorInjector interface. |
| + virtual bool AddError(const FileErrorInfo& error_info) OVERRIDE; |
| + virtual bool InjectErrors() 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. |
|
Randy Smith (Not in Mondays)
2012/02/28 22:06:13
nit: Aren't these callbacks from the factory, not
ahendrickson
2012/03/01 09:17:32
No, the factory passes the callback objects to the
Randy Smith (Not in Mondays)
2012/03/01 20:28:24
Right, got it--sorry for the confusion.
|
| + virtual void DownloadFileCreated(int file_index); |
|
cbentzel
2012/02/28 14:51:41
These don't need to be virtual. Could also be priv
ahendrickson
2012/03/01 09:17:32
Done.
|
| + virtual void DestroyingDownloadFile(int file_index); |
| + |
| + private: |
| + virtual ~TestFileErrorInjectorImpl(); |
| + |
| + void RecordDownloadFileConstructionDestruction(int index, bool creating); |
| + |
| + typedef std::set<int> FileSet; |
| + |
| + // Our injected error list, mapped by file index. One per file. |
|
Randy Smith (Not in Mondays)
2012/02/28 22:06:13
This is the first time I noticed that we could onl
ahendrickson
2012/03/01 09:17:32
I added a comment to AddError().
|
| + ErrorMap injected_errors_; |
| + |
| + // Keep track of active DownloadFiles. |
| + FileSet files_; |
| + |
| + // We have created a factory. Used for validation. |
| + bool created_factory_; |
| + |
| + // The number of files we've recorded. |
| + int file_counter_; |
| + |
| + 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::DownloadFileCallback& 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); |
| + |
| + // 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 creation and destruction. |
| + TestFileErrorInjectorImpl::DownloadFileCallback callback_; |
| +}; |
| + |
| +// A factory for constructing DownloadFiles that inject errors. |
| +class DownloadFileWithErrorsFactory |
| + : public DownloadFileManager::DownloadFileFactory { |
| + public: |
| + |
| + DownloadFileWithErrorsFactory( |
| + const TestFileErrorInjectorImpl::DownloadFileCallback& 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); |
| + |
| + size_t files_created() const { return file_counter_; } |
| + |
| + private: |
| + // Number of files we've created. |
| + size_t file_counter_; |
| + |
| + // Our injected error list, mapped by file index. One per file. |
| + ErrorMap injected_errors_; |
| + |
| + // Callback for creation and destruction. |
| + TestFileErrorInjectorImpl::DownloadFileCallback 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::DownloadFileCallback& callback) |
| + : DownloadFileImpl(info, |
| + request_handle, |
| + download_manager, |
| + calculate_hash, |
| + bound_net_log), |
| + error_info_(error_info), |
| + callback_(callback) { |
| + // Record creation. |
| + callback_.Run(error_info_.file_index, true); |
| +} |
| + |
| +DownloadFileWithErrors::~DownloadFileWithErrors() { |
| + // Record destruction. |
| + callback_.Run(error_info_.file_index, false); |
| +} |
| + |
| + |
| +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]; |
|
cbentzel
2012/02/28 14:51:41
I'd get rid of the operation_counter_ map here - n
ahendrickson
2012/03/02 21:58:47
We're now using operation instances other than 0.
|
| + ++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__ << "()" |
| + << " code = " << content::TestFileErrorInjector::DebugString(code) |
| + << " (" << code << ")" |
| + << " counter = " << counter |
| + << " original_error = " << original_net_error |
| + << " new error = " << error_info_.net_error; |
| + |
| + return error_info_.net_error; |
| +} |
| + |
| +DownloadFileWithErrorsFactory::DownloadFileWithErrorsFactory( |
| + const TestFileErrorInjectorImpl::DownloadFileCallback& callback) |
| + : file_counter_(0), callback_(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) { |
| + // 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_++], |
|
Randy Smith (Not in Mondays)
2012/02/28 22:06:13
What happens if this accesses an uninitialized ent
ahendrickson
2012/03/01 09:17:32
No error is injected.
Randy Smith (Not in Mondays)
2012/03/01 20:28:24
I think Chris did the analysis that I was worried
ahendrickson
2012/03/02 21:58:47
Fixed.
|
| + callback_); |
| +} |
| + |
| +bool DownloadFileWithErrorsFactory::AddError( |
| + const content::TestFileErrorInjector::FileErrorInfo& error_info) { |
| + DCHECK_LE(0, error_info.file_index); |
| + DCHECK_LE(0, error_info.operation_instance); |
| + |
| + // Creates an empty entry if necessary. |
| + injected_errors_[error_info.file_index] = error_info; |
|
Randy Smith (Not in Mondays)
2012/02/28 22:06:13
Should this interface allow us to overwrite alread
ahendrickson
2012/03/01 09:17:32
No, although it just replaces the existing value.
|
| + |
| + return true; |
| +} |
| + |
| +TestFileErrorInjectorImpl::TestFileErrorInjectorImpl() |
| + : created_factory_(false), file_counter_(0) { |
| +} |
| + |
| +TestFileErrorInjectorImpl::~TestFileErrorInjectorImpl() { |
| +} |
| + |
| +bool TestFileErrorInjectorImpl::AddError(const FileErrorInfo& error_info) { |
| + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| + DCHECK(!created_factory_); |
| + DCHECK_LE(0, error_info.file_index); |
| + DCHECK_LE(0, error_info.operation_instance); |
| + |
| + // Creates an empty entry if necessary. |
| + injected_errors_[error_info.file_index] = error_info; |
|
cbentzel
2012/02/28 14:51:41
Why are you worried about adding support for a map
ahendrickson
2012/03/01 09:17:32
The new tests run multiple downloads, so we need t
|
| + |
| + return true; |
| +} |
| + |
| +bool TestFileErrorInjectorImpl::InjectErrors() { |
| + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| + DCHECK(!created_factory_); |
| + |
| + DownloadFileWithErrorsFactory* download_file_factory = |
| + new DownloadFileWithErrorsFactory( |
| + base::Bind( |
| + &TestFileErrorInjectorImpl:: |
| + RecordDownloadFileConstructionDestruction, |
| + this)); |
| + |
| + for (ErrorMap::const_iterator it = injected_errors_.begin(); |
| + it != injected_errors_.end(); |
| + ++it) { |
| + download_file_factory->AddError(it->second); |
|
Randy Smith (Not in Mondays)
2012/02/28 22:06:13
nit, suggestion: You could create download_file_fa
ahendrickson
2012/03/01 09:17:32
I did that in an earlier pass, but Chris asked me
Randy Smith (Not in Mondays)
2012/03/01 20:28:24
Given that I think Chris just suggested the same i
|
| + } |
| + |
| + DownloadFileManager* download_file_manager = GetDownloadFileManager(); |
| + DCHECK(download_file_manager); |
| + // Transfers ownership to |DownloadFileManager|. |
|
cbentzel
2012/02/28 14:51:41
Use scoped_ptr.Pass here instead.
ahendrickson
2012/03/01 09:17:32
Done.
|
| + download_file_manager->SetFileFactoryForTesting(download_file_factory); |
| + |
| + created_factory_ = true; |
| + |
| + return true; |
| +} |
| + |
| +size_t TestFileErrorInjectorImpl::CurrentFileCount() const { |
| + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| + return files_.size(); |
| +} |
| + |
| +size_t TestFileErrorInjectorImpl::FileCreationCount() const { |
| + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| + return file_counter_; |
| +} |
| + |
| + |
| +bool TestFileErrorInjectorImpl::HasFile(int file_index) const { |
| + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| + return (files_.find(file_index) != files_.end()); |
| +} |
| + |
| +void TestFileErrorInjectorImpl::DownloadFileCreated(int file_index) { |
| + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| + DCHECK_EQ(file_index, file_counter_); |
| + |
| + file_counter_++; |
| + files_.insert(file_index); |
| +} |
| + |
| +void TestFileErrorInjectorImpl::DestroyingDownloadFile(int file_index) { |
| + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| + |
| + files_.erase(file_index); |
| +} |
| + |
| +void TestFileErrorInjectorImpl::RecordDownloadFileConstructionDestruction( |
| + int index, bool constructing) { |
| + content::BrowserThread::PostTask( |
| + content::BrowserThread::UI, |
| + FROM_HERE, |
| + base::Bind(constructing ? |
| + &TestFileErrorInjectorImpl::DownloadFileCreated : |
| + &TestFileErrorInjectorImpl::DestroyingDownloadFile, |
| + this, |
| + index)); |
| +} |
| + |
| +} // namespace |
| + |
| +namespace content { |
| + |
| +// static |
| +scoped_refptr<TestFileErrorInjector> TestFileErrorInjector::Create() { |
| + return new TestFileErrorInjectorImpl; |
| +} |
| + |
| +std::string TestFileErrorInjector::DebugString(FileOperationCode code) { |
| + |
| +#define TO_STRING(code) \ |
|
cbentzel
2012/02/28 14:51:41
Although this macro is localized, it provides very
ahendrickson
2012/03/01 09:17:32
Removed the namespace reference.
It is now used i
Randy Smith (Not in Mondays)
2012/03/06 23:05:19
FWIW, I agree with Chris; I think writing it direc
|
| + case content::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 |