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.
8 #include "base/basictypes.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"
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;
43 typedef fileapi::FileSystemOperation::FileEntryList FileEntryList;
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);
53 class TestValidatorFactory : public fileapi::CopyOrMoveFileValidatorFactory {
55 // A factory that creates validators that accept everything or nothing.
56 TestValidatorFactory() {}
57 virtual ~TestValidatorFactory() {}
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"));
67 class TestValidator : public fileapi::CopyOrMoveFileValidator {
69 explicit TestValidator(bool pre_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) {
78 virtual ~TestValidator() {}
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_));
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;
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));
101 base::File::Error result_;
102 base::File::Error write_result_;
103 std::string reject_string_;
105 DISALLOW_COPY_AND_ASSIGN(TestValidator);
109 // Records CopyProgressCallback invocations.
110 struct ProgressRecord {
111 fileapi::FileSystemOperation::CopyProgressType type;
112 FileSystemURL source_url;
113 FileSystemURL dest_url;
117 void RecordProgressCallback(std::vector<ProgressRecord>* records,
118 fileapi::FileSystemOperation::CopyProgressType type,
119 const FileSystemURL& source_url,
120 const FileSystemURL& dest_url,
122 ProgressRecord record;
124 record.source_url = source_url;
125 record.dest_url = dest_url;
127 records->push_back(record);
130 void RecordFileProgressCallback(std::vector<int64>* records,
132 records->push_back(progress);
135 void AssignAndQuit(base::RunLoop* run_loop,
136 base::File::Error* result_out,
137 base::File::Error result) {
138 *result_out = result;
142 class ScopedThreadStopper {
144 ScopedThreadStopper(base::Thread* thread) : thread_(thread) {
147 ~ScopedThreadStopper() {
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());
158 bool is_valid() const { return thread_; }
161 base::Thread* thread_;
162 DISALLOW_COPY_AND_ASSIGN(ScopedThreadStopper);
167 class CopyOrMoveOperationTestHelper {
169 CopyOrMoveOperationTestHelper(
171 FileSystemType src_type,
172 FileSystemType dest_type)
175 dest_type_(dest_type) {}
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();
189 void SetUpNoValidator() {
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();
198 new quota::MockQuotaManager(false /* is_incognito */,
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);
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());
225 backend->OpenFileSystem(origin_, dest_type_,
226 fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
227 base::Bind(&ExpectOk));
228 base::RunLoop().RunUntilIdle();
230 // Grant relatively big quota initially.
231 quota_manager_->SetQuota(
233 fileapi::FileSystemTypeToQuotaStorageType(src_type_),
235 quota_manager_->SetQuota(
237 fileapi::FileSystemTypeToQuotaStorageType(dest_type_),
241 int64 GetSourceUsage() {
243 GetUsageAndQuota(src_type_, &usage, NULL);
247 int64 GetDestUsage() {
249 GetUsageAndQuota(dest_type_, &usage, NULL);
253 FileSystemURL SourceURL(const std::string& path) {
254 return file_system_context_->CreateCrackedFileSystemURL(
255 origin_, src_type_, base::FilePath::FromUTF8Unsafe(path));
258 FileSystemURL DestURL(const std::string& path) {
259 return file_system_context_->CreateCrackedFileSystemURL(
260 origin_, dest_type_, base::FilePath::FromUTF8Unsafe(path));
263 base::File::Error Copy(const FileSystemURL& src,
264 const FileSystemURL& dest) {
265 return AsyncFileTestHelper::Copy(file_system_context_.get(), src, dest);
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);
276 base::File::Error Move(const FileSystemURL& src,
277 const FileSystemURL& dest) {
278 return AsyncFileTestHelper::Move(file_system_context_.get(), src, dest);
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(
291 root.virtual_path().Append(test_case.path));
292 if (test_case.is_directory)
293 result = CreateDirectory(url);
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)
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) {
310 base::FilePath(test_cases[i].path).NormalizePathSeparators()] =
314 std::queue<FileSystemURL> directories;
315 FileEntryList entries;
316 directories.push(root);
317 while (!directories.empty()) {
318 FileSystemURL dir = directories.front();
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(
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);
334 EXPECT_FALSE(test_case_map[relative]->is_directory);
335 EXPECT_TRUE(FileExists(url, test_case_map[relative]->data_file_size));
337 test_case_map.erase(relative);
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();
347 base::File::Error ReadDirectory(const FileSystemURL& url,
348 FileEntryList* entries) {
349 return AsyncFileTestHelper::ReadDirectory(
350 file_system_context_.get(), url, entries);
353 base::File::Error CreateDirectory(const FileSystemURL& url) {
354 return AsyncFileTestHelper::CreateDirectory(file_system_context_.get(),
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)
363 return AsyncFileTestHelper::TruncateFile(
364 file_system_context_.get(), url, size);
367 bool FileExists(const FileSystemURL& url, int64 expected_size) {
368 return AsyncFileTestHelper::FileExists(
369 file_system_context_.get(), url, expected_size);
372 bool DirectoryExists(const FileSystemURL& url) {
373 return AsyncFileTestHelper::DirectoryExists(file_system_context_.get(),
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);
385 base::ScopedTempDir base_;
388 const FileSystemType src_type_;
389 const FileSystemType dest_type_;
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_;
396 DISALLOW_COPY_AND_ASSIGN(CopyOrMoveOperationTestHelper);
399 TEST(LocalFileSystemCopyOrMoveOperationTest, CopySingleFile) {
400 CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
401 fileapi::kFileSystemTypeTemporary,
402 fileapi::kFileSystemTypePersistent);
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();
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;
415 ASSERT_EQ(base::File::FILE_OK, helper.Copy(src, dest));
418 ASSERT_TRUE(helper.FileExists(src, 10));
419 ASSERT_TRUE(helper.FileExists(dest, 10));
421 int64 src_new_usage = helper.GetSourceUsage();
422 ASSERT_EQ(src_initial_usage + src_increase, src_new_usage);
424 int64 dest_increase = helper.GetDestUsage() - dest_initial_usage;
425 ASSERT_EQ(src_increase, dest_increase);
428 TEST(LocalFileSystemCopyOrMoveOperationTest, MoveSingleFile) {
429 CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
430 fileapi::kFileSystemTypeTemporary,
431 fileapi::kFileSystemTypePersistent);
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();
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;
444 ASSERT_EQ(base::File::FILE_OK, helper.Move(src, dest));
447 ASSERT_FALSE(helper.FileExists(src, AsyncFileTestHelper::kDontCheckSize));
448 ASSERT_TRUE(helper.FileExists(dest, 10));
450 int64 src_new_usage = helper.GetSourceUsage();
451 ASSERT_EQ(src_initial_usage, src_new_usage);
453 int64 dest_increase = helper.GetDestUsage() - dest_initial_usage;
454 ASSERT_EQ(src_increase, dest_increase);
457 TEST(LocalFileSystemCopyOrMoveOperationTest, CopySingleDirectory) {
458 CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
459 fileapi::kFileSystemTypeTemporary,
460 fileapi::kFileSystemTypePersistent);
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();
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;
473 ASSERT_EQ(base::File::FILE_OK, helper.Copy(src, dest));
476 ASSERT_TRUE(helper.DirectoryExists(src));
477 ASSERT_TRUE(helper.DirectoryExists(dest));
479 int64 src_new_usage = helper.GetSourceUsage();
480 ASSERT_EQ(src_initial_usage + src_increase, src_new_usage);
482 int64 dest_increase = helper.GetDestUsage() - dest_initial_usage;
483 ASSERT_EQ(src_increase, dest_increase);
486 TEST(LocalFileSystemCopyOrMoveOperationTest, MoveSingleDirectory) {
487 CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
488 fileapi::kFileSystemTypeTemporary,
489 fileapi::kFileSystemTypePersistent);
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();
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;
502 ASSERT_EQ(base::File::FILE_OK, helper.Move(src, dest));
505 ASSERT_FALSE(helper.DirectoryExists(src));
506 ASSERT_TRUE(helper.DirectoryExists(dest));
508 int64 src_new_usage = helper.GetSourceUsage();
509 ASSERT_EQ(src_initial_usage, src_new_usage);
511 int64 dest_increase = helper.GetDestUsage() - dest_initial_usage;
512 ASSERT_EQ(src_increase, dest_increase);
515 TEST(LocalFileSystemCopyOrMoveOperationTest, CopyDirectory) {
516 CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
517 fileapi::kFileSystemTypeTemporary,
518 fileapi::kFileSystemTypePersistent);
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();
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;
535 ASSERT_EQ(base::File::FILE_OK,
536 helper.CopyWithProgress(
538 AsyncFileTestHelper::CopyProgressCallback()));
541 ASSERT_TRUE(helper.DirectoryExists(src));
542 ASSERT_TRUE(helper.DirectoryExists(dest));
544 helper.VerifyTestCaseFiles(dest,
545 fileapi::test::kRegularTestCases,
546 fileapi::test::kRegularTestCaseSize);
548 int64 src_new_usage = helper.GetSourceUsage();
549 ASSERT_EQ(src_initial_usage + src_increase, src_new_usage);
551 int64 dest_increase = helper.GetDestUsage() - dest_initial_usage;
552 ASSERT_EQ(src_increase, dest_increase);
555 TEST(LocalFileSystemCopyOrMoveOperationTest, MoveDirectory) {
556 CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
557 fileapi::kFileSystemTypeTemporary,
558 fileapi::kFileSystemTypePersistent);
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();
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;
575 ASSERT_EQ(base::File::FILE_OK, helper.Move(src, dest));
578 ASSERT_FALSE(helper.DirectoryExists(src));
579 ASSERT_TRUE(helper.DirectoryExists(dest));
581 helper.VerifyTestCaseFiles(dest,
582 fileapi::test::kRegularTestCases,
583 fileapi::test::kRegularTestCaseSize);
585 int64 src_new_usage = helper.GetSourceUsage();
586 ASSERT_EQ(src_initial_usage, src_new_usage);
588 int64 dest_increase = helper.GetDestUsage() - dest_initial_usage;
589 ASSERT_EQ(src_increase, dest_increase);
592 TEST(LocalFileSystemCopyOrMoveOperationTest,
593 MoveDirectoryFailPostWriteValidation) {
594 CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
595 fileapi::kFileSystemTypeTemporary,
596 fileapi::kFileSystemTypeTest);
599 FileSystemURL src = helper.SourceURL("a");
600 FileSystemURL dest = helper.DestURL("b");
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));
610 helper.Move(src, dest);
613 ASSERT_TRUE(helper.DirectoryExists(src));
614 ASSERT_TRUE(helper.DirectoryExists(dest));
616 TestCaseRecord kMoveDirResultCases[] = {
617 {false, FILE_PATH_LITERAL("file 0"), 38},
618 {false, FILE_PATH_LITERAL("file 3"), 0},
621 helper.VerifyTestCaseFiles(dest,
623 arraysize(kMoveDirResultCases));
626 TEST(LocalFileSystemCopyOrMoveOperationTest, CopySingleFileNoValidator) {
627 CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
628 fileapi::kFileSystemTypeTemporary,
629 fileapi::kFileSystemTypeTest);
630 helper.SetUpNoValidator();
632 FileSystemURL src = helper.SourceURL("a");
633 FileSystemURL dest = helper.DestURL("b");
635 // Set up a source file.
636 ASSERT_EQ(base::File::FILE_OK, helper.CreateFile(src, 10));
638 // The copy attempt should fail with a security error -- getting
639 // the factory returns a security error, and the copy operation must
641 ASSERT_EQ(base::File::FILE_ERROR_SECURITY, helper.Copy(src, dest));
644 TEST(LocalFileSystemCopyOrMoveOperationTest, ProgressCallback) {
645 CopyOrMoveOperationTestHelper helper(GURL("http://foo"),
646 fileapi::kFileSystemTypeTemporary,
647 fileapi::kFileSystemTypePersistent);
650 FileSystemURL src = helper.SourceURL("a");
651 FileSystemURL dest = helper.DestURL("b");
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));
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))));
666 // Verify progress callback.
667 for (size_t i = 0; i < fileapi::test::kRegularTestCaseSize; ++i) {
668 const TestCaseRecord& test_case = fileapi::test::kRegularTestCases[i];
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());
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())
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);
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);
697 if (test_case.is_directory) {
698 // For directory copy, the progress shouldn't be interlaced.
699 EXPECT_EQ(begin_index + 1, end_index);
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;
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'.
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);
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());
733 scoped_refptr<base::MessageLoopProxy> task_runner =
734 file_thread.message_loop_proxy();
736 scoped_ptr<webkit_blob::FileStreamReader> reader(
737 webkit_blob::FileStreamReader::CreateForLocalFile(
738 task_runner.get(), source_path, 0, base::Time()));
740 scoped_ptr<FileStreamWriter> writer(
741 FileStreamWriter::CreateForLocalFile(task_runner.get(), dest_path, 0));
743 std::vector<int64> progress;
744 CopyOrMoveOperationDelegate::StreamCopyHelper helper(
745 reader.Pass(), writer.Pass(),
746 false, // don't need flush
748 base::Bind(&RecordFileProgressCallback, base::Unretained(&progress)),
749 base::TimeDelta()); // For testing, we need all the progress.
751 base::File::Error error = base::File::FILE_ERROR_FAILED;
752 base::RunLoop run_loop;
753 helper.Run(base::Bind(&AssignAndQuit, &run_loop, &error));
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]);
765 ASSERT_TRUE(base::ReadFileToString(dest_path, &content));
766 EXPECT_EQ(kTestData, content);
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'.
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);
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());
791 scoped_refptr<base::MessageLoopProxy> task_runner =
792 file_thread.message_loop_proxy();
794 scoped_ptr<webkit_blob::FileStreamReader> reader(
795 webkit_blob::FileStreamReader::CreateForLocalFile(
796 task_runner.get(), source_path, 0, base::Time()));
798 scoped_ptr<FileStreamWriter> writer(
799 FileStreamWriter::CreateForLocalFile(task_runner.get(), dest_path, 0));
801 std::vector<int64> progress;
802 CopyOrMoveOperationDelegate::StreamCopyHelper helper(
803 reader.Pass(), writer.Pass(),
806 base::Bind(&RecordFileProgressCallback, base::Unretained(&progress)),
807 base::TimeDelta()); // For testing, we need all the progress.
809 base::File::Error error = base::File::FILE_ERROR_FAILED;
810 base::RunLoop run_loop;
811 helper.Run(base::Bind(&AssignAndQuit, &run_loop, &error));
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]);
823 ASSERT_TRUE(base::ReadFileToString(dest_path, &content));
824 EXPECT_EQ(kTestData, content);
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'.
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);
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());
845 scoped_refptr<base::MessageLoopProxy> task_runner =
846 file_thread.message_loop_proxy();
848 scoped_ptr<webkit_blob::FileStreamReader> reader(
849 webkit_blob::FileStreamReader::CreateForLocalFile(
850 task_runner.get(), source_path, 0, base::Time()));
852 scoped_ptr<FileStreamWriter> writer(
853 FileStreamWriter::CreateForLocalFile(task_runner.get(), dest_path, 0));
855 std::vector<int64> progress;
856 CopyOrMoveOperationDelegate::StreamCopyHelper helper(
857 reader.Pass(), writer.Pass(),
860 base::Bind(&RecordFileProgressCallback, base::Unretained(&progress)),
861 base::TimeDelta()); // For testing, we need all the progress.
863 // Call Cancel() later.
864 base::MessageLoopProxy::current()->PostTask(
866 base::Bind(&CopyOrMoveOperationDelegate::StreamCopyHelper::Cancel,
867 base::Unretained(&helper)));
869 base::File::Error error = base::File::FILE_ERROR_FAILED;
870 base::RunLoop run_loop;
871 helper.Run(base::Bind(&AssignAndQuit, &run_loop, &error));
874 EXPECT_EQ(base::File::FILE_ERROR_ABORT, error);
877 } // namespace content