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
33using content::AsyncFileTestHelper;
34using storage::CopyOrMoveOperationDelegate;
35using storage::FileStreamWriter;
36using storage::FileSystemOperation;
37using storage::FileSystemURL;
38
39namespace content {
40
41typedef storage::FileSystemOperation::FileEntryList FileEntryList;
42
43namespace {
44
45void 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
51class 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.
108struct ProgressRecord {
109  storage::FileSystemOperation::CopyProgressType type;
110  FileSystemURL source_url;
111  FileSystemURL dest_url;
112  int64 size;
113};
114
115void 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
128void RecordFileProgressCallback(std::vector<int64>* records,
129                                int64 progress) {
130  records->push_back(progress);
131}
132
133void 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
140class 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
165class 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
399TEST(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
428TEST(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
457TEST(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
486TEST(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
515TEST(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
555TEST(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
592TEST(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
626TEST(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
644TEST(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
715TEST(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
766TEST(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
822TEST(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
870