Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(505)

Unified Diff: content/test/test_file_error_injector.cc

Issue 9426029: Test file errors in downloads. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Refactored per Chris' comments Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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
« content/test/test_file_error_injector.h ('K') | « content/test/test_file_error_injector.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698