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

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: Added test that injects an error on the 2nd write. 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/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 TestFileErrorInjectorImpl();
40
41 // content::TestFileErrorInjector interface.
42 virtual bool AddError(const FileErrorInfo& error_info) OVERRIDE;
43 virtual void ClearErrors() OVERRIDE;
44 virtual bool InjectErrors() OVERRIDE;
45 virtual size_t CurrentFileCount() const OVERRIDE;
46 virtual size_t TotalFileCount() const OVERRIDE;
47 virtual bool HadFile(const GURL& url) const OVERRIDE;
48 virtual const content::DownloadId GetId(const GURL& url) const OVERRIDE;
49 virtual void ClearFoundFiles() OVERRIDE;
50
51 private:
52 typedef std::map<GURL, content::DownloadId> FileMap;
53
54 virtual ~TestFileErrorInjectorImpl();
55
56 void RecordDownloadFileConstruction(GURL url, content::DownloadId id);
57 void RecordDownloadFileDestruction(GURL url);
58
59 // Callbacks from the download file, to record lifetimes.
60 void DownloadFileCreated(GURL url, content::DownloadId id);
61 void DestroyingDownloadFile(GURL url);
62
63 // Our injected error list, mapped by file index. One per file.
64 ErrorMap injected_errors_;
65
66 // Keep track of active DownloadFiles.
67 FileMap files_;
68
69 // Keep track of found DownloadFiles.
70 FileMap found_files_;
71
72 // We have created a factory. Used for validation.
73 bool created_factory_;
74
75 DISALLOW_COPY_AND_ASSIGN(TestFileErrorInjectorImpl);
76 };
77
78 // A class that performs file operations and injects errors.
79 class DownloadFileWithErrors: public DownloadFileImpl {
80 public:
81 DownloadFileWithErrors(
82 const DownloadCreateInfo* info,
83 DownloadRequestHandleInterface* request_handle,
84 content::DownloadManager* download_manager,
85 bool calculate_hash,
86 const net::BoundNetLog& bound_net_log,
87 const content::TestFileErrorInjector::FileErrorInfo& error_info,
88 const TestFileErrorInjectorImpl::ConstructionCallback& ctor_callback,
89 const TestFileErrorInjectorImpl::DestructionCallback& dtor_callback);
90
91 ~DownloadFileWithErrors();
92
93 // DownloadFile interface.
94 virtual net::Error Initialize() OVERRIDE;
95 virtual net::Error AppendDataToFile(const char* data,
96 size_t data_len) OVERRIDE;
97 virtual net::Error Rename(const FilePath& full_path) OVERRIDE;
98
99 private:
100 // Error generating helper.
101 net::Error ShouldReturnError(
102 content::TestFileErrorInjector::FileOperationCode code,
103 net::Error original_net_error);
104
105 // Source URL for the file being downloaded.
106 GURL source_url_;
107
108 // Our injected error. Only one per file.
109 content::TestFileErrorInjector::FileErrorInfo error_info_;
110
111 // Count per operation. 0-based.
112 std::map<content::TestFileErrorInjector::FileOperationCode, int>
113 operation_counter_;
114
115 // Callback for destruction.
116 TestFileErrorInjectorImpl::DestructionCallback destruction_callback_;
117 };
118
119 // A factory for constructing DownloadFiles that inject errors.
120 class DownloadFileWithErrorsFactory
121 : public DownloadFileManager::DownloadFileFactory {
122 public:
123
124 DownloadFileWithErrorsFactory(
125 const TestFileErrorInjectorImpl::ConstructionCallback& ctor_callback,
126 const TestFileErrorInjectorImpl::DestructionCallback& dtor_callback);
127 virtual ~DownloadFileWithErrorsFactory();
128
129 // DownloadFileFactory interface.
130 virtual content::DownloadFile* CreateFile(
131 DownloadCreateInfo* info,
132 const DownloadRequestHandle& request_handle,
133 content::DownloadManager* download_manager,
134 bool calculate_hash,
135 const net::BoundNetLog& bound_net_log);
136
137 bool AddError(
138 const content::TestFileErrorInjector::FileErrorInfo& error_info);
139
140 void ClearErrors();
141
142 private:
143 // Our injected error list, mapped by file index. One per file.
144 ErrorMap injected_errors_;
145
146 // Callback for creation and destruction.
147 TestFileErrorInjectorImpl::ConstructionCallback construction_callback_;
148 TestFileErrorInjectorImpl::DestructionCallback destruction_callback_;
149 };
150
151 // Implementations.
152
153 DownloadFileWithErrors::DownloadFileWithErrors(
154 const DownloadCreateInfo* info,
155 DownloadRequestHandleInterface* request_handle,
156 content::DownloadManager* download_manager,
157 bool calculate_hash,
158 const net::BoundNetLog& bound_net_log,
159 const content::TestFileErrorInjector::FileErrorInfo& error_info,
160 const TestFileErrorInjectorImpl::ConstructionCallback& ctor_callback,
161 const TestFileErrorInjectorImpl::DestructionCallback& dtor_callback)
162 : DownloadFileImpl(info,
163 request_handle,
164 download_manager,
165 calculate_hash,
166 bound_net_log),
167 source_url_(info->url()),
168 error_info_(error_info),
169 destruction_callback_(dtor_callback) {
170 ctor_callback.Run(source_url_, info->download_id);
171 }
172
173 DownloadFileWithErrors::~DownloadFileWithErrors() {
174 destruction_callback_.Run(source_url_);
175 }
176
177
178 net::Error DownloadFileWithErrors::Initialize() {
179 return ShouldReturnError(
180 content::TestFileErrorInjector::FILE_OPERATION_INITIALIZE,
181 DownloadFileImpl::Initialize());
182 }
183
184 net::Error DownloadFileWithErrors::AppendDataToFile(const char* data,
185 size_t data_len) {
186 return ShouldReturnError(
187 content::TestFileErrorInjector::FILE_OPERATION_WRITE,
188 DownloadFileImpl::AppendDataToFile(data, data_len));
189 }
190
191 net::Error DownloadFileWithErrors::Rename(const FilePath& full_path) {
192 return ShouldReturnError(
193 content::TestFileErrorInjector::FILE_OPERATION_RENAME,
194 DownloadFileImpl::Rename(full_path));
195 }
196
197 net::Error DownloadFileWithErrors::ShouldReturnError(
198 content::TestFileErrorInjector::FileOperationCode code,
199 net::Error original_net_error) {
200 int counter = operation_counter_[code];
201 ++operation_counter_[code];
202
203 if (code != error_info_.code)
204 return original_net_error;
205
206 if (counter != error_info_.operation_instance)
207 return original_net_error;
208
209 VLOG(1) << " " << __FUNCTION__ << "()"
210 << " url = '" << source_url_.spec() << "'"
211 << " code = " << content::TestFileErrorInjector::DebugString(code)
212 << " (" << code << ")"
213 << " counter = " << counter
214 << " original_error = " << net::ErrorToString(original_net_error)
215 << " (" << original_net_error << ")"
216 << " new error = " << net::ErrorToString(error_info_.net_error)
217 << " (" << error_info_.net_error << ")";
218
219 return error_info_.net_error;
220 }
221
222 DownloadFileWithErrorsFactory::DownloadFileWithErrorsFactory(
223 const TestFileErrorInjectorImpl::ConstructionCallback& ctor_callback,
224 const TestFileErrorInjectorImpl::DestructionCallback& dtor_callback)
225 : construction_callback_(ctor_callback),
226 destruction_callback_(dtor_callback) {
227 }
228
229 DownloadFileWithErrorsFactory::~DownloadFileWithErrorsFactory() {
230 }
231
232 content::DownloadFile* DownloadFileWithErrorsFactory::CreateFile(
233 DownloadCreateInfo* info,
234 const DownloadRequestHandle& request_handle,
235 content::DownloadManager* download_manager,
236 bool calculate_hash,
237 const net::BoundNetLog& bound_net_log) {
238 std::string url = info->url().spec();
239
240 if (injected_errors_.find(url) == injected_errors_.end()) {
cbentzel 2012/03/02 20:45:19 Just as a question - do you ever expect this case
ahendrickson 2012/03/02 21:58:47 Yes, I would like to handle the case where I have
241 // Have to create entry, because FileErrorInfo is not a POD type.
242 content::TestFileErrorInjector::FileErrorInfo err_info = {
cbentzel 2012/03/02 20:45:19 You could also define a default constructor, that
ahendrickson 2012/03/02 21:58:47 If I have a constructor, I can't use initializer l
Randy Smith (Not in Mondays) 2012/03/06 23:05:19 I don't think there's any conflict between having
ahendrickson 2012/03/08 22:00:08 I actually added a default constructor & tried to
243 url,
244 content::TestFileErrorInjector::FILE_OPERATION_INITIALIZE,
245 -1,
246 net::OK
247 };
248 injected_errors_[url] = err_info;
249 }
250
251 return new DownloadFileWithErrors(info,
252 new DownloadRequestHandle(request_handle),
253 download_manager,
254 calculate_hash,
255 bound_net_log,
256 injected_errors_[url],
257 construction_callback_,
258 destruction_callback_);
259 }
260
261 bool DownloadFileWithErrorsFactory::AddError(
262 const content::TestFileErrorInjector::FileErrorInfo& error_info) {
263 // Creates an empty entry if necessary. Duplicate entries overwrite.
264 injected_errors_[error_info.url] = error_info;
265
266 return true;
267 }
268
269 void DownloadFileWithErrorsFactory::ClearErrors() {
270 injected_errors_.clear();
271 }
272
273 TestFileErrorInjectorImpl::TestFileErrorInjectorImpl()
274 : created_factory_(false) {
275 }
276
277 TestFileErrorInjectorImpl::~TestFileErrorInjectorImpl() {
278 }
279
280 bool TestFileErrorInjectorImpl::AddError(const FileErrorInfo& error_info) {
281 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
282 DCHECK_LE(0, error_info.operation_instance);
283 DCHECK(injected_errors_.find(error_info.url) == injected_errors_.end());
284
285 // Creates an empty entry if necessary.
286 injected_errors_[error_info.url] = error_info;
287
288 return true;
289 }
290
291 void TestFileErrorInjectorImpl::ClearErrors() {
292 injected_errors_.clear();
293 }
294
295 bool TestFileErrorInjectorImpl::InjectErrors() {
296 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
297
298 DownloadFileManager* download_file_manager = GetDownloadFileManager();
299 DCHECK(download_file_manager);
300
301 ClearFoundFiles();
302
303 if (!created_factory_) {
304 scoped_ptr<DownloadFileManager::DownloadFileFactory> download_file_factory(
305 new DownloadFileWithErrorsFactory(
306 base::Bind(&TestFileErrorInjectorImpl::
307 RecordDownloadFileConstruction,
308 this),
309 base::Bind(&TestFileErrorInjectorImpl::
310 RecordDownloadFileDestruction,
311 this)));
312
313 download_file_manager->SetFileFactoryForTesting(
314 download_file_factory.Pass());
315
316 created_factory_ = true;
317 }
318
319 DownloadFileWithErrorsFactory* file_factory =
320 static_cast<DownloadFileWithErrorsFactory*>(
321 download_file_manager->GetFileFactoryForTesting());
322
323 file_factory->ClearErrors();
324
325 for (ErrorMap::const_iterator it = injected_errors_.begin();
326 it != injected_errors_.end();
327 ++it) {
328 file_factory->AddError(it->second);
329 }
330
331 return true;
332 }
333
334 size_t TestFileErrorInjectorImpl::CurrentFileCount() const {
335 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
336 return files_.size();
337 }
338
339 size_t TestFileErrorInjectorImpl::TotalFileCount() const {
340 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
341 return found_files_.size();
342 }
343
344
345 bool TestFileErrorInjectorImpl::HadFile(const GURL& url) const {
346 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
347
348 return (found_files_.find(url) != found_files_.end());
349 }
350
351 const content::DownloadId TestFileErrorInjectorImpl::GetId(
352 const GURL& url) const {
353 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
354
355 FileMap::const_iterator it = found_files_.find(url);
356 if (it == found_files_.end())
357 return content::DownloadId::Invalid();
358
359 return it->second;
360 }
361
362 void TestFileErrorInjectorImpl::ClearFoundFiles() {
363 found_files_.clear();
364 }
365
366 void TestFileErrorInjectorImpl::DownloadFileCreated(GURL url,
367 content::DownloadId id) {
368 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
369 DCHECK(files_.find(url) == files_.end());
370
371 files_[url] = id;
372 found_files_[url] = id;
373 }
374
375 void TestFileErrorInjectorImpl::DestroyingDownloadFile(GURL url) {
376 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
377 DCHECK(files_.find(url) != files_.end());
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 TestFileErrorInjector::~TestFileErrorInjector() {
407 }
408
409 // static
410 scoped_refptr<TestFileErrorInjector> TestFileErrorInjector::Get() {
411 scoped_refptr<TestFileErrorInjector> single_injector_;
412 if (single_injector_.get() == NULL)
413 single_injector_ = new TestFileErrorInjectorImpl;
414 return single_injector_;
415 }
416
417 std::string TestFileErrorInjector::DebugString(FileOperationCode code) {
418
419 #define TO_STRING(code) \
420 case TestFileErrorInjector::FILE_OPERATION_##code: return #code
421
422 switch (code) {
423 TO_STRING(INITIALIZE);
424 TO_STRING(WRITE);
425 TO_STRING(RENAME);
426 default:
427 break;
428 }
429
430 #undef TO_STRING
431
432 return "Unknown";
433 }
434
435 } // 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