| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "content/test/test_file_error_injector.h" |
| 6 |
| 7 #include <map> |
| 8 #include <vector> |
| 9 |
| 10 #include "base/compiler_specific.h" |
| 11 #include "base/logging.h" |
| 12 #include "content/public/browser/browser_thread.h" |
| 13 #include "content/browser/download/download_create_info.h" |
| 14 #include "content/browser/download/download_file_impl.h" |
| 15 #include "content/browser/download/download_file_manager.h" |
| 16 #include "content/browser/renderer_host/resource_dispatcher_host.h" |
| 17 #include "googleurl/src/gurl.h" |
| 18 |
| 19 namespace { |
| 20 |
| 21 class DownloadFileWithErrors; |
| 22 class DownloadFileWithErrorsFactory; |
| 23 |
| 24 typedef std::map<std::string, content::TestFileErrorInjector::FileErrorInfo> |
| 25 ErrorMap; |
| 26 |
| 27 DownloadFileManager* GetDownloadFileManager() { |
| 28 ResourceDispatcherHost* rdh = ResourceDispatcherHost::Get(); |
| 29 DCHECK(rdh != NULL); |
| 30 return rdh->download_file_manager(); |
| 31 } |
| 32 |
| 33 class TestFileErrorInjectorImpl : public content::TestFileErrorInjector { |
| 34 public: |
| 35 typedef base::Callback<void(GURL url, content::DownloadId id)> |
| 36 ConstructionCallback; |
| 37 typedef base::Callback<void(GURL url)> DestructionCallback; |
| 38 |
| 39 struct InjectedFileInfo { |
| 40 InjectedFileInfo() : id(content::DownloadId::Invalid()) {} |
| 41 |
| 42 GURL url; |
| 43 content::DownloadId id; |
| 44 }; |
| 45 |
| 46 TestFileErrorInjectorImpl(); |
| 47 |
| 48 // content::TestFileErrorInjector interface. |
| 49 virtual bool AddError(const FileErrorInfo& error_info) OVERRIDE; |
| 50 virtual void ClearErrors() OVERRIDE; |
| 51 virtual bool InjectErrors() OVERRIDE; |
| 52 virtual size_t CurrentFileCount() const OVERRIDE; |
| 53 virtual size_t TotalFileCount() const OVERRIDE; |
| 54 virtual bool HadFile(const GURL& url) const OVERRIDE; |
| 55 virtual const content::DownloadId GetId(const GURL& url) const OVERRIDE; |
| 56 virtual void ClearFoundFiles() OVERRIDE; |
| 57 |
| 58 private: |
| 59 typedef std::map<GURL, InjectedFileInfo> FileMap; |
| 60 |
| 61 virtual ~TestFileErrorInjectorImpl(); |
| 62 |
| 63 void RecordDownloadFileConstruction(GURL url, content::DownloadId id); |
| 64 void RecordDownloadFileDestruction(GURL url); |
| 65 |
| 66 // Callbacks from the download file, to record lifetimes. |
| 67 void DownloadFileCreated(GURL url, content::DownloadId id); |
| 68 void DestroyingDownloadFile(GURL url); |
| 69 |
| 70 // Our injected error list, mapped by file index. One per file. |
| 71 ErrorMap injected_errors_; |
| 72 |
| 73 // Keep track of active DownloadFiles. |
| 74 FileMap files_; |
| 75 |
| 76 // Keep track of found DownloadFiles. |
| 77 FileMap found_files_; |
| 78 |
| 79 // We have created a factory. Used for validation. |
| 80 bool created_factory_; |
| 81 |
| 82 DISALLOW_COPY_AND_ASSIGN(TestFileErrorInjectorImpl); |
| 83 }; |
| 84 |
| 85 // A class that performs file operations and injects errors. |
| 86 class DownloadFileWithErrors: public DownloadFileImpl { |
| 87 public: |
| 88 DownloadFileWithErrors( |
| 89 const DownloadCreateInfo* info, |
| 90 DownloadRequestHandleInterface* request_handle, |
| 91 content::DownloadManager* download_manager, |
| 92 bool calculate_hash, |
| 93 const net::BoundNetLog& bound_net_log, |
| 94 const content::TestFileErrorInjector::FileErrorInfo& error_info, |
| 95 const TestFileErrorInjectorImpl::ConstructionCallback& ctor_callback, |
| 96 const TestFileErrorInjectorImpl::DestructionCallback& dtor_callback); |
| 97 |
| 98 ~DownloadFileWithErrors(); |
| 99 |
| 100 // DownloadFile interface. |
| 101 virtual net::Error Initialize() OVERRIDE; |
| 102 virtual net::Error AppendDataToFile(const char* data, |
| 103 size_t data_len) OVERRIDE; |
| 104 virtual net::Error Rename(const FilePath& full_path) OVERRIDE; |
| 105 |
| 106 private: |
| 107 // Error generating helper. |
| 108 net::Error ShouldReturnError( |
| 109 content::TestFileErrorInjector::FileOperationCode code, |
| 110 net::Error original_net_error); |
| 111 |
| 112 // Source URL for the file being downloaded. |
| 113 GURL source_url_; |
| 114 |
| 115 // Our injected error. Only one per file. |
| 116 content::TestFileErrorInjector::FileErrorInfo error_info_; |
| 117 |
| 118 // Count per operation. 0-based. |
| 119 std::map<content::TestFileErrorInjector::FileOperationCode, int> |
| 120 operation_counter_; |
| 121 |
| 122 // Callback for destruction. |
| 123 TestFileErrorInjectorImpl::DestructionCallback destruction_callback_; |
| 124 }; |
| 125 |
| 126 // A factory for constructing DownloadFiles that inject errors. |
| 127 class DownloadFileWithErrorsFactory |
| 128 : public DownloadFileManager::DownloadFileFactory { |
| 129 public: |
| 130 |
| 131 DownloadFileWithErrorsFactory( |
| 132 const TestFileErrorInjectorImpl::ConstructionCallback& ctor_callback, |
| 133 const TestFileErrorInjectorImpl::DestructionCallback& dtor_callback); |
| 134 virtual ~DownloadFileWithErrorsFactory(); |
| 135 |
| 136 // DownloadFileFactory interface. |
| 137 virtual content::DownloadFile* CreateFile( |
| 138 DownloadCreateInfo* info, |
| 139 const DownloadRequestHandle& request_handle, |
| 140 content::DownloadManager* download_manager, |
| 141 bool calculate_hash, |
| 142 const net::BoundNetLog& bound_net_log); |
| 143 |
| 144 bool AddError( |
| 145 const content::TestFileErrorInjector::FileErrorInfo& error_info); |
| 146 |
| 147 void ClearErrors(); |
| 148 |
| 149 private: |
| 150 // Our injected error list, mapped by file index. One per file. |
| 151 ErrorMap injected_errors_; |
| 152 |
| 153 // Callback for creation and destruction. |
| 154 TestFileErrorInjectorImpl::ConstructionCallback construction_callback_; |
| 155 TestFileErrorInjectorImpl::DestructionCallback destruction_callback_; |
| 156 }; |
| 157 |
| 158 // Implementations. |
| 159 |
| 160 DownloadFileWithErrors::DownloadFileWithErrors( |
| 161 const DownloadCreateInfo* info, |
| 162 DownloadRequestHandleInterface* request_handle, |
| 163 content::DownloadManager* download_manager, |
| 164 bool calculate_hash, |
| 165 const net::BoundNetLog& bound_net_log, |
| 166 const content::TestFileErrorInjector::FileErrorInfo& error_info, |
| 167 const TestFileErrorInjectorImpl::ConstructionCallback& ctor_callback, |
| 168 const TestFileErrorInjectorImpl::DestructionCallback& dtor_callback) |
| 169 : DownloadFileImpl(info, |
| 170 request_handle, |
| 171 download_manager, |
| 172 calculate_hash, |
| 173 bound_net_log), |
| 174 source_url_(info->url()), |
| 175 error_info_(error_info), |
| 176 destruction_callback_(dtor_callback) { |
| 177 // Record creation. |
| 178 ctor_callback.Run(source_url_, info->download_id); |
| 179 } |
| 180 |
| 181 DownloadFileWithErrors::~DownloadFileWithErrors() { |
| 182 // Record destruction. |
| 183 destruction_callback_.Run(source_url_); |
| 184 } |
| 185 |
| 186 |
| 187 net::Error DownloadFileWithErrors::Initialize() { |
| 188 return ShouldReturnError( |
| 189 content::TestFileErrorInjector::FILE_OPERATION_INITIALIZE, |
| 190 DownloadFileImpl::Initialize()); |
| 191 } |
| 192 |
| 193 net::Error DownloadFileWithErrors::AppendDataToFile(const char* data, |
| 194 size_t data_len) { |
| 195 return ShouldReturnError( |
| 196 content::TestFileErrorInjector::FILE_OPERATION_WRITE, |
| 197 DownloadFileImpl::AppendDataToFile(data, data_len)); |
| 198 } |
| 199 |
| 200 net::Error DownloadFileWithErrors::Rename(const FilePath& full_path) { |
| 201 return ShouldReturnError( |
| 202 content::TestFileErrorInjector::FILE_OPERATION_RENAME, |
| 203 DownloadFileImpl::Rename(full_path)); |
| 204 } |
| 205 |
| 206 net::Error DownloadFileWithErrors::ShouldReturnError( |
| 207 content::TestFileErrorInjector::FileOperationCode code, |
| 208 net::Error original_net_error) { |
| 209 int counter = operation_counter_[code]; |
| 210 ++operation_counter_[code]; |
| 211 |
| 212 if (code != error_info_.code) |
| 213 return original_net_error; |
| 214 |
| 215 if (counter != error_info_.operation_instance) |
| 216 return original_net_error; |
| 217 |
| 218 VLOG(1) << " " << __FUNCTION__ << "()" |
| 219 << " url = '" << source_url_.spec() << "'" |
| 220 << " code = " << content::TestFileErrorInjector::DebugString(code) |
| 221 << " (" << code << ")" |
| 222 << " counter = " << counter |
| 223 << " original_error = " << net::ErrorToString(original_net_error) |
| 224 << " (" << original_net_error << ")" |
| 225 << " new error = " << net::ErrorToString(error_info_.net_error) |
| 226 << " (" << error_info_.net_error << ")"; |
| 227 |
| 228 return error_info_.net_error; |
| 229 } |
| 230 |
| 231 DownloadFileWithErrorsFactory::DownloadFileWithErrorsFactory( |
| 232 const TestFileErrorInjectorImpl::ConstructionCallback& ctor_callback, |
| 233 const TestFileErrorInjectorImpl::DestructionCallback& dtor_callback) |
| 234 : construction_callback_(ctor_callback), |
| 235 destruction_callback_(dtor_callback) { |
| 236 } |
| 237 |
| 238 DownloadFileWithErrorsFactory::~DownloadFileWithErrorsFactory() { |
| 239 } |
| 240 |
| 241 content::DownloadFile* DownloadFileWithErrorsFactory::CreateFile( |
| 242 DownloadCreateInfo* info, |
| 243 const DownloadRequestHandle& request_handle, |
| 244 content::DownloadManager* download_manager, |
| 245 bool calculate_hash, |
| 246 const net::BoundNetLog& bound_net_log) { |
| 247 // Creates injected_errors_ entry if it doesn't exist. |
| 248 return new DownloadFileWithErrors(info, |
| 249 new DownloadRequestHandle(request_handle), |
| 250 download_manager, |
| 251 calculate_hash, |
| 252 bound_net_log, |
| 253 injected_errors_[info->url().spec()], |
| 254 construction_callback_, |
| 255 destruction_callback_); |
| 256 } |
| 257 |
| 258 bool DownloadFileWithErrorsFactory::AddError( |
| 259 const content::TestFileErrorInjector::FileErrorInfo& error_info) { |
| 260 DCHECK_LE(0, error_info.operation_instance); |
| 261 |
| 262 // Creates an empty entry if necessary. Duplicate entries overwrite. |
| 263 injected_errors_[error_info.url] = error_info; |
| 264 |
| 265 return true; |
| 266 } |
| 267 |
| 268 void DownloadFileWithErrorsFactory::ClearErrors() { |
| 269 injected_errors_.clear(); |
| 270 } |
| 271 |
| 272 TestFileErrorInjectorImpl::TestFileErrorInjectorImpl() |
| 273 : created_factory_(false) { |
| 274 } |
| 275 |
| 276 TestFileErrorInjectorImpl::~TestFileErrorInjectorImpl() { |
| 277 } |
| 278 |
| 279 bool TestFileErrorInjectorImpl::AddError(const FileErrorInfo& error_info) { |
| 280 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 281 DCHECK_LE(0, error_info.operation_instance); |
| 282 DCHECK(injected_errors_.find(error_info.url) == injected_errors_.end()); |
| 283 |
| 284 // Creates an empty entry if necessary. |
| 285 injected_errors_[error_info.url] = error_info; |
| 286 |
| 287 return true; |
| 288 } |
| 289 |
| 290 void TestFileErrorInjectorImpl::ClearErrors() { |
| 291 injected_errors_.clear(); |
| 292 } |
| 293 |
| 294 bool TestFileErrorInjectorImpl::InjectErrors() { |
| 295 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 296 |
| 297 DownloadFileManager* download_file_manager = GetDownloadFileManager(); |
| 298 DCHECK(download_file_manager); |
| 299 |
| 300 if (!created_factory_) { |
| 301 scoped_ptr<DownloadFileWithErrorsFactory> download_file_factory( |
| 302 new DownloadFileWithErrorsFactory( |
| 303 base::Bind(&TestFileErrorInjectorImpl:: |
| 304 RecordDownloadFileConstruction, |
| 305 this), |
| 306 base::Bind(&TestFileErrorInjectorImpl:: |
| 307 RecordDownloadFileDestruction, |
| 308 this))); |
| 309 |
| 310 download_file_manager->SetFileFactoryForTesting( |
| 311 download_file_factory.Pass()); |
| 312 |
| 313 created_factory_ = true; |
| 314 } |
| 315 |
| 316 DownloadFileWithErrorsFactory* file_factory = |
| 317 static_cast<DownloadFileWithErrorsFactory*>( |
| 318 download_file_manager->GetFileFactoryForTesting()); |
| 319 |
| 320 file_factory->ClearErrors(); |
| 321 |
| 322 for (ErrorMap::const_iterator it = injected_errors_.begin(); |
| 323 it != injected_errors_.end(); |
| 324 ++it) { |
| 325 file_factory->AddError(it->second); |
| 326 } |
| 327 |
| 328 return true; |
| 329 } |
| 330 |
| 331 size_t TestFileErrorInjectorImpl::CurrentFileCount() const { |
| 332 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 333 return files_.size(); |
| 334 } |
| 335 |
| 336 size_t TestFileErrorInjectorImpl::TotalFileCount() const { |
| 337 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 338 return found_files_.size(); |
| 339 } |
| 340 |
| 341 |
| 342 bool TestFileErrorInjectorImpl::HadFile(const GURL& url) const { |
| 343 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 344 |
| 345 return (found_files_.find(url) != found_files_.end()); |
| 346 } |
| 347 |
| 348 const content::DownloadId TestFileErrorInjectorImpl::GetId( |
| 349 const GURL& url) const { |
| 350 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 351 |
| 352 FileMap::const_iterator it = found_files_.find(url); |
| 353 if (it == found_files_.end()) |
| 354 return content::DownloadId::Invalid(); |
| 355 |
| 356 return it->second.id; |
| 357 } |
| 358 |
| 359 void TestFileErrorInjectorImpl::ClearFoundFiles() { |
| 360 found_files_.clear(); |
| 361 } |
| 362 |
| 363 void TestFileErrorInjectorImpl::DownloadFileCreated(GURL url, |
| 364 content::DownloadId id) { |
| 365 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 366 DCHECK(files_.find(url) == files_.end()); |
| 367 |
| 368 InjectedFileInfo info; |
| 369 info.url = url; |
| 370 info.id = id; |
| 371 |
| 372 files_[url] = info; |
| 373 found_files_[url] = info; |
| 374 } |
| 375 |
| 376 void TestFileErrorInjectorImpl::DestroyingDownloadFile(GURL url) { |
| 377 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 378 |
| 379 files_.erase(url); |
| 380 } |
| 381 |
| 382 void TestFileErrorInjectorImpl::RecordDownloadFileConstruction( |
| 383 GURL url, content::DownloadId id) { |
| 384 content::BrowserThread::PostTask( |
| 385 content::BrowserThread::UI, |
| 386 FROM_HERE, |
| 387 base::Bind(&TestFileErrorInjectorImpl::DownloadFileCreated, |
| 388 this, |
| 389 url, |
| 390 id)); |
| 391 } |
| 392 |
| 393 void TestFileErrorInjectorImpl::RecordDownloadFileDestruction(GURL url) { |
| 394 content::BrowserThread::PostTask( |
| 395 content::BrowserThread::UI, |
| 396 FROM_HERE, |
| 397 base::Bind(&TestFileErrorInjectorImpl::DestroyingDownloadFile, |
| 398 this, |
| 399 url)); |
| 400 } |
| 401 |
| 402 } // namespace |
| 403 |
| 404 namespace content { |
| 405 |
| 406 // static |
| 407 scoped_refptr<TestFileErrorInjector> TestFileErrorInjector::Get() { |
| 408 scoped_refptr<TestFileErrorInjector> single_injector_; |
| 409 if (single_injector_.get() == NULL) |
| 410 single_injector_ = new TestFileErrorInjectorImpl; |
| 411 return single_injector_; |
| 412 } |
| 413 |
| 414 std::string TestFileErrorInjector::DebugString(FileOperationCode code) { |
| 415 |
| 416 #define TO_STRING(code) \ |
| 417 case TestFileErrorInjector::FILE_OPERATION_##code: return #code |
| 418 |
| 419 switch (code) { |
| 420 TO_STRING(INITIALIZE); |
| 421 TO_STRING(WRITE); |
| 422 TO_STRING(RENAME); |
| 423 default: |
| 424 break; |
| 425 } |
| 426 |
| 427 #undef TO_STRING |
| 428 |
| 429 return "Unknown"; |
| 430 } |
| 431 |
| 432 } // namespace content |
| OLD | NEW |