test_file_error_injector.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
1// Copyright (c) 2012 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/public/test/test_file_error_injector.h" 6 7#include <vector> 8 9#include "base/compiler_specific.h" 10#include "base/logging.h" 11#include "content/browser/download/download_file_impl.h" 12#include "content/browser/download/download_file_factory.h" 13#include "content/browser/download/download_interrupt_reasons_impl.h" 14#include "content/browser/download/download_manager_impl.h" 15#include "content/browser/power_save_blocker.h" 16#include "content/browser/renderer_host/resource_dispatcher_host_impl.h" 17#include "content/public/browser/browser_thread.h" 18#include "googleurl/src/gurl.h" 19 20namespace content { 21class ByteStreamReader; 22 23namespace { 24 25// A class that performs file operations and injects errors. 26class DownloadFileWithErrors: public DownloadFileImpl { 27 public: 28 typedef base::Callback<void(const GURL& url)> ConstructionCallback; 29 typedef base::Callback<void(const GURL& url)> DestructionCallback; 30 31 DownloadFileWithErrors( 32 scoped_ptr<DownloadSaveInfo> save_info, 33 const FilePath& default_download_directory, 34 const GURL& url, 35 const GURL& referrer_url, 36 bool calculate_hash, 37 scoped_ptr<ByteStreamReader> stream, 38 const net::BoundNetLog& bound_net_log, 39 scoped_ptr<PowerSaveBlocker> power_save_blocker, 40 base::WeakPtr<DownloadDestinationObserver> observer, 41 const TestFileErrorInjector::FileErrorInfo& error_info, 42 const ConstructionCallback& ctor_callback, 43 const DestructionCallback& dtor_callback); 44 45 ~DownloadFileWithErrors(); 46 47 virtual void Initialize(const InitializeCallback& callback) OVERRIDE; 48 49 // DownloadFile interface. 50 virtual DownloadInterruptReason AppendDataToFile( 51 const char* data, size_t data_len) OVERRIDE; 52 virtual void RenameAndUniquify( 53 const FilePath& full_path, 54 const RenameCompletionCallback& callback) OVERRIDE; 55 virtual void RenameAndAnnotate( 56 const FilePath& full_path, 57 const RenameCompletionCallback& callback) OVERRIDE; 58 59 private: 60 // Error generating helper. 61 DownloadInterruptReason ShouldReturnError( 62 TestFileErrorInjector::FileOperationCode code, 63 DownloadInterruptReason original_error); 64 65 // Determine whether to overwrite an operation with the given code 66 // with a substitute error; if returns true, |*original_error| is 67 // written with the error to use for overwriting. 68 // NOTE: This routine changes state; specifically, it increases the 69 // operations counts for the specified code. It should only be called 70 // once per operation. 71 bool OverwriteError( 72 TestFileErrorInjector::FileOperationCode code, 73 DownloadInterruptReason* output_error); 74 75 // Source URL for the file being downloaded. 76 GURL source_url_; 77 78 // Our injected error. Only one per file. 79 TestFileErrorInjector::FileErrorInfo error_info_; 80 81 // Count per operation. 0-based. 82 std::map<TestFileErrorInjector::FileOperationCode, int> operation_counter_; 83 84 // Callback for destruction. 85 DestructionCallback destruction_callback_; 86}; 87 88static void InitializeErrorCallback( 89 const DownloadFile::InitializeCallback original_callback, 90 DownloadInterruptReason overwrite_error, 91 DownloadInterruptReason original_error) { 92 original_callback.Run(overwrite_error); 93} 94 95static void RenameErrorCallback( 96 const DownloadFile::RenameCompletionCallback original_callback, 97 DownloadInterruptReason overwrite_error, 98 DownloadInterruptReason original_error, 99 const FilePath& path_result) { 100 original_callback.Run( 101 overwrite_error, 102 overwrite_error == DOWNLOAD_INTERRUPT_REASON_NONE ? 103 path_result : FilePath()); 104} 105 106DownloadFileWithErrors::DownloadFileWithErrors( 107 scoped_ptr<DownloadSaveInfo> save_info, 108 const FilePath& default_download_directory, 109 const GURL& url, 110 const GURL& referrer_url, 111 bool calculate_hash, 112 scoped_ptr<ByteStreamReader> stream, 113 const net::BoundNetLog& bound_net_log, 114 scoped_ptr<PowerSaveBlocker> power_save_blocker, 115 base::WeakPtr<DownloadDestinationObserver> observer, 116 const TestFileErrorInjector::FileErrorInfo& error_info, 117 const ConstructionCallback& ctor_callback, 118 const DestructionCallback& dtor_callback) 119 : DownloadFileImpl( 120 save_info.Pass(), default_download_directory, url, referrer_url, 121 calculate_hash, stream.Pass(), bound_net_log, 122 power_save_blocker.Pass(), observer), 123 source_url_(url), 124 error_info_(error_info), 125 destruction_callback_(dtor_callback) { 126 ctor_callback.Run(source_url_); 127} 128 129DownloadFileWithErrors::~DownloadFileWithErrors() { 130 destruction_callback_.Run(source_url_); 131} 132 133void DownloadFileWithErrors::Initialize( 134 const InitializeCallback& callback) { 135 DownloadInterruptReason error_to_return = DOWNLOAD_INTERRUPT_REASON_NONE; 136 InitializeCallback callback_to_use = callback; 137 138 // Replace callback if the error needs to be overwritten. 139 if (OverwriteError( 140 TestFileErrorInjector::FILE_OPERATION_INITIALIZE, 141 &error_to_return)) { 142 callback_to_use = base::Bind(&InitializeErrorCallback, callback, 143 error_to_return); 144 } 145 146 DownloadFileImpl::Initialize(callback_to_use); 147} 148 149DownloadInterruptReason DownloadFileWithErrors::AppendDataToFile( 150 const char* data, size_t data_len) { 151 return ShouldReturnError( 152 TestFileErrorInjector::FILE_OPERATION_WRITE, 153 DownloadFileImpl::AppendDataToFile(data, data_len)); 154} 155 156void DownloadFileWithErrors::RenameAndUniquify( 157 const FilePath& full_path, 158 const RenameCompletionCallback& callback) { 159 DownloadInterruptReason error_to_return = DOWNLOAD_INTERRUPT_REASON_NONE; 160 RenameCompletionCallback callback_to_use = callback; 161 162 // Replace callback if the error needs to be overwritten. 163 if (OverwriteError( 164 TestFileErrorInjector::FILE_OPERATION_RENAME_UNIQUIFY, 165 &error_to_return)) { 166 callback_to_use = base::Bind(&RenameErrorCallback, callback, 167 error_to_return); 168 } 169 170 DownloadFileImpl::RenameAndUniquify(full_path, callback_to_use); 171} 172 173void DownloadFileWithErrors::RenameAndAnnotate( 174 const FilePath& full_path, 175 const RenameCompletionCallback& callback) { 176 DownloadInterruptReason error_to_return = DOWNLOAD_INTERRUPT_REASON_NONE; 177 RenameCompletionCallback callback_to_use = callback; 178 179 // Replace callback if the error needs to be overwritten. 180 if (OverwriteError( 181 TestFileErrorInjector::FILE_OPERATION_RENAME_ANNOTATE, 182 &error_to_return)) { 183 callback_to_use = base::Bind(&RenameErrorCallback, callback, 184 error_to_return); 185 } 186 187 DownloadFileImpl::RenameAndAnnotate(full_path, callback_to_use); 188} 189 190bool DownloadFileWithErrors::OverwriteError( 191 TestFileErrorInjector::FileOperationCode code, 192 DownloadInterruptReason* output_error) { 193 int counter = operation_counter_[code]++; 194 195 if (code != error_info_.code) 196 return false; 197 198 if (counter != error_info_.operation_instance) 199 return false; 200 201 *output_error = error_info_.error; 202 return true; 203} 204 205DownloadInterruptReason DownloadFileWithErrors::ShouldReturnError( 206 TestFileErrorInjector::FileOperationCode code, 207 DownloadInterruptReason original_error) { 208 DownloadInterruptReason output_error = original_error; 209 OverwriteError(code, &output_error); 210 return output_error; 211} 212 213} // namespace 214 215// A factory for constructing DownloadFiles that inject errors. 216class DownloadFileWithErrorsFactory : public DownloadFileFactory { 217 public: 218 DownloadFileWithErrorsFactory( 219 const DownloadFileWithErrors::ConstructionCallback& ctor_callback, 220 const DownloadFileWithErrors::DestructionCallback& dtor_callback); 221 virtual ~DownloadFileWithErrorsFactory(); 222 223 // DownloadFileFactory interface. 224 virtual DownloadFile* CreateFile( 225 scoped_ptr<DownloadSaveInfo> save_info, 226 const FilePath& default_download_directory, 227 const GURL& url, 228 const GURL& referrer_url, 229 bool calculate_hash, 230 scoped_ptr<ByteStreamReader> stream, 231 const net::BoundNetLog& bound_net_log, 232 base::WeakPtr<DownloadDestinationObserver> observer) OVERRIDE; 233 234 bool AddError( 235 const TestFileErrorInjector::FileErrorInfo& error_info); 236 237 void ClearErrors(); 238 239 private: 240 // Our injected error list, mapped by URL. One per file. 241 TestFileErrorInjector::ErrorMap injected_errors_; 242 243 // Callback for creation and destruction. 244 DownloadFileWithErrors::ConstructionCallback construction_callback_; 245 DownloadFileWithErrors::DestructionCallback destruction_callback_; 246}; 247 248DownloadFileWithErrorsFactory::DownloadFileWithErrorsFactory( 249 const DownloadFileWithErrors::ConstructionCallback& ctor_callback, 250 const DownloadFileWithErrors::DestructionCallback& dtor_callback) 251 : construction_callback_(ctor_callback), 252 destruction_callback_(dtor_callback) { 253} 254 255DownloadFileWithErrorsFactory::~DownloadFileWithErrorsFactory() { 256} 257 258DownloadFile* DownloadFileWithErrorsFactory::CreateFile( 259 scoped_ptr<DownloadSaveInfo> save_info, 260 const FilePath& default_download_directory, 261 const GURL& url, 262 const GURL& referrer_url, 263 bool calculate_hash, 264 scoped_ptr<ByteStreamReader> stream, 265 const net::BoundNetLog& bound_net_log, 266 base::WeakPtr<DownloadDestinationObserver> observer) { 267 if (injected_errors_.find(url.spec()) == injected_errors_.end()) { 268 // Have to create entry, because FileErrorInfo is not a POD type. 269 TestFileErrorInjector::FileErrorInfo err_info = { 270 url.spec(), 271 TestFileErrorInjector::FILE_OPERATION_INITIALIZE, 272 -1, 273 DOWNLOAD_INTERRUPT_REASON_NONE 274 }; 275 injected_errors_[url.spec()] = err_info; 276 } 277 278 scoped_ptr<PowerSaveBlocker> psb( 279 new PowerSaveBlocker( 280 PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension, 281 "Download in progress")); 282 283 return new DownloadFileWithErrors( 284 save_info.Pass(), 285 default_download_directory, 286 url, 287 referrer_url, 288 calculate_hash, 289 stream.Pass(), 290 bound_net_log, 291 psb.Pass(), 292 observer, 293 injected_errors_[url.spec()], 294 construction_callback_, 295 destruction_callback_); 296} 297 298bool DownloadFileWithErrorsFactory::AddError( 299 const TestFileErrorInjector::FileErrorInfo& error_info) { 300 // Creates an empty entry if necessary. Duplicate entries overwrite. 301 injected_errors_[error_info.url] = error_info; 302 303 return true; 304} 305 306void DownloadFileWithErrorsFactory::ClearErrors() { 307 injected_errors_.clear(); 308} 309 310TestFileErrorInjector::TestFileErrorInjector( 311 scoped_refptr<DownloadManager> download_manager) 312 : created_factory_(NULL), 313 // This code is only used for browser_tests, so a 314 // DownloadManager is always a DownloadManagerImpl. 315 download_manager_( 316 static_cast<DownloadManagerImpl*>(download_manager.release())) { 317 // Record the value of the pointer, for later validation. 318 created_factory_ = 319 new DownloadFileWithErrorsFactory( 320 base::Bind(&TestFileErrorInjector::RecordDownloadFileConstruction, 321 this), 322 base::Bind(&TestFileErrorInjector::RecordDownloadFileDestruction, 323 this)); 324 325 // We will transfer ownership of the factory to the download manager. 326 scoped_ptr<DownloadFileFactory> download_file_factory( 327 created_factory_); 328 329 download_manager_->SetDownloadFileFactoryForTesting( 330 download_file_factory.Pass()); 331} 332 333TestFileErrorInjector::~TestFileErrorInjector() { 334} 335 336bool TestFileErrorInjector::AddError(const FileErrorInfo& error_info) { 337 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 338 DCHECK_LE(0, error_info.operation_instance); 339 DCHECK(injected_errors_.find(error_info.url) == injected_errors_.end()); 340 341 // Creates an empty entry if necessary. 342 injected_errors_[error_info.url] = error_info; 343 344 return true; 345} 346 347void TestFileErrorInjector::ClearErrors() { 348 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 349 injected_errors_.clear(); 350} 351 352bool TestFileErrorInjector::InjectErrors() { 353 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 354 355 ClearFoundFiles(); 356 357 DCHECK_EQ(static_cast<DownloadFileFactory*>(created_factory_), 358 download_manager_->GetDownloadFileFactoryForTesting()); 359 360 created_factory_->ClearErrors(); 361 362 for (ErrorMap::const_iterator it = injected_errors_.begin(); 363 it != injected_errors_.end(); ++it) 364 created_factory_->AddError(it->second); 365 366 return true; 367} 368 369size_t TestFileErrorInjector::CurrentFileCount() const { 370 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 371 return files_.size(); 372} 373 374size_t TestFileErrorInjector::TotalFileCount() const { 375 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 376 return found_files_.size(); 377} 378 379 380bool TestFileErrorInjector::HadFile(const GURL& url) const { 381 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 382 383 return (found_files_.find(url) != found_files_.end()); 384} 385 386void TestFileErrorInjector::ClearFoundFiles() { 387 found_files_.clear(); 388} 389 390void TestFileErrorInjector::DownloadFileCreated(GURL url) { 391 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 392 DCHECK(files_.find(url) == files_.end()); 393 394 files_.insert(url); 395 found_files_.insert(url); 396} 397 398void TestFileErrorInjector::DestroyingDownloadFile(GURL url) { 399 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 400 DCHECK(files_.find(url) != files_.end()); 401 402 files_.erase(url); 403} 404 405void TestFileErrorInjector::RecordDownloadFileConstruction(const GURL& url) { 406 BrowserThread::PostTask( 407 BrowserThread::UI, 408 FROM_HERE, 409 base::Bind(&TestFileErrorInjector::DownloadFileCreated, this, url)); 410} 411 412void TestFileErrorInjector::RecordDownloadFileDestruction(const GURL& url) { 413 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 414 base::Bind(&TestFileErrorInjector::DestroyingDownloadFile, this, url)); 415} 416 417// static 418scoped_refptr<TestFileErrorInjector> TestFileErrorInjector::Create( 419 scoped_refptr<DownloadManager> download_manager) { 420 static bool visited = false; 421 DCHECK(!visited); // Only allowed to be called once. 422 visited = true; 423 424 scoped_refptr<TestFileErrorInjector> single_injector( 425 new TestFileErrorInjector(download_manager)); 426 427 return single_injector; 428} 429 430// static 431std::string TestFileErrorInjector::DebugString(FileOperationCode code) { 432 switch (code) { 433 case FILE_OPERATION_INITIALIZE: 434 return "INITIALIZE"; 435 case FILE_OPERATION_WRITE: 436 return "WRITE"; 437 case FILE_OPERATION_RENAME_UNIQUIFY: 438 return "RENAME_UNIQUIFY"; 439 case FILE_OPERATION_RENAME_ANNOTATE: 440 return "RENAME_ANNOTATE"; 441 default: 442 break; 443 } 444 445 return "Unknown"; 446} 447 448} // namespace content 449