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

Side by Side 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 & Randy's comments. Created 8 years, 9 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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/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/browser/download/download_create_info.h"
13 #include "content/browser/download/download_file_impl.h"
14 #include "content/browser/download/download_file_manager.h"
15 #include "content/browser/renderer_host/resource_dispatcher_host.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/download_id.h"
18 #include "googleurl/src/gurl.h"
19
20 namespace {
21
22 typedef std::map<std::string, content::TestFileErrorInjector::FileErrorInfo>
23 ErrorMap;
24
25 DownloadFileManager* GetDownloadFileManager() {
26 ResourceDispatcherHost* rdh = ResourceDispatcherHost::Get();
27 DCHECK(rdh != NULL);
28 return rdh->download_file_manager();
29 }
30
31 // A class that performs file operations and injects errors.
32 class DownloadFileWithErrors: public DownloadFileImpl {
33 public:
34 typedef base::Callback<void(GURL url, content::DownloadId id)>
35 ConstructionCallback;
36 typedef base::Callback<void(GURL url)> DestructionCallback;
37
38 DownloadFileWithErrors(
39 const DownloadCreateInfo* info,
40 DownloadRequestHandleInterface* request_handle,
41 content::DownloadManager* download_manager,
42 bool calculate_hash,
43 const net::BoundNetLog& bound_net_log,
44 const content::TestFileErrorInjector::FileErrorInfo& error_info,
45 const ConstructionCallback& ctor_callback,
46 const DestructionCallback& dtor_callback);
47
48 ~DownloadFileWithErrors();
49
50 // DownloadFile interface.
51 virtual net::Error Initialize() OVERRIDE;
52 virtual net::Error AppendDataToFile(const char* data,
53 size_t data_len) OVERRIDE;
54 virtual net::Error Rename(const FilePath& full_path) OVERRIDE;
55
56 private:
57 // Error generating helper.
58 net::Error ShouldReturnError(
59 content::TestFileErrorInjector::FileOperationCode code,
60 net::Error original_net_error);
61
62 // Source URL for the file being downloaded.
63 GURL source_url_;
64
65 // Our injected error. Only one per file.
66 content::TestFileErrorInjector::FileErrorInfo error_info_;
67
68 // Count per operation. 0-based.
69 std::map<content::TestFileErrorInjector::FileOperationCode, int>
70 operation_counter_;
71
72 // Callback for destruction.
73 DestructionCallback destruction_callback_;
74 };
75
76 // A factory for constructing DownloadFiles that inject errors.
77 class DownloadFileWithErrorsFactory
78 : public DownloadFileManager::DownloadFileFactory {
79 public:
80
81 DownloadFileWithErrorsFactory(
82 const DownloadFileWithErrors::ConstructionCallback& ctor_callback,
83 const DownloadFileWithErrors::DestructionCallback& dtor_callback);
84 virtual ~DownloadFileWithErrorsFactory();
85
86 // DownloadFileFactory interface.
87 virtual content::DownloadFile* CreateFile(
88 DownloadCreateInfo* info,
89 const DownloadRequestHandle& request_handle,
90 content::DownloadManager* download_manager,
91 bool calculate_hash,
92 const net::BoundNetLog& bound_net_log);
93
94 bool AddError(
95 const content::TestFileErrorInjector::FileErrorInfo& error_info);
96
97 void ClearErrors();
98
99 private:
100 // Our injected error list, mapped by URL. One per file.
101 ErrorMap injected_errors_;
102
103 // Callback for creation and destruction.
104 DownloadFileWithErrors::ConstructionCallback construction_callback_;
105 DownloadFileWithErrors::DestructionCallback destruction_callback_;
106 };
107
108 class TestFileErrorInjectorImpl : public content::TestFileErrorInjector {
109 public:
110 TestFileErrorInjectorImpl();
111
112 // content::TestFileErrorInjector interface.
113 virtual bool AddError(const FileErrorInfo& error_info) OVERRIDE;
114 virtual void ClearErrors() OVERRIDE;
115 virtual bool InjectErrors() OVERRIDE;
116 virtual size_t CurrentFileCount() const OVERRIDE;
117 virtual size_t TotalFileCount() const OVERRIDE;
118 virtual bool HadFile(const GURL& url) const OVERRIDE;
119 virtual const content::DownloadId GetId(const GURL& url) const OVERRIDE;
120 virtual void ClearFoundFiles() OVERRIDE;
121
122 private:
123 typedef std::map<GURL, content::DownloadId> FileMap;
124
125 virtual ~TestFileErrorInjectorImpl();
126
127 void AddFactory(scoped_ptr<DownloadFileManager::DownloadFileFactory> factory);
128
129 void InjectErrorsOnFileThread(ErrorMap map);
130
131 // Callbacks from the download file, to record lifetimes.
132 // These may be called on any thread.
133 void RecordDownloadFileConstruction(GURL url, content::DownloadId id);
cbentzel 2012/03/08 20:02:55 Nit: these should probably take const GURL&
ahendrickson 2012/03/09 16:58:05 Done.
134 void RecordDownloadFileDestruction(GURL url);
135
136 // These run on the UI thread.
137 void DownloadFileCreated(GURL url, content::DownloadId id);
138 void DestroyingDownloadFile(GURL url);
139
140 // All the data is used on the UI thread.
141 // Our injected error list, mapped by URL. One per file.
142 ErrorMap injected_errors_;
143
144 // Keep track of active DownloadFiles.
145 FileMap files_;
146
147 // Keep track of found DownloadFiles.
148 FileMap found_files_;
149
150 // The factory we created. USED FOR VALIDATION ONLY.
151 DownloadFileManager::DownloadFileFactory* created_factory_;
152
153 DISALLOW_COPY_AND_ASSIGN(TestFileErrorInjectorImpl);
154 };
155
156 // Implementations.
157
158 DownloadFileWithErrors::DownloadFileWithErrors(
159 const DownloadCreateInfo* info,
160 DownloadRequestHandleInterface* request_handle,
161 content::DownloadManager* download_manager,
162 bool calculate_hash,
163 const net::BoundNetLog& bound_net_log,
164 const content::TestFileErrorInjector::FileErrorInfo& error_info,
165 const ConstructionCallback& ctor_callback,
166 const DestructionCallback& dtor_callback)
167 : DownloadFileImpl(info,
168 request_handle,
169 download_manager,
170 calculate_hash,
171 bound_net_log),
172 source_url_(info->url()),
173 error_info_(error_info),
174 destruction_callback_(dtor_callback) {
175 ctor_callback.Run(source_url_, info->download_id);
176 }
177
178 DownloadFileWithErrors::~DownloadFileWithErrors() {
179 destruction_callback_.Run(source_url_);
180 }
181
182
cbentzel 2012/03/08 20:02:55 Nit: extra newline
ahendrickson 2012/03/09 16:58:05 Done.
183 net::Error DownloadFileWithErrors::Initialize() {
184 return ShouldReturnError(
185 content::TestFileErrorInjector::FILE_OPERATION_INITIALIZE,
186 DownloadFileImpl::Initialize());
187 }
188
189 net::Error DownloadFileWithErrors::AppendDataToFile(const char* data,
190 size_t data_len) {
191 return ShouldReturnError(
192 content::TestFileErrorInjector::FILE_OPERATION_WRITE,
193 DownloadFileImpl::AppendDataToFile(data, data_len));
194 }
195
196 net::Error DownloadFileWithErrors::Rename(const FilePath& full_path) {
197 return ShouldReturnError(
198 content::TestFileErrorInjector::FILE_OPERATION_RENAME,
199 DownloadFileImpl::Rename(full_path));
200 }
201
202 net::Error DownloadFileWithErrors::ShouldReturnError(
203 content::TestFileErrorInjector::FileOperationCode code,
204 net::Error original_net_error) {
205 int counter = operation_counter_[code];
206 ++operation_counter_[code];
207
208 if (code != error_info_.code)
209 return original_net_error;
210
211 if (counter != error_info_.operation_instance)
212 return original_net_error;
213
214 VLOG(1) << " " << __FUNCTION__ << "()"
215 << " url = '" << source_url_.spec() << "'"
216 << " code = " << content::TestFileErrorInjector::DebugString(code)
217 << " (" << code << ")"
218 << " counter = " << counter
219 << " original_error = " << net::ErrorToString(original_net_error)
220 << " (" << original_net_error << ")"
221 << " new error = " << net::ErrorToString(error_info_.net_error)
222 << " (" << error_info_.net_error << ")";
223
224 return error_info_.net_error;
225 }
226
227 DownloadFileWithErrorsFactory::DownloadFileWithErrorsFactory(
228 const DownloadFileWithErrors::ConstructionCallback& ctor_callback,
229 const DownloadFileWithErrors::DestructionCallback& dtor_callback)
230 : construction_callback_(ctor_callback),
231 destruction_callback_(dtor_callback) {
232 }
233
234 DownloadFileWithErrorsFactory::~DownloadFileWithErrorsFactory() {
235 }
236
237 content::DownloadFile* DownloadFileWithErrorsFactory::CreateFile(
238 DownloadCreateInfo* info,
239 const DownloadRequestHandle& request_handle,
240 content::DownloadManager* download_manager,
241 bool calculate_hash,
242 const net::BoundNetLog& bound_net_log) {
243 std::string url = info->url().spec();
244
245 if (injected_errors_.find(url) == injected_errors_.end()) {
246 // Have to create entry, because FileErrorInfo is not a POD type.
247 content::TestFileErrorInjector::FileErrorInfo err_info = {
248 url,
249 content::TestFileErrorInjector::FILE_OPERATION_INITIALIZE,
250 -1,
251 net::OK
252 };
253 injected_errors_[url] = err_info;
254 }
255
256 return new DownloadFileWithErrors(info,
257 new DownloadRequestHandle(request_handle),
258 download_manager,
259 calculate_hash,
260 bound_net_log,
261 injected_errors_[url],
262 construction_callback_,
263 destruction_callback_);
264 }
265
266 bool DownloadFileWithErrorsFactory::AddError(
267 const content::TestFileErrorInjector::FileErrorInfo& error_info) {
268 // Creates an empty entry if necessary. Duplicate entries overwrite.
269 injected_errors_[error_info.url] = error_info;
270
271 return true;
272 }
273
274 void DownloadFileWithErrorsFactory::ClearErrors() {
275 injected_errors_.clear();
276 }
277
278 TestFileErrorInjectorImpl::TestFileErrorInjectorImpl()
279 : created_factory_(NULL) {
280 scoped_ptr<DownloadFileManager::DownloadFileFactory> download_file_factory(
281 new DownloadFileWithErrorsFactory(
282 base::Bind(&TestFileErrorInjectorImpl::
283 RecordDownloadFileConstruction,
284 this),
285 base::Bind(&TestFileErrorInjectorImpl::
286 RecordDownloadFileDestruction,
287 this)));
288
289 // Record the value of the pointer, for later validation.
290 created_factory_ = download_file_factory.get();
291
292 content::BrowserThread::PostTask(
cbentzel 2012/03/08 20:02:55 Why not pass this down at the same time that you d
ahendrickson 2012/03/12 11:51:02 Done.
293 content::BrowserThread::FILE,
294 FROM_HERE,
295 base::Bind(&TestFileErrorInjectorImpl::AddFactory,
296 this,
297 base::Passed(&download_file_factory)));
298 }
299
300 TestFileErrorInjectorImpl::~TestFileErrorInjectorImpl() {
301 }
302
303 void TestFileErrorInjectorImpl::AddFactory(
304 scoped_ptr<DownloadFileManager::DownloadFileFactory> factory) {
305 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
306
307 DownloadFileManager* download_file_manager = GetDownloadFileManager();
308 DCHECK(download_file_manager);
309
310 download_file_manager->SetFileFactoryForTesting(factory.Pass());
311 }
312
313 bool TestFileErrorInjectorImpl::AddError(const FileErrorInfo& error_info) {
314 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
315 DCHECK_LE(0, error_info.operation_instance);
316 DCHECK(injected_errors_.find(error_info.url) == injected_errors_.end());
317
318 // Creates an empty entry if necessary.
319 injected_errors_[error_info.url] = error_info;
320
321 return true;
322 }
323
324 void TestFileErrorInjectorImpl::ClearErrors() {
325 injected_errors_.clear();
cbentzel 2012/03/08 20:02:55 Maybe a CurrentlyOn check here.
ahendrickson 2012/03/09 16:58:05 Done.
326 }
327
328 bool TestFileErrorInjectorImpl::InjectErrors() {
329 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
330
331 ClearFoundFiles();
332
333 content::BrowserThread::PostTask(
334 content::BrowserThread::FILE,
335 FROM_HERE,
336 base::Bind(&TestFileErrorInjectorImpl::InjectErrorsOnFileThread,
337 this,
338 injected_errors_));
339
340 return true;
341 }
342
343 void TestFileErrorInjectorImpl::InjectErrorsOnFileThread(ErrorMap map) {
344 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
345
346 DownloadFileManager* download_file_manager = GetDownloadFileManager();
347 DCHECK(download_file_manager);
348
349 DownloadFileWithErrorsFactory* file_factory =
350 static_cast<DownloadFileWithErrorsFactory*>(
351 download_file_manager->GetFileFactoryForTesting());
352
353 // Validate that we still have the same factory.
354 DCHECK_EQ(created_factory_, file_factory);
355
356 // We want to replace all existing injection errors.
357 file_factory->ClearErrors();
358
359 for (ErrorMap::const_iterator it = map.begin(); it != map.end(); ++it)
360 file_factory->AddError(it->second);
361 }
362
363 size_t TestFileErrorInjectorImpl::CurrentFileCount() const {
364 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
365 return files_.size();
366 }
367
368 size_t TestFileErrorInjectorImpl::TotalFileCount() const {
369 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
370 return found_files_.size();
371 }
372
373
374 bool TestFileErrorInjectorImpl::HadFile(const GURL& url) const {
375 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
376
377 return (found_files_.find(url) != found_files_.end());
378 }
379
380 const content::DownloadId TestFileErrorInjectorImpl::GetId(
381 const GURL& url) const {
382 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
383
384 FileMap::const_iterator it = found_files_.find(url);
385 if (it == found_files_.end())
386 return content::DownloadId::Invalid();
387
388 return it->second;
389 }
390
391 void TestFileErrorInjectorImpl::ClearFoundFiles() {
392 found_files_.clear();
393 }
394
395 void TestFileErrorInjectorImpl::DownloadFileCreated(GURL url,
396 content::DownloadId id) {
397 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
398 DCHECK(files_.find(url) == files_.end());
399
400 files_[url] = id;
401 found_files_[url] = id;
402 }
403
404 void TestFileErrorInjectorImpl::DestroyingDownloadFile(GURL url) {
405 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
406 DCHECK(files_.find(url) != files_.end());
407
408 files_.erase(url);
409 }
410
411 void TestFileErrorInjectorImpl::RecordDownloadFileConstruction(
412 GURL url, content::DownloadId id) {
413 content::BrowserThread::PostTask(
414 content::BrowserThread::UI,
415 FROM_HERE,
416 base::Bind(&TestFileErrorInjectorImpl::DownloadFileCreated,
417 this,
418 url,
419 id));
420 }
421
422 void TestFileErrorInjectorImpl::RecordDownloadFileDestruction(GURL url) {
423 content::BrowserThread::PostTask(
424 content::BrowserThread::UI,
425 FROM_HERE,
426 base::Bind(&TestFileErrorInjectorImpl::DestroyingDownloadFile,
427 this,
428 url));
429 }
430
431 } // namespace
432
433 namespace content {
434
435 TestFileErrorInjector::~TestFileErrorInjector() {
436 }
437
438 // static
439 scoped_refptr<TestFileErrorInjector> TestFileErrorInjector::Create() {
440 static bool visited = false;
441 DCHECK(!visited); // Only allowed to be called once.
cbentzel 2012/03/08 20:02:55 In this case, why do we care about it having singl
ahendrickson 2012/03/09 16:58:05 Because there can only be one DownloadFileFactory
442 visited = true;
443
444 scoped_refptr<TestFileErrorInjector> single_injector(
445 new TestFileErrorInjectorImpl);
446
447 return single_injector;
448 }
449
450 std::string TestFileErrorInjector::DebugString(FileOperationCode code) {
451
452 #define TO_STRING(code) \
cbentzel 2012/03/08 20:02:55 I'd remove this macro - generally against the styl
ahendrickson 2012/03/09 16:58:05 Done.
453 case TestFileErrorInjector::FILE_OPERATION_##code: return #code
454
455 switch (code) {
456 TO_STRING(INITIALIZE);
457 TO_STRING(WRITE);
458 TO_STRING(RENAME);
459 default:
460 break;
461 }
462
463 #undef TO_STRING
464
465 return "Unknown";
466 }
467
468 } // namespace content
OLDNEW
« 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