Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / content / browser / fileapi / copy_or_move_operation_delegate_unittest.cc
1 // Copyright 2013 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 <map>
6 #include <queue>
7
8 #include "base/basictypes.h"
9 #include "base/bind.h"
10 #include "base/file_util.h"
11 #include "base/files/scoped_temp_dir.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/run_loop.h"
14 #include "base/stl_util.h"
15 #include "content/public/test/async_file_test_helper.h"
16 #include "content/public/test/test_file_system_backend.h"
17 #include "content/public/test/test_file_system_context.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "webkit/browser/blob/file_stream_reader.h"
20 #include "webkit/browser/fileapi/copy_or_move_file_validator.h"
21 #include "webkit/browser/fileapi/copy_or_move_operation_delegate.h"
22 #include "webkit/browser/fileapi/file_stream_writer.h"
23 #include "webkit/browser/fileapi/file_system_backend.h"
24 #include "webkit/browser/fileapi/file_system_context.h"
25 #include "webkit/browser/fileapi/file_system_operation.h"
26 #include "webkit/browser/fileapi/file_system_url.h"
27 #include "webkit/browser/fileapi/test_file_set.h"
28 #include "webkit/browser/quota/mock_quota_manager.h"
29 #include "webkit/browser/quota/mock_quota_manager_proxy.h"
30 #include "webkit/browser/quota/quota_manager.h"
31 #include "webkit/common/fileapi/file_system_util.h"
32
33 using content::AsyncFileTestHelper;
34 using fileapi::CopyOrMoveOperationDelegate;
35 using fileapi::FileStreamWriter;
36 using fileapi::FileSystemOperation;
37 using fileapi::FileSystemType;
38 using fileapi::FileSystemURL;
39 using fileapi::test::TestCaseRecord;
40
41 namespace content {
42
43 typedef fileapi::FileSystemOperation::FileEntryList FileEntryList;
44
45 namespace {
46
47 void ExpectOk(const GURL& origin_url,
48               const std::string& name,
49               base::File::Error error) {
50   ASSERT_EQ(base::File::FILE_OK, error);
51 }
52
53 class TestValidatorFactory : public fileapi::CopyOrMoveFileValidatorFactory {
54  public:
55   // A factory that creates validators that accept everything or nothing.
56   TestValidatorFactory() {}
57   virtual ~TestValidatorFactory() {}
58
59   virtual fileapi::CopyOrMoveFileValidator* CreateCopyOrMoveFileValidator(
60       const FileSystemURL& /*src_url*/,
61       const base::FilePath& /*platform_path*/) OVERRIDE {
62     // Move arg management to TestValidator?
63     return new TestValidator(true, true, std::string("2"));
64   }
65
66  private:
67   class TestValidator : public fileapi::CopyOrMoveFileValidator {
68    public:
69     explicit TestValidator(bool pre_copy_valid,
70                            bool post_copy_valid,
71                            const std::string& reject_string)
72         : result_(pre_copy_valid ? base::File::FILE_OK :
73                                    base::File::FILE_ERROR_SECURITY),
74           write_result_(post_copy_valid ? base::File::FILE_OK :
75                                           base::File::FILE_ERROR_SECURITY),
76           reject_string_(reject_string) {
77     }
78     virtual ~TestValidator() {}
79
80     virtual void StartPreWriteValidation(
81         const ResultCallback& result_callback) OVERRIDE {
82       // Post the result since a real validator must do work asynchronously.
83       base::MessageLoop::current()->PostTask(
84           FROM_HERE, base::Bind(result_callback, result_));
85     }
86
87     virtual void StartPostWriteValidation(
88         const base::FilePath& dest_platform_path,
89         const ResultCallback& result_callback) OVERRIDE {
90       base::File::Error result = write_result_;
91       std::string unsafe = dest_platform_path.BaseName().AsUTF8Unsafe();
92       if (unsafe.find(reject_string_) != std::string::npos) {
93         result = base::File::FILE_ERROR_SECURITY;
94       }
95       // Post the result since a real validator must do work asynchronously.
96       base::MessageLoop::current()->PostTask(
97           FROM_HERE, base::Bind(result_callback, result));
98     }
99
100    private:
101     base::File::Error result_;
102     base::File::Error write_result_;
103     std::string reject_string_;
104
105     DISALLOW_COPY_AND_ASSIGN(TestValidator);
106   };
107 };
108
109 // Records CopyProgressCallback invocations.
110 struct ProgressRecord {
111   fileapi::FileSystemOperation::CopyProgressType type;
112   FileSystemURL source_url;
113   FileSystemURL dest_url;
114   int64 size;
115 };
116
117 void RecordProgressCallback(std::vector<ProgressRecord>* records,
118                             fileapi::FileSystemOperation::CopyProgressType type,
119                             const FileSystemURL& source_url,
120                             const FileSystemURL& dest_url,
121                             int64 size) {
122   ProgressRecord record;
123   record.type = type;
124   record.source_url = source_url;
125   record.dest_url = dest_url;
126   record.size = size;
127   records->push_back(record);
128 }
129
130 void RecordFileProgressCallback(std::vector<int64>* records,
131                                 int64 progress) {
132   records->push_back(progress);
133 }
134
135 void AssignAndQuit(base::RunLoop* run_loop,
136                    base::File::Error* result_out,
137                    base::File::Error result) {
138   *result_out = result;
139   run_loop->Quit();
140 }
141
142 class ScopedThreadStopper {
143  public:
144   ScopedThreadStopper(base::Thread* thread) : thread_(thread) {
145   }
146
147   ~ScopedThreadStopper() {
148     if (thread_) {
149       // Give another chance for deleted streams to perform Close.
150       base::RunLoop run_loop;
151       thread_->message_loop_proxy()->PostTaskAndReply(
152           FROM_HERE, base::Bind(&base::DoNothing), run_loop.QuitClosure());
153       run_loop.Run();
154       thread_->Stop();
155     }
156   }
157
158   bool is_valid() const { return thread_; }
159
160  private:
161   base::Thread* thread_;
162   DISALLOW_COPY_AND_ASSIGN(ScopedThreadStopper);
163 };
164
165 }  // namespace
166
167 class CopyOrMoveOperationTestHelper {
168  public:
169   CopyOrMoveOperationTestHelper(
170       const GURL& origin,
171       FileSystemType src_type,
172       FileSystemType dest_type)
173       : origin_(origin),
174         src_type_(src_type),
175         dest_type_(dest_type) {}
176
177   ~CopyOrMoveOperationTestHelper() {
178     file_system_context_ = NULL;
179     quota_manager_proxy_->SimulateQuotaManagerDestroyed();
180     quota_manager_ = NULL;
181     quota_manager_proxy_ = NULL;
182     base::RunLoop().RunUntilIdle();
183   }
184
185   void SetUp() {
186     SetUp(true, true);
187   }
188
189   void SetUpNoValidator() {
190     SetUp(true, false);
191   }
192
193   void SetUp(bool require_copy_or_move_validator,
194              bool init_copy_or_move_validator) {
195     ASSERT_TRUE(base_.CreateUniqueTempDir());
196     base::FilePath base_dir = base_.path();
197     quota_manager_ =
198         new quota::MockQuotaManager(false /* is_incognito */,
199                                     base_dir,
200                                     base::MessageLoopProxy::current().get(),
201                                     base::MessageLoopProxy::current().get(),
202                                     NULL /* special storage policy */);
203     quota_manager_proxy_ = new quota::MockQuotaManagerProxy(
204         quota_manager_.get(), base::MessageLoopProxy::current().get());
205     file_system_context_ =
206         CreateFileSystemContextForTesting(quota_manager_proxy_.get(), base_dir);
207
208     // Prepare the origin's root directory.
209     fileapi::FileSystemBackend* backend =
210         file_system_context_->GetFileSystemBackend(src_type_);
211     backend->OpenFileSystem(origin_, src_type_,
212                             fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
213                             base::Bind(&ExpectOk));
214     backend = file_system_context_->GetFileSystemBackend(dest_type_);
215     if (dest_type_ == fileapi::kFileSystemTypeTest) {
216       TestFileSystemBackend* test_backend =
217           static_cast<TestFileSystemBackend*>(backend);
218       scoped_ptr<fileapi::CopyOrMoveFileValidatorFactory> factory(
219           new TestValidatorFactory);
220       test_backend->set_require_copy_or_move_validator(
221           require_copy_or_move_validator);
222       if (init_copy_or_move_validator)
223         test_backend->InitializeCopyOrMoveFileValidatorFactory(factory.Pass());
224     }
225     backend->OpenFileSystem(origin_, dest_type_,
226                             fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
227                             base::Bind(&ExpectOk));
228     base::RunLoop().RunUntilIdle();
229
230     // Grant relatively big quota initially.
231     quota_manager_->SetQuota(
232         origin_,
233         fileapi::FileSystemTypeToQuotaStorageType(src_type_),
234         1024 * 1024);
235     quota_manager_->SetQuota(
236         origin_,
237         fileapi::FileSystemTypeToQuotaStorageType(dest_type_),
238         1024 * 1024);
239   }
240
241   int64 GetSourceUsage() {
242     int64 usage = 0;
243     GetUsageAndQuota(src_type_, &usage, NULL);
244     return usage;
245   }
246
247   int64 GetDestUsage() {
248     int64 usage = 0;
249     GetUsageAndQuota(dest_type_, &usage, NULL);
250     return usage;
251   }
252
253   FileSystemURL SourceURL(const std::string& path) {
254     return file_system_context_->CreateCrackedFileSystemURL(
255         origin_, src_type_, base::FilePath::FromUTF8Unsafe(path));
256   }
257
258   FileSystemURL DestURL(const std::string& path) {
259     return file_system_context_->CreateCrackedFileSystemURL(
260         origin_, dest_type_, base::FilePath::FromUTF8Unsafe(path));
261   }
262
263   base::File::Error Copy(const FileSystemURL& src,
264                          const FileSystemURL& dest) {
265     return AsyncFileTestHelper::Copy(file_system_context_.get(), src, dest);
266   }
267
268   base::File::Error CopyWithProgress(
269       const FileSystemURL& src,
270       const FileSystemURL& dest,
271       const AsyncFileTestHelper::CopyProgressCallback& progress_callback) {
272     return AsyncFileTestHelper::CopyWithProgress(
273         file_system_context_.get(), src, dest, progress_callback);
274   }
275
276   base::File::Error Move(const FileSystemURL& src,
277                          const FileSystemURL& dest) {
278     return AsyncFileTestHelper::Move(file_system_context_.get(), src, dest);
279   }
280
281   base::File::Error SetUpTestCaseFiles(
282       const FileSystemURL& root,
283       const TestCaseRecord* const test_cases,
284       size_t test_case_size) {
285     base::File::Error result = base::File::FILE_ERROR_FAILED;
286     for (size_t i = 0; i < test_case_size; ++i) {
287       const TestCaseRecord& test_case = test_cases[i];
288       FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
289           root.origin(),
290           root.mount_type(),
291           root.virtual_path().Append(test_case.path));
292       if (test_case.is_directory)
293         result = CreateDirectory(url);
294       else
295         result = CreateFile(url, test_case.data_file_size);
296       EXPECT_EQ(base::File::FILE_OK, result) << url.DebugString();
297       if (result != base::File::FILE_OK)
298         return result;
299     }
300     return result;
301   }
302
303   void VerifyTestCaseFiles(
304       const FileSystemURL& root,
305       const TestCaseRecord* const test_cases,
306       size_t test_case_size) {
307     std::map<base::FilePath, const TestCaseRecord*> test_case_map;
308     for (size_t i = 0; i < test_case_size; ++i) {
309       test_case_map[
310           base::FilePath(test_cases[i].path).NormalizePathSeparators()] =
311               &test_cases[i];
312     }
313
314     std::queue<FileSystemURL> directories;
315     FileEntryList entries;
316     directories.push(root);
317     while (!directories.empty()) {
318       FileSystemURL dir = directories.front();
319       directories.pop();
320       ASSERT_EQ(base::File::FILE_OK, ReadDirectory(dir, &entries));
321       for (size_t i = 0; i < entries.size(); ++i) {
322         FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
323             dir.origin(),
324             dir.mount_type(),
325             dir.virtual_path().Append(entries[i].name));
326         base::FilePath relative;
327         root.virtual_path().AppendRelativePath(url.virtual_path(), &relative);
328         relative = relative.NormalizePathSeparators();
329         ASSERT_TRUE(ContainsKey(test_case_map, relative));
330         if (entries[i].is_directory) {
331           EXPECT_TRUE(test_case_map[relative]->is_directory);
332           directories.push(url);
333         } else {
334           EXPECT_FALSE(test_case_map[relative]->is_directory);
335           EXPECT_TRUE(FileExists(url, test_case_map[relative]->data_file_size));
336         }
337         test_case_map.erase(relative);
338       }
339     }
340     EXPECT_TRUE(test_case_map.empty());
341     std::map<base::FilePath, const TestCaseRecord*>::const_iterator it;
342     for (it = test_case_map.begin(); it != test_case_map.end(); ++it) {
343       LOG(ERROR) << "Extra entry: " << it->first.LossyDisplayName();
344     }
345   }
346
347   base::File::Error ReadDirectory(const FileSystemURL& url,
348                                   FileEntryList* entries) {
349     return AsyncFileTestHelper::ReadDirectory(
350         file_system_context_.get(), url, entries);
351   }
352
353   base::File::Error CreateDirectory(const FileSystemURL& url) {
354     return AsyncFileTestHelper::CreateDirectory(file_system_context_.get(),
355                                                 url);
356   }
357
358   base::File::Error CreateFile(const FileSystemURL& url, size_t size) {
359     base::File::Error result =
360         AsyncFileTestHelper::CreateFile(file_system_context_.get(), url);
361     if (result != base::File::FILE_OK)
362       return result;
363     return AsyncFileTestHelper::TruncateFile(
364         file_system_context_.get(), url, size);
365   }
366
367   bool FileExists(const FileSystemURL& url, int64 expected_size) {
368     return AsyncFileTestHelper::FileExists(
369         file_system_context_.get(), url, expected_size);
370   }
371
372   bool DirectoryExists(const FileSystemURL& url) {
373     return AsyncFileTestHelper::DirectoryExists(file_system_context_.get(),
374                                                 url);
375   }
376
377  private:
378   void GetUsageAndQuota(FileSystemType type, int64* usage, int64* quota) {
379     quota::QuotaStatusCode status = AsyncFileTestHelper::GetUsageAndQuota(
380         quota_manager_.get(), origin_, type, usage, quota);
381     ASSERT_EQ(quota::kQuotaStatusOk, status);
382   }
383
384  private:
385   base::ScopedTempDir base_;
386
387   const GURL origin_;
388   const FileSystemType src_type_;
389   const FileSystemType dest_type_;
390
391   base::MessageLoopForIO message_loop_;
392   scoped_refptr<fileapi::FileSystemContext> file_system_context_;
393   scoped_refptr<quota::MockQuotaManagerProxy> quota_manager_proxy_;
394   scoped_refptr<quota::MockQuotaManager> quota_manager_;
395
396   DISALLOW_COPY_AND_ASSIGN(CopyOrMoveOperationTestHelper);
397 };
398
399 TEST(LocalFileSystemCopyOrMoveOperationTest, CopySingleFile) {
400   CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
401                                        fileapi::kFileSystemTypeTemporary,
402                                        fileapi::kFileSystemTypePersistent);
403   helper.SetUp();
404
405   FileSystemURL src = helper.SourceURL("a");
406   FileSystemURL dest = helper.DestURL("b");
407   int64 src_initial_usage = helper.GetSourceUsage();
408   int64 dest_initial_usage = helper.GetDestUsage();
409
410   // Set up a source file.
411   ASSERT_EQ(base::File::FILE_OK, helper.CreateFile(src, 10));
412   int64 src_increase = helper.GetSourceUsage() - src_initial_usage;
413
414   // Copy it.
415   ASSERT_EQ(base::File::FILE_OK, helper.Copy(src, dest));
416
417   // Verify.
418   ASSERT_TRUE(helper.FileExists(src, 10));
419   ASSERT_TRUE(helper.FileExists(dest, 10));
420
421   int64 src_new_usage = helper.GetSourceUsage();
422   ASSERT_EQ(src_initial_usage + src_increase, src_new_usage);
423
424   int64 dest_increase = helper.GetDestUsage() - dest_initial_usage;
425   ASSERT_EQ(src_increase, dest_increase);
426 }
427
428 TEST(LocalFileSystemCopyOrMoveOperationTest, MoveSingleFile) {
429   CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
430                                        fileapi::kFileSystemTypeTemporary,
431                                        fileapi::kFileSystemTypePersistent);
432   helper.SetUp();
433
434   FileSystemURL src = helper.SourceURL("a");
435   FileSystemURL dest = helper.DestURL("b");
436   int64 src_initial_usage = helper.GetSourceUsage();
437   int64 dest_initial_usage = helper.GetDestUsage();
438
439   // Set up a source file.
440   ASSERT_EQ(base::File::FILE_OK, helper.CreateFile(src, 10));
441   int64 src_increase = helper.GetSourceUsage() - src_initial_usage;
442
443   // Move it.
444   ASSERT_EQ(base::File::FILE_OK, helper.Move(src, dest));
445
446   // Verify.
447   ASSERT_FALSE(helper.FileExists(src, AsyncFileTestHelper::kDontCheckSize));
448   ASSERT_TRUE(helper.FileExists(dest, 10));
449
450   int64 src_new_usage = helper.GetSourceUsage();
451   ASSERT_EQ(src_initial_usage, src_new_usage);
452
453   int64 dest_increase = helper.GetDestUsage() - dest_initial_usage;
454   ASSERT_EQ(src_increase, dest_increase);
455 }
456
457 TEST(LocalFileSystemCopyOrMoveOperationTest, CopySingleDirectory) {
458   CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
459                                        fileapi::kFileSystemTypeTemporary,
460                                        fileapi::kFileSystemTypePersistent);
461   helper.SetUp();
462
463   FileSystemURL src = helper.SourceURL("a");
464   FileSystemURL dest = helper.DestURL("b");
465   int64 src_initial_usage = helper.GetSourceUsage();
466   int64 dest_initial_usage = helper.GetDestUsage();
467
468   // Set up a source directory.
469   ASSERT_EQ(base::File::FILE_OK, helper.CreateDirectory(src));
470   int64 src_increase = helper.GetSourceUsage() - src_initial_usage;
471
472   // Copy it.
473   ASSERT_EQ(base::File::FILE_OK, helper.Copy(src, dest));
474
475   // Verify.
476   ASSERT_TRUE(helper.DirectoryExists(src));
477   ASSERT_TRUE(helper.DirectoryExists(dest));
478
479   int64 src_new_usage = helper.GetSourceUsage();
480   ASSERT_EQ(src_initial_usage + src_increase, src_new_usage);
481
482   int64 dest_increase = helper.GetDestUsage() - dest_initial_usage;
483   ASSERT_EQ(src_increase, dest_increase);
484 }
485
486 TEST(LocalFileSystemCopyOrMoveOperationTest, MoveSingleDirectory) {
487   CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
488                                        fileapi::kFileSystemTypeTemporary,
489                                        fileapi::kFileSystemTypePersistent);
490   helper.SetUp();
491
492   FileSystemURL src = helper.SourceURL("a");
493   FileSystemURL dest = helper.DestURL("b");
494   int64 src_initial_usage = helper.GetSourceUsage();
495   int64 dest_initial_usage = helper.GetDestUsage();
496
497   // Set up a source directory.
498   ASSERT_EQ(base::File::FILE_OK, helper.CreateDirectory(src));
499   int64 src_increase = helper.GetSourceUsage() - src_initial_usage;
500
501   // Move it.
502   ASSERT_EQ(base::File::FILE_OK, helper.Move(src, dest));
503
504   // Verify.
505   ASSERT_FALSE(helper.DirectoryExists(src));
506   ASSERT_TRUE(helper.DirectoryExists(dest));
507
508   int64 src_new_usage = helper.GetSourceUsage();
509   ASSERT_EQ(src_initial_usage, src_new_usage);
510
511   int64 dest_increase = helper.GetDestUsage() - dest_initial_usage;
512   ASSERT_EQ(src_increase, dest_increase);
513 }
514
515 TEST(LocalFileSystemCopyOrMoveOperationTest, CopyDirectory) {
516   CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
517                                        fileapi::kFileSystemTypeTemporary,
518                                        fileapi::kFileSystemTypePersistent);
519   helper.SetUp();
520
521   FileSystemURL src = helper.SourceURL("a");
522   FileSystemURL dest = helper.DestURL("b");
523   int64 src_initial_usage = helper.GetSourceUsage();
524   int64 dest_initial_usage = helper.GetDestUsage();
525
526   // Set up a source directory.
527   ASSERT_EQ(base::File::FILE_OK, helper.CreateDirectory(src));
528   ASSERT_EQ(base::File::FILE_OK,
529             helper.SetUpTestCaseFiles(src,
530                                       fileapi::test::kRegularTestCases,
531                                       fileapi::test::kRegularTestCaseSize));
532   int64 src_increase = helper.GetSourceUsage() - src_initial_usage;
533
534   // Copy it.
535   ASSERT_EQ(base::File::FILE_OK,
536             helper.CopyWithProgress(
537                 src, dest,
538                 AsyncFileTestHelper::CopyProgressCallback()));
539
540   // Verify.
541   ASSERT_TRUE(helper.DirectoryExists(src));
542   ASSERT_TRUE(helper.DirectoryExists(dest));
543
544   helper.VerifyTestCaseFiles(dest,
545                              fileapi::test::kRegularTestCases,
546                              fileapi::test::kRegularTestCaseSize);
547
548   int64 src_new_usage = helper.GetSourceUsage();
549   ASSERT_EQ(src_initial_usage + src_increase, src_new_usage);
550
551   int64 dest_increase = helper.GetDestUsage() - dest_initial_usage;
552   ASSERT_EQ(src_increase, dest_increase);
553 }
554
555 TEST(LocalFileSystemCopyOrMoveOperationTest, MoveDirectory) {
556   CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
557                                        fileapi::kFileSystemTypeTemporary,
558                                        fileapi::kFileSystemTypePersistent);
559   helper.SetUp();
560
561   FileSystemURL src = helper.SourceURL("a");
562   FileSystemURL dest = helper.DestURL("b");
563   int64 src_initial_usage = helper.GetSourceUsage();
564   int64 dest_initial_usage = helper.GetDestUsage();
565
566   // Set up a source directory.
567   ASSERT_EQ(base::File::FILE_OK, helper.CreateDirectory(src));
568   ASSERT_EQ(base::File::FILE_OK,
569             helper.SetUpTestCaseFiles(src,
570                                       fileapi::test::kRegularTestCases,
571                                       fileapi::test::kRegularTestCaseSize));
572   int64 src_increase = helper.GetSourceUsage() - src_initial_usage;
573
574   // Move it.
575   ASSERT_EQ(base::File::FILE_OK, helper.Move(src, dest));
576
577   // Verify.
578   ASSERT_FALSE(helper.DirectoryExists(src));
579   ASSERT_TRUE(helper.DirectoryExists(dest));
580
581   helper.VerifyTestCaseFiles(dest,
582                              fileapi::test::kRegularTestCases,
583                              fileapi::test::kRegularTestCaseSize);
584
585   int64 src_new_usage = helper.GetSourceUsage();
586   ASSERT_EQ(src_initial_usage, src_new_usage);
587
588   int64 dest_increase = helper.GetDestUsage() - dest_initial_usage;
589   ASSERT_EQ(src_increase, dest_increase);
590 }
591
592 TEST(LocalFileSystemCopyOrMoveOperationTest,
593      MoveDirectoryFailPostWriteValidation) {
594   CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
595                                        fileapi::kFileSystemTypeTemporary,
596                                        fileapi::kFileSystemTypeTest);
597   helper.SetUp();
598
599   FileSystemURL src = helper.SourceURL("a");
600   FileSystemURL dest = helper.DestURL("b");
601
602   // Set up a source directory.
603   ASSERT_EQ(base::File::FILE_OK, helper.CreateDirectory(src));
604   ASSERT_EQ(base::File::FILE_OK,
605             helper.SetUpTestCaseFiles(src,
606                                       fileapi::test::kRegularTestCases,
607                                       fileapi::test::kRegularTestCaseSize));
608
609   // Move it.
610   helper.Move(src, dest);
611
612   // Verify.
613   ASSERT_TRUE(helper.DirectoryExists(src));
614   ASSERT_TRUE(helper.DirectoryExists(dest));
615
616   TestCaseRecord kMoveDirResultCases[] = {
617     {false, FILE_PATH_LITERAL("file 0"), 38},
618     {false, FILE_PATH_LITERAL("file 3"), 0},
619   };
620
621   helper.VerifyTestCaseFiles(dest,
622                              kMoveDirResultCases,
623                              arraysize(kMoveDirResultCases));
624 }
625
626 TEST(LocalFileSystemCopyOrMoveOperationTest, CopySingleFileNoValidator) {
627   CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
628                                        fileapi::kFileSystemTypeTemporary,
629                                        fileapi::kFileSystemTypeTest);
630   helper.SetUpNoValidator();
631
632   FileSystemURL src = helper.SourceURL("a");
633   FileSystemURL dest = helper.DestURL("b");
634
635   // Set up a source file.
636   ASSERT_EQ(base::File::FILE_OK, helper.CreateFile(src, 10));
637
638   // The copy attempt should fail with a security error -- getting
639   // the factory returns a security error, and the copy operation must
640   // respect that.
641   ASSERT_EQ(base::File::FILE_ERROR_SECURITY, helper.Copy(src, dest));
642 }
643
644 TEST(LocalFileSystemCopyOrMoveOperationTest, ProgressCallback) {
645   CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
646                                        fileapi::kFileSystemTypeTemporary,
647                                        fileapi::kFileSystemTypePersistent);
648   helper.SetUp();
649
650   FileSystemURL src = helper.SourceURL("a");
651   FileSystemURL dest = helper.DestURL("b");
652
653   // Set up a source directory.
654   ASSERT_EQ(base::File::FILE_OK, helper.CreateDirectory(src));
655   ASSERT_EQ(base::File::FILE_OK,
656             helper.SetUpTestCaseFiles(src,
657                                       fileapi::test::kRegularTestCases,
658                                       fileapi::test::kRegularTestCaseSize));
659
660   std::vector<ProgressRecord> records;
661   ASSERT_EQ(base::File::FILE_OK,
662             helper.CopyWithProgress(src, dest,
663                                     base::Bind(&RecordProgressCallback,
664                                                base::Unretained(&records))));
665
666   // Verify progress callback.
667   for (size_t i = 0; i < fileapi::test::kRegularTestCaseSize; ++i) {
668     const TestCaseRecord& test_case = fileapi::test::kRegularTestCases[i];
669
670     FileSystemURL src_url = helper.SourceURL(
671         std::string("a/") + base::FilePath(test_case.path).AsUTF8Unsafe());
672     FileSystemURL dest_url = helper.DestURL(
673         std::string("b/") + base::FilePath(test_case.path).AsUTF8Unsafe());
674
675     // Find the first and last progress record.
676     size_t begin_index = records.size();
677     size_t end_index = records.size();
678     for (size_t j = 0; j < records.size(); ++j) {
679       if (records[j].source_url == src_url) {
680         if (begin_index == records.size())
681           begin_index = j;
682         end_index = j;
683       }
684     }
685
686     // The record should be found.
687     ASSERT_NE(begin_index, records.size());
688     ASSERT_NE(end_index, records.size());
689     ASSERT_NE(begin_index, end_index);
690
691     EXPECT_EQ(FileSystemOperation::BEGIN_COPY_ENTRY,
692               records[begin_index].type);
693     EXPECT_FALSE(records[begin_index].dest_url.is_valid());
694     EXPECT_EQ(FileSystemOperation::END_COPY_ENTRY, records[end_index].type);
695     EXPECT_EQ(dest_url, records[end_index].dest_url);
696
697     if (test_case.is_directory) {
698       // For directory copy, the progress shouldn't be interlaced.
699       EXPECT_EQ(begin_index + 1, end_index);
700     } else {
701       // PROGRESS event's size should be assending order.
702       int64 current_size = 0;
703       for (size_t j = begin_index + 1; j < end_index; ++j) {
704         if (records[j].source_url == src_url) {
705           EXPECT_EQ(FileSystemOperation::PROGRESS, records[j].type);
706           EXPECT_FALSE(records[j].dest_url.is_valid());
707           EXPECT_GE(records[j].size, current_size);
708           current_size = records[j].size;
709         }
710       }
711     }
712   }
713 }
714
715 TEST(LocalFileSystemCopyOrMoveOperationTest, StreamCopyHelper) {
716   base::ScopedTempDir temp_dir;
717   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
718   base::FilePath source_path = temp_dir.path().AppendASCII("source");
719   const char kTestData[] = "abcdefghijklmnopqrstuvwxyz0123456789";
720   file_util::WriteFile(source_path, kTestData,
721                        arraysize(kTestData) - 1);  // Exclude trailing '\0'.
722
723   base::FilePath dest_path = temp_dir.path().AppendASCII("dest");
724   // LocalFileWriter requires the file exists. So create an empty file here.
725   file_util::WriteFile(dest_path, "", 0);
726
727   base::MessageLoopForIO message_loop;
728   base::Thread file_thread("file_thread");
729   ASSERT_TRUE(file_thread.Start());
730   ScopedThreadStopper thread_stopper(&file_thread);
731   ASSERT_TRUE(thread_stopper.is_valid());
732
733   scoped_refptr<base::MessageLoopProxy> task_runner =
734       file_thread.message_loop_proxy();
735
736   scoped_ptr<webkit_blob::FileStreamReader> reader(
737       webkit_blob::FileStreamReader::CreateForLocalFile(
738           task_runner.get(), source_path, 0, base::Time()));
739
740   scoped_ptr<FileStreamWriter> writer(
741       FileStreamWriter::CreateForLocalFile(task_runner.get(), dest_path, 0));
742
743   std::vector<int64> progress;
744   CopyOrMoveOperationDelegate::StreamCopyHelper helper(
745       reader.Pass(), writer.Pass(),
746       false,  // don't need flush
747       10,  // buffer size
748       base::Bind(&RecordFileProgressCallback, base::Unretained(&progress)),
749       base::TimeDelta());  // For testing, we need all the progress.
750
751   base::File::Error error = base::File::FILE_ERROR_FAILED;
752   base::RunLoop run_loop;
753   helper.Run(base::Bind(&AssignAndQuit, &run_loop, &error));
754   run_loop.Run();
755
756   EXPECT_EQ(base::File::FILE_OK, error);
757   ASSERT_EQ(5U, progress.size());
758   EXPECT_EQ(0, progress[0]);
759   EXPECT_EQ(10, progress[1]);
760   EXPECT_EQ(20, progress[2]);
761   EXPECT_EQ(30, progress[3]);
762   EXPECT_EQ(36, progress[4]);
763
764   std::string content;
765   ASSERT_TRUE(base::ReadFileToString(dest_path, &content));
766   EXPECT_EQ(kTestData, content);
767 }
768
769 TEST(LocalFileSystemCopyOrMoveOperationTest, StreamCopyHelperWithFlush) {
770   // Testing the same configuration as StreamCopyHelper, but with |need_flush|
771   // parameter set to true. Since it is hard to test that the flush is indeed
772   // taking place, this test just only verifies that the file is correctly
773   // written with or without the flag.
774   base::ScopedTempDir temp_dir;
775   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
776   base::FilePath source_path = temp_dir.path().AppendASCII("source");
777   const char kTestData[] = "abcdefghijklmnopqrstuvwxyz0123456789";
778   file_util::WriteFile(source_path, kTestData,
779                        arraysize(kTestData) - 1);  // Exclude trailing '\0'.
780
781   base::FilePath dest_path = temp_dir.path().AppendASCII("dest");
782   // LocalFileWriter requires the file exists. So create an empty file here.
783   file_util::WriteFile(dest_path, "", 0);
784
785   base::MessageLoopForIO message_loop;
786   base::Thread file_thread("file_thread");
787   ASSERT_TRUE(file_thread.Start());
788   ScopedThreadStopper thread_stopper(&file_thread);
789   ASSERT_TRUE(thread_stopper.is_valid());
790
791   scoped_refptr<base::MessageLoopProxy> task_runner =
792       file_thread.message_loop_proxy();
793
794   scoped_ptr<webkit_blob::FileStreamReader> reader(
795       webkit_blob::FileStreamReader::CreateForLocalFile(
796           task_runner.get(), source_path, 0, base::Time()));
797
798   scoped_ptr<FileStreamWriter> writer(
799       FileStreamWriter::CreateForLocalFile(task_runner.get(), dest_path, 0));
800
801   std::vector<int64> progress;
802   CopyOrMoveOperationDelegate::StreamCopyHelper helper(
803       reader.Pass(), writer.Pass(),
804       true,  // need flush
805       10,  // buffer size
806       base::Bind(&RecordFileProgressCallback, base::Unretained(&progress)),
807       base::TimeDelta());  // For testing, we need all the progress.
808
809   base::File::Error error = base::File::FILE_ERROR_FAILED;
810   base::RunLoop run_loop;
811   helper.Run(base::Bind(&AssignAndQuit, &run_loop, &error));
812   run_loop.Run();
813
814   EXPECT_EQ(base::File::FILE_OK, error);
815   ASSERT_EQ(5U, progress.size());
816   EXPECT_EQ(0, progress[0]);
817   EXPECT_EQ(10, progress[1]);
818   EXPECT_EQ(20, progress[2]);
819   EXPECT_EQ(30, progress[3]);
820   EXPECT_EQ(36, progress[4]);
821
822   std::string content;
823   ASSERT_TRUE(base::ReadFileToString(dest_path, &content));
824   EXPECT_EQ(kTestData, content);
825 }
826
827 TEST(LocalFileSystemCopyOrMoveOperationTest, StreamCopyHelper_Cancel) {
828   base::ScopedTempDir temp_dir;
829   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
830   base::FilePath source_path = temp_dir.path().AppendASCII("source");
831   const char kTestData[] = "abcdefghijklmnopqrstuvwxyz0123456789";
832   file_util::WriteFile(source_path, kTestData,
833                        arraysize(kTestData) - 1);  // Exclude trailing '\0'.
834
835   base::FilePath dest_path = temp_dir.path().AppendASCII("dest");
836   // LocalFileWriter requires the file exists. So create an empty file here.
837   file_util::WriteFile(dest_path, "", 0);
838
839   base::MessageLoopForIO message_loop;
840   base::Thread file_thread("file_thread");
841   ASSERT_TRUE(file_thread.Start());
842   ScopedThreadStopper thread_stopper(&file_thread);
843   ASSERT_TRUE(thread_stopper.is_valid());
844
845   scoped_refptr<base::MessageLoopProxy> task_runner =
846       file_thread.message_loop_proxy();
847
848   scoped_ptr<webkit_blob::FileStreamReader> reader(
849       webkit_blob::FileStreamReader::CreateForLocalFile(
850           task_runner.get(), source_path, 0, base::Time()));
851
852   scoped_ptr<FileStreamWriter> writer(
853       FileStreamWriter::CreateForLocalFile(task_runner.get(), dest_path, 0));
854
855   std::vector<int64> progress;
856   CopyOrMoveOperationDelegate::StreamCopyHelper helper(
857       reader.Pass(), writer.Pass(),
858       false,  // need_flush
859       10,  // buffer size
860       base::Bind(&RecordFileProgressCallback, base::Unretained(&progress)),
861       base::TimeDelta());  // For testing, we need all the progress.
862
863   // Call Cancel() later.
864   base::MessageLoopProxy::current()->PostTask(
865       FROM_HERE,
866       base::Bind(&CopyOrMoveOperationDelegate::StreamCopyHelper::Cancel,
867                  base::Unretained(&helper)));
868
869   base::File::Error error = base::File::FILE_ERROR_FAILED;
870   base::RunLoop run_loop;
871   helper.Run(base::Bind(&AssignAndQuit, &run_loop, &error));
872   run_loop.Run();
873
874   EXPECT_EQ(base::File::FILE_ERROR_ABORT, error);
875 }
876
877 }  // namespace content