Upstream version 10.39.225.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/files/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/browser/quota/mock_quota_manager.h"
16 #include "content/browser/quota/mock_quota_manager_proxy.h"
17 #include "content/public/test/async_file_test_helper.h"
18 #include "content/public/test/test_file_system_backend.h"
19 #include "content/public/test/test_file_system_context.h"
20 #include "content/test/fileapi_test_file_set.h"
21 #include "storage/browser/blob/file_stream_reader.h"
22 #include "storage/browser/fileapi/copy_or_move_file_validator.h"
23 #include "storage/browser/fileapi/copy_or_move_operation_delegate.h"
24 #include "storage/browser/fileapi/file_stream_writer.h"
25 #include "storage/browser/fileapi/file_system_backend.h"
26 #include "storage/browser/fileapi/file_system_context.h"
27 #include "storage/browser/fileapi/file_system_operation.h"
28 #include "storage/browser/fileapi/file_system_url.h"
29 #include "storage/browser/quota/quota_manager.h"
30 #include "storage/common/fileapi/file_system_util.h"
31 #include "testing/gtest/include/gtest/gtest.h"
32
33 using content::AsyncFileTestHelper;
34 using storage::CopyOrMoveOperationDelegate;
35 using storage::FileStreamWriter;
36 using storage::FileSystemOperation;
37 using storage::FileSystemURL;
38
39 namespace content {
40
41 typedef storage::FileSystemOperation::FileEntryList FileEntryList;
42
43 namespace {
44
45 void ExpectOk(const GURL& origin_url,
46               const std::string& name,
47               base::File::Error error) {
48   ASSERT_EQ(base::File::FILE_OK, error);
49 }
50
51 class TestValidatorFactory : public storage::CopyOrMoveFileValidatorFactory {
52  public:
53   // A factory that creates validators that accept everything or nothing.
54   TestValidatorFactory() {}
55   virtual ~TestValidatorFactory() {}
56
57   virtual storage::CopyOrMoveFileValidator* CreateCopyOrMoveFileValidator(
58       const FileSystemURL& /*src_url*/,
59       const base::FilePath& /*platform_path*/) OVERRIDE {
60     // Move arg management to TestValidator?
61     return new TestValidator(true, true, std::string("2"));
62   }
63
64  private:
65   class TestValidator : public storage::CopyOrMoveFileValidator {
66    public:
67     explicit TestValidator(bool pre_copy_valid,
68                            bool post_copy_valid,
69                            const std::string& reject_string)
70         : result_(pre_copy_valid ? base::File::FILE_OK :
71                                    base::File::FILE_ERROR_SECURITY),
72           write_result_(post_copy_valid ? base::File::FILE_OK :
73                                           base::File::FILE_ERROR_SECURITY),
74           reject_string_(reject_string) {
75     }
76     virtual ~TestValidator() {}
77
78     virtual void StartPreWriteValidation(
79         const ResultCallback& result_callback) OVERRIDE {
80       // Post the result since a real validator must do work asynchronously.
81       base::MessageLoop::current()->PostTask(
82           FROM_HERE, base::Bind(result_callback, result_));
83     }
84
85     virtual void StartPostWriteValidation(
86         const base::FilePath& dest_platform_path,
87         const ResultCallback& result_callback) OVERRIDE {
88       base::File::Error result = write_result_;
89       std::string unsafe = dest_platform_path.BaseName().AsUTF8Unsafe();
90       if (unsafe.find(reject_string_) != std::string::npos) {
91         result = base::File::FILE_ERROR_SECURITY;
92       }
93       // Post the result since a real validator must do work asynchronously.
94       base::MessageLoop::current()->PostTask(
95           FROM_HERE, base::Bind(result_callback, result));
96     }
97
98    private:
99     base::File::Error result_;
100     base::File::Error write_result_;
101     std::string reject_string_;
102
103     DISALLOW_COPY_AND_ASSIGN(TestValidator);
104   };
105 };
106
107 // Records CopyProgressCallback invocations.
108 struct ProgressRecord {
109   storage::FileSystemOperation::CopyProgressType type;
110   FileSystemURL source_url;
111   FileSystemURL dest_url;
112   int64 size;
113 };
114
115 void RecordProgressCallback(std::vector<ProgressRecord>* records,
116                             storage::FileSystemOperation::CopyProgressType type,
117                             const FileSystemURL& source_url,
118                             const FileSystemURL& dest_url,
119                             int64 size) {
120   ProgressRecord record;
121   record.type = type;
122   record.source_url = source_url;
123   record.dest_url = dest_url;
124   record.size = size;
125   records->push_back(record);
126 }
127
128 void RecordFileProgressCallback(std::vector<int64>* records,
129                                 int64 progress) {
130   records->push_back(progress);
131 }
132
133 void AssignAndQuit(base::RunLoop* run_loop,
134                    base::File::Error* result_out,
135                    base::File::Error result) {
136   *result_out = result;
137   run_loop->Quit();
138 }
139
140 class ScopedThreadStopper {
141  public:
142   ScopedThreadStopper(base::Thread* thread) : thread_(thread) {
143   }
144
145   ~ScopedThreadStopper() {
146     if (thread_) {
147       // Give another chance for deleted streams to perform Close.
148       base::RunLoop run_loop;
149       thread_->message_loop_proxy()->PostTaskAndReply(
150           FROM_HERE, base::Bind(&base::DoNothing), run_loop.QuitClosure());
151       run_loop.Run();
152       thread_->Stop();
153     }
154   }
155
156   bool is_valid() const { return thread_; }
157
158  private:
159   base::Thread* thread_;
160   DISALLOW_COPY_AND_ASSIGN(ScopedThreadStopper);
161 };
162
163 }  // namespace
164
165 class CopyOrMoveOperationTestHelper {
166  public:
167   CopyOrMoveOperationTestHelper(const GURL& origin,
168                                 storage::FileSystemType src_type,
169                                 storage::FileSystemType dest_type)
170       : origin_(origin), src_type_(src_type), dest_type_(dest_type) {}
171
172   ~CopyOrMoveOperationTestHelper() {
173     file_system_context_ = NULL;
174     quota_manager_proxy_->SimulateQuotaManagerDestroyed();
175     quota_manager_ = NULL;
176     quota_manager_proxy_ = NULL;
177     base::RunLoop().RunUntilIdle();
178   }
179
180   void SetUp() {
181     SetUp(true, true);
182   }
183
184   void SetUpNoValidator() {
185     SetUp(true, false);
186   }
187
188   void SetUp(bool require_copy_or_move_validator,
189              bool init_copy_or_move_validator) {
190     ASSERT_TRUE(base_.CreateUniqueTempDir());
191     base::FilePath base_dir = base_.path();
192     quota_manager_ =
193         new MockQuotaManager(false /* is_incognito */,
194                                     base_dir,
195                                     base::MessageLoopProxy::current().get(),
196                                     base::MessageLoopProxy::current().get(),
197                                     NULL /* special storage policy */);
198     quota_manager_proxy_ = new MockQuotaManagerProxy(
199         quota_manager_.get(), base::MessageLoopProxy::current().get());
200     file_system_context_ =
201         CreateFileSystemContextForTesting(quota_manager_proxy_.get(), base_dir);
202
203     // Prepare the origin's root directory.
204     storage::FileSystemBackend* backend =
205         file_system_context_->GetFileSystemBackend(src_type_);
206     backend->ResolveURL(
207         FileSystemURL::CreateForTest(origin_, src_type_, base::FilePath()),
208         storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
209         base::Bind(&ExpectOk));
210     backend = file_system_context_->GetFileSystemBackend(dest_type_);
211     if (dest_type_ == storage::kFileSystemTypeTest) {
212       TestFileSystemBackend* test_backend =
213           static_cast<TestFileSystemBackend*>(backend);
214       scoped_ptr<storage::CopyOrMoveFileValidatorFactory> factory(
215           new TestValidatorFactory);
216       test_backend->set_require_copy_or_move_validator(
217           require_copy_or_move_validator);
218       if (init_copy_or_move_validator)
219         test_backend->InitializeCopyOrMoveFileValidatorFactory(factory.Pass());
220     }
221     backend->ResolveURL(
222         FileSystemURL::CreateForTest(origin_, dest_type_, base::FilePath()),
223         storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
224         base::Bind(&ExpectOk));
225     base::RunLoop().RunUntilIdle();
226
227     // Grant relatively big quota initially.
228     quota_manager_->SetQuota(
229         origin_,
230         storage::FileSystemTypeToQuotaStorageType(src_type_),
231         1024 * 1024);
232     quota_manager_->SetQuota(
233         origin_,
234         storage::FileSystemTypeToQuotaStorageType(dest_type_),
235         1024 * 1024);
236   }
237
238   int64 GetSourceUsage() {
239     int64 usage = 0;
240     GetUsageAndQuota(src_type_, &usage, NULL);
241     return usage;
242   }
243
244   int64 GetDestUsage() {
245     int64 usage = 0;
246     GetUsageAndQuota(dest_type_, &usage, NULL);
247     return usage;
248   }
249
250   FileSystemURL SourceURL(const std::string& path) {
251     return file_system_context_->CreateCrackedFileSystemURL(
252         origin_, src_type_, base::FilePath::FromUTF8Unsafe(path));
253   }
254
255   FileSystemURL DestURL(const std::string& path) {
256     return file_system_context_->CreateCrackedFileSystemURL(
257         origin_, dest_type_, base::FilePath::FromUTF8Unsafe(path));
258   }
259
260   base::File::Error Copy(const FileSystemURL& src,
261                          const FileSystemURL& dest) {
262     return AsyncFileTestHelper::Copy(file_system_context_.get(), src, dest);
263   }
264
265   base::File::Error CopyWithProgress(
266       const FileSystemURL& src,
267       const FileSystemURL& dest,
268       const AsyncFileTestHelper::CopyProgressCallback& progress_callback) {
269     return AsyncFileTestHelper::CopyWithProgress(
270         file_system_context_.get(), src, dest, progress_callback);
271   }
272
273   base::File::Error Move(const FileSystemURL& src,
274                          const FileSystemURL& dest) {
275     return AsyncFileTestHelper::Move(file_system_context_.get(), src, dest);
276   }
277
278   base::File::Error SetUpTestCaseFiles(
279       const FileSystemURL& root,
280       const FileSystemTestCaseRecord* const test_cases,
281       size_t test_case_size) {
282     base::File::Error result = base::File::FILE_ERROR_FAILED;
283     for (size_t i = 0; i < test_case_size; ++i) {
284       const FileSystemTestCaseRecord& test_case = test_cases[i];
285       FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
286           root.origin(),
287           root.mount_type(),
288           root.virtual_path().Append(test_case.path));
289       if (test_case.is_directory)
290         result = CreateDirectory(url);
291       else
292         result = CreateFile(url, test_case.data_file_size);
293       EXPECT_EQ(base::File::FILE_OK, result) << url.DebugString();
294       if (result != base::File::FILE_OK)
295         return result;
296     }
297     return result;
298   }
299
300   void VerifyTestCaseFiles(
301       const FileSystemURL& root,
302       const FileSystemTestCaseRecord* const test_cases,
303       size_t test_case_size) {
304     std::map<base::FilePath, const FileSystemTestCaseRecord*> test_case_map;
305     for (size_t i = 0; i < test_case_size; ++i) {
306       test_case_map[
307           base::FilePath(test_cases[i].path).NormalizePathSeparators()] =
308               &test_cases[i];
309     }
310
311     std::queue<FileSystemURL> directories;
312     FileEntryList entries;
313     directories.push(root);
314     while (!directories.empty()) {
315       FileSystemURL dir = directories.front();
316       directories.pop();
317       ASSERT_EQ(base::File::FILE_OK, ReadDirectory(dir, &entries));
318       for (size_t i = 0; i < entries.size(); ++i) {
319         FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
320             dir.origin(),
321             dir.mount_type(),
322             dir.virtual_path().Append(entries[i].name));
323         base::FilePath relative;
324         root.virtual_path().AppendRelativePath(url.virtual_path(), &relative);
325         relative = relative.NormalizePathSeparators();
326         ASSERT_TRUE(ContainsKey(test_case_map, relative));
327         if (entries[i].is_directory) {
328           EXPECT_TRUE(test_case_map[relative]->is_directory);
329           directories.push(url);
330         } else {
331           EXPECT_FALSE(test_case_map[relative]->is_directory);
332           EXPECT_TRUE(FileExists(url, test_case_map[relative]->data_file_size));
333         }
334         test_case_map.erase(relative);
335       }
336     }
337     EXPECT_TRUE(test_case_map.empty());
338     std::map<base::FilePath,
339         const FileSystemTestCaseRecord*>::const_iterator it;
340     for (it = test_case_map.begin(); it != test_case_map.end(); ++it) {
341       LOG(ERROR) << "Extra entry: " << it->first.LossyDisplayName();
342     }
343   }
344
345   base::File::Error ReadDirectory(const FileSystemURL& url,
346                                   FileEntryList* entries) {
347     return AsyncFileTestHelper::ReadDirectory(
348         file_system_context_.get(), url, entries);
349   }
350
351   base::File::Error CreateDirectory(const FileSystemURL& url) {
352     return AsyncFileTestHelper::CreateDirectory(file_system_context_.get(),
353                                                 url);
354   }
355
356   base::File::Error CreateFile(const FileSystemURL& url, size_t size) {
357     base::File::Error result =
358         AsyncFileTestHelper::CreateFile(file_system_context_.get(), url);
359     if (result != base::File::FILE_OK)
360       return result;
361     return AsyncFileTestHelper::TruncateFile(
362         file_system_context_.get(), url, size);
363   }
364
365   bool FileExists(const FileSystemURL& url, int64 expected_size) {
366     return AsyncFileTestHelper::FileExists(
367         file_system_context_.get(), url, expected_size);
368   }
369
370   bool DirectoryExists(const FileSystemURL& url) {
371     return AsyncFileTestHelper::DirectoryExists(file_system_context_.get(),
372                                                 url);
373   }
374
375  private:
376   void GetUsageAndQuota(storage::FileSystemType type,
377                         int64* usage,
378                         int64* quota) {
379     storage::QuotaStatusCode status = AsyncFileTestHelper::GetUsageAndQuota(
380         quota_manager_.get(), origin_, type, usage, quota);
381     ASSERT_EQ(storage::kQuotaStatusOk, status);
382   }
383
384  private:
385   base::ScopedTempDir base_;
386
387   const GURL origin_;
388   const storage::FileSystemType src_type_;
389   const storage::FileSystemType dest_type_;
390
391   base::MessageLoopForIO message_loop_;
392   scoped_refptr<storage::FileSystemContext> file_system_context_;
393   scoped_refptr<MockQuotaManagerProxy> quota_manager_proxy_;
394   scoped_refptr<MockQuotaManager> quota_manager_;
395
396   DISALLOW_COPY_AND_ASSIGN(CopyOrMoveOperationTestHelper);
397 };
398
399 TEST(LocalFileSystemCopyOrMoveOperationTest, CopySingleFile) {
400   CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
401                                        storage::kFileSystemTypeTemporary,
402                                        storage::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                                        storage::kFileSystemTypeTemporary,
431                                        storage::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                                        storage::kFileSystemTypeTemporary,
460                                        storage::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                                        storage::kFileSystemTypeTemporary,
489                                        storage::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                                        storage::kFileSystemTypeTemporary,
518                                        storage::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                                       kRegularFileSystemTestCases,
531                                       kRegularFileSystemTestCaseSize));
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                              kRegularFileSystemTestCases,
546                              kRegularFileSystemTestCaseSize);
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                                        storage::kFileSystemTypeTemporary,
558                                        storage::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                                       kRegularFileSystemTestCases,
571                                       kRegularFileSystemTestCaseSize));
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                              kRegularFileSystemTestCases,
583                              kRegularFileSystemTestCaseSize);
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                                        storage::kFileSystemTypeTemporary,
596                                        storage::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                                       kRegularFileSystemTestCases,
607                                       kRegularFileSystemTestCaseSize));
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   FileSystemTestCaseRecord 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                                        storage::kFileSystemTypeTemporary,
629                                        storage::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                                        storage::kFileSystemTypeTemporary,
647                                        storage::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                                       kRegularFileSystemTestCases,
658                                       kRegularFileSystemTestCaseSize));
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 < kRegularFileSystemTestCaseSize; ++i) {
668     const FileSystemTestCaseRecord& test_case = kRegularFileSystemTestCases[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   base::FilePath dest_path = temp_dir.path().AppendASCII("dest");
720   const char kTestData[] = "abcdefghijklmnopqrstuvwxyz0123456789";
721   base::WriteFile(source_path, kTestData,
722                   arraysize(kTestData) - 1);  // Exclude trailing '\0'.
723
724   base::MessageLoopForIO message_loop;
725   base::Thread file_thread("file_thread");
726   ASSERT_TRUE(file_thread.Start());
727   ScopedThreadStopper thread_stopper(&file_thread);
728   ASSERT_TRUE(thread_stopper.is_valid());
729
730   scoped_refptr<base::MessageLoopProxy> task_runner =
731       file_thread.message_loop_proxy();
732
733   scoped_ptr<storage::FileStreamReader> reader(
734       storage::FileStreamReader::CreateForLocalFile(
735           task_runner.get(), source_path, 0, base::Time()));
736
737   scoped_ptr<FileStreamWriter> writer(FileStreamWriter::CreateForLocalFile(
738       task_runner.get(), dest_path, 0, FileStreamWriter::CREATE_NEW_FILE));
739
740   std::vector<int64> progress;
741   CopyOrMoveOperationDelegate::StreamCopyHelper helper(
742       reader.Pass(), writer.Pass(),
743       false,  // don't need flush
744       10,  // buffer size
745       base::Bind(&RecordFileProgressCallback, base::Unretained(&progress)),
746       base::TimeDelta());  // For testing, we need all the progress.
747
748   base::File::Error error = base::File::FILE_ERROR_FAILED;
749   base::RunLoop run_loop;
750   helper.Run(base::Bind(&AssignAndQuit, &run_loop, &error));
751   run_loop.Run();
752
753   EXPECT_EQ(base::File::FILE_OK, error);
754   ASSERT_EQ(5U, progress.size());
755   EXPECT_EQ(0, progress[0]);
756   EXPECT_EQ(10, progress[1]);
757   EXPECT_EQ(20, progress[2]);
758   EXPECT_EQ(30, progress[3]);
759   EXPECT_EQ(36, progress[4]);
760
761   std::string content;
762   ASSERT_TRUE(base::ReadFileToString(dest_path, &content));
763   EXPECT_EQ(kTestData, content);
764 }
765
766 TEST(LocalFileSystemCopyOrMoveOperationTest, StreamCopyHelperWithFlush) {
767   // Testing the same configuration as StreamCopyHelper, but with |need_flush|
768   // parameter set to true. Since it is hard to test that the flush is indeed
769   // taking place, this test just only verifies that the file is correctly
770   // written with or without the flag.
771   base::ScopedTempDir temp_dir;
772   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
773   base::FilePath source_path = temp_dir.path().AppendASCII("source");
774   base::FilePath dest_path = temp_dir.path().AppendASCII("dest");
775   const char kTestData[] = "abcdefghijklmnopqrstuvwxyz0123456789";
776   base::WriteFile(source_path, kTestData,
777                   arraysize(kTestData) - 1);  // Exclude trailing '\0'.
778
779
780   base::MessageLoopForIO message_loop;
781   base::Thread file_thread("file_thread");
782   ASSERT_TRUE(file_thread.Start());
783   ScopedThreadStopper thread_stopper(&file_thread);
784   ASSERT_TRUE(thread_stopper.is_valid());
785
786   scoped_refptr<base::MessageLoopProxy> task_runner =
787       file_thread.message_loop_proxy();
788
789   scoped_ptr<storage::FileStreamReader> reader(
790       storage::FileStreamReader::CreateForLocalFile(
791           task_runner.get(), source_path, 0, base::Time()));
792
793   scoped_ptr<FileStreamWriter> writer(FileStreamWriter::CreateForLocalFile(
794       task_runner.get(), dest_path, 0, FileStreamWriter::CREATE_NEW_FILE));
795
796   std::vector<int64> progress;
797   CopyOrMoveOperationDelegate::StreamCopyHelper helper(
798       reader.Pass(), writer.Pass(),
799       true,  // need flush
800       10,  // buffer size
801       base::Bind(&RecordFileProgressCallback, base::Unretained(&progress)),
802       base::TimeDelta());  // For testing, we need all the progress.
803
804   base::File::Error error = base::File::FILE_ERROR_FAILED;
805   base::RunLoop run_loop;
806   helper.Run(base::Bind(&AssignAndQuit, &run_loop, &error));
807   run_loop.Run();
808
809   EXPECT_EQ(base::File::FILE_OK, error);
810   ASSERT_EQ(5U, progress.size());
811   EXPECT_EQ(0, progress[0]);
812   EXPECT_EQ(10, progress[1]);
813   EXPECT_EQ(20, progress[2]);
814   EXPECT_EQ(30, progress[3]);
815   EXPECT_EQ(36, progress[4]);
816
817   std::string content;
818   ASSERT_TRUE(base::ReadFileToString(dest_path, &content));
819   EXPECT_EQ(kTestData, content);
820 }
821
822 TEST(LocalFileSystemCopyOrMoveOperationTest, StreamCopyHelper_Cancel) {
823   base::ScopedTempDir temp_dir;
824   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
825   base::FilePath source_path = temp_dir.path().AppendASCII("source");
826   base::FilePath dest_path = temp_dir.path().AppendASCII("dest");
827   const char kTestData[] = "abcdefghijklmnopqrstuvwxyz0123456789";
828   base::WriteFile(source_path, kTestData,
829                   arraysize(kTestData) - 1);  // Exclude trailing '\0'.
830
831   base::MessageLoopForIO message_loop;
832   base::Thread file_thread("file_thread");
833   ASSERT_TRUE(file_thread.Start());
834   ScopedThreadStopper thread_stopper(&file_thread);
835   ASSERT_TRUE(thread_stopper.is_valid());
836
837   scoped_refptr<base::MessageLoopProxy> task_runner =
838       file_thread.message_loop_proxy();
839
840   scoped_ptr<storage::FileStreamReader> reader(
841       storage::FileStreamReader::CreateForLocalFile(
842           task_runner.get(), source_path, 0, base::Time()));
843
844   scoped_ptr<FileStreamWriter> writer(FileStreamWriter::CreateForLocalFile(
845       task_runner.get(), dest_path, 0, FileStreamWriter::CREATE_NEW_FILE));
846
847   std::vector<int64> progress;
848   CopyOrMoveOperationDelegate::StreamCopyHelper helper(
849       reader.Pass(), writer.Pass(),
850       false,  // need_flush
851       10,  // buffer size
852       base::Bind(&RecordFileProgressCallback, base::Unretained(&progress)),
853       base::TimeDelta());  // For testing, we need all the progress.
854
855   // Call Cancel() later.
856   base::MessageLoopProxy::current()->PostTask(
857       FROM_HERE,
858       base::Bind(&CopyOrMoveOperationDelegate::StreamCopyHelper::Cancel,
859                  base::Unretained(&helper)));
860
861   base::File::Error error = base::File::FILE_ERROR_FAILED;
862   base::RunLoop run_loop;
863   helper.Run(base::Bind(&AssignAndQuit, &run_loop, &error));
864   run_loop.Run();
865
866   EXPECT_EQ(base::File::FILE_ERROR_ABORT, error);
867 }
868
869 }  // namespace content