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 "content/test/fileapi_test_file_set.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20 #include "webkit/browser/blob/file_stream_reader.h"
21 #include "webkit/browser/fileapi/copy_or_move_file_validator.h"
22 #include "webkit/browser/fileapi/copy_or_move_operation_delegate.h"
23 #include "webkit/browser/fileapi/file_stream_writer.h"
24 #include "webkit/browser/fileapi/file_system_backend.h"
25 #include "webkit/browser/fileapi/file_system_context.h"
26 #include "webkit/browser/fileapi/file_system_operation.h"
27 #include "webkit/browser/fileapi/file_system_url.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
;
42 typedef fileapi::FileSystemOperation::FileEntryList FileEntryList
;
46 void ExpectOk(const GURL
& origin_url
,
47 const std::string
& name
,
48 base::File::Error error
) {
49 ASSERT_EQ(base::File::FILE_OK
, error
);
52 class TestValidatorFactory
: public fileapi::CopyOrMoveFileValidatorFactory
{
54 // A factory that creates validators that accept everything or nothing.
55 TestValidatorFactory() {}
56 virtual ~TestValidatorFactory() {}
58 virtual fileapi::CopyOrMoveFileValidator
* CreateCopyOrMoveFileValidator(
59 const FileSystemURL
& /*src_url*/,
60 const base::FilePath
& /*platform_path*/) OVERRIDE
{
61 // Move arg management to TestValidator?
62 return new TestValidator(true, true, std::string("2"));
66 class TestValidator
: public fileapi::CopyOrMoveFileValidator
{
68 explicit TestValidator(bool pre_copy_valid
,
70 const std::string
& reject_string
)
71 : result_(pre_copy_valid
? base::File::FILE_OK
:
72 base::File::FILE_ERROR_SECURITY
),
73 write_result_(post_copy_valid
? base::File::FILE_OK
:
74 base::File::FILE_ERROR_SECURITY
),
75 reject_string_(reject_string
) {
77 virtual ~TestValidator() {}
79 virtual void StartPreWriteValidation(
80 const ResultCallback
& result_callback
) OVERRIDE
{
81 // Post the result since a real validator must do work asynchronously.
82 base::MessageLoop::current()->PostTask(
83 FROM_HERE
, base::Bind(result_callback
, result_
));
86 virtual void StartPostWriteValidation(
87 const base::FilePath
& dest_platform_path
,
88 const ResultCallback
& result_callback
) OVERRIDE
{
89 base::File::Error result
= write_result_
;
90 std::string unsafe
= dest_platform_path
.BaseName().AsUTF8Unsafe();
91 if (unsafe
.find(reject_string_
) != std::string::npos
) {
92 result
= base::File::FILE_ERROR_SECURITY
;
94 // Post the result since a real validator must do work asynchronously.
95 base::MessageLoop::current()->PostTask(
96 FROM_HERE
, base::Bind(result_callback
, result
));
100 base::File::Error result_
;
101 base::File::Error write_result_
;
102 std::string reject_string_
;
104 DISALLOW_COPY_AND_ASSIGN(TestValidator
);
108 // Records CopyProgressCallback invocations.
109 struct ProgressRecord
{
110 fileapi::FileSystemOperation::CopyProgressType type
;
111 FileSystemURL source_url
;
112 FileSystemURL dest_url
;
116 void RecordProgressCallback(std::vector
<ProgressRecord
>* records
,
117 fileapi::FileSystemOperation::CopyProgressType type
,
118 const FileSystemURL
& source_url
,
119 const FileSystemURL
& dest_url
,
121 ProgressRecord record
;
123 record
.source_url
= source_url
;
124 record
.dest_url
= dest_url
;
126 records
->push_back(record
);
129 void RecordFileProgressCallback(std::vector
<int64
>* records
,
131 records
->push_back(progress
);
134 void AssignAndQuit(base::RunLoop
* run_loop
,
135 base::File::Error
* result_out
,
136 base::File::Error result
) {
137 *result_out
= result
;
141 class ScopedThreadStopper
{
143 ScopedThreadStopper(base::Thread
* thread
) : thread_(thread
) {
146 ~ScopedThreadStopper() {
148 // Give another chance for deleted streams to perform Close.
149 base::RunLoop run_loop
;
150 thread_
->message_loop_proxy()->PostTaskAndReply(
151 FROM_HERE
, base::Bind(&base::DoNothing
), run_loop
.QuitClosure());
157 bool is_valid() const { return thread_
; }
160 base::Thread
* thread_
;
161 DISALLOW_COPY_AND_ASSIGN(ScopedThreadStopper
);
166 class CopyOrMoveOperationTestHelper
{
168 CopyOrMoveOperationTestHelper(
170 FileSystemType src_type
,
171 FileSystemType dest_type
)
174 dest_type_(dest_type
) {}
176 ~CopyOrMoveOperationTestHelper() {
177 file_system_context_
= NULL
;
178 quota_manager_proxy_
->SimulateQuotaManagerDestroyed();
179 quota_manager_
= NULL
;
180 quota_manager_proxy_
= NULL
;
181 base::RunLoop().RunUntilIdle();
188 void SetUpNoValidator() {
192 void SetUp(bool require_copy_or_move_validator
,
193 bool init_copy_or_move_validator
) {
194 ASSERT_TRUE(base_
.CreateUniqueTempDir());
195 base::FilePath base_dir
= base_
.path();
197 new quota::MockQuotaManager(false /* is_incognito */,
199 base::MessageLoopProxy::current().get(),
200 base::MessageLoopProxy::current().get(),
201 NULL
/* special storage policy */);
202 quota_manager_proxy_
= new quota::MockQuotaManagerProxy(
203 quota_manager_
.get(), base::MessageLoopProxy::current().get());
204 file_system_context_
=
205 CreateFileSystemContextForTesting(quota_manager_proxy_
.get(), base_dir
);
207 // Prepare the origin's root directory.
208 fileapi::FileSystemBackend
* backend
=
209 file_system_context_
->GetFileSystemBackend(src_type_
);
211 FileSystemURL::CreateForTest(origin_
, src_type_
, base::FilePath()),
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());
226 FileSystemURL::CreateForTest(origin_
, dest_type_
, base::FilePath()),
227 fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT
,
228 base::Bind(&ExpectOk
));
229 base::RunLoop().RunUntilIdle();
231 // Grant relatively big quota initially.
232 quota_manager_
->SetQuota(
234 fileapi::FileSystemTypeToQuotaStorageType(src_type_
),
236 quota_manager_
->SetQuota(
238 fileapi::FileSystemTypeToQuotaStorageType(dest_type_
),
242 int64
GetSourceUsage() {
244 GetUsageAndQuota(src_type_
, &usage
, NULL
);
248 int64
GetDestUsage() {
250 GetUsageAndQuota(dest_type_
, &usage
, NULL
);
254 FileSystemURL
SourceURL(const std::string
& path
) {
255 return file_system_context_
->CreateCrackedFileSystemURL(
256 origin_
, src_type_
, base::FilePath::FromUTF8Unsafe(path
));
259 FileSystemURL
DestURL(const std::string
& path
) {
260 return file_system_context_
->CreateCrackedFileSystemURL(
261 origin_
, dest_type_
, base::FilePath::FromUTF8Unsafe(path
));
264 base::File::Error
Copy(const FileSystemURL
& src
,
265 const FileSystemURL
& dest
) {
266 return AsyncFileTestHelper::Copy(file_system_context_
.get(), src
, dest
);
269 base::File::Error
CopyWithProgress(
270 const FileSystemURL
& src
,
271 const FileSystemURL
& dest
,
272 const AsyncFileTestHelper::CopyProgressCallback
& progress_callback
) {
273 return AsyncFileTestHelper::CopyWithProgress(
274 file_system_context_
.get(), src
, dest
, progress_callback
);
277 base::File::Error
Move(const FileSystemURL
& src
,
278 const FileSystemURL
& dest
) {
279 return AsyncFileTestHelper::Move(file_system_context_
.get(), src
, dest
);
282 base::File::Error
SetUpTestCaseFiles(
283 const FileSystemURL
& root
,
284 const FileSystemTestCaseRecord
* const test_cases
,
285 size_t test_case_size
) {
286 base::File::Error result
= base::File::FILE_ERROR_FAILED
;
287 for (size_t i
= 0; i
< test_case_size
; ++i
) {
288 const FileSystemTestCaseRecord
& test_case
= test_cases
[i
];
289 FileSystemURL url
= file_system_context_
->CreateCrackedFileSystemURL(
292 root
.virtual_path().Append(test_case
.path
));
293 if (test_case
.is_directory
)
294 result
= CreateDirectory(url
);
296 result
= CreateFile(url
, test_case
.data_file_size
);
297 EXPECT_EQ(base::File::FILE_OK
, result
) << url
.DebugString();
298 if (result
!= base::File::FILE_OK
)
304 void VerifyTestCaseFiles(
305 const FileSystemURL
& root
,
306 const FileSystemTestCaseRecord
* const test_cases
,
307 size_t test_case_size
) {
308 std::map
<base::FilePath
, const FileSystemTestCaseRecord
*> test_case_map
;
309 for (size_t i
= 0; i
< test_case_size
; ++i
) {
311 base::FilePath(test_cases
[i
].path
).NormalizePathSeparators()] =
315 std::queue
<FileSystemURL
> directories
;
316 FileEntryList entries
;
317 directories
.push(root
);
318 while (!directories
.empty()) {
319 FileSystemURL dir
= directories
.front();
321 ASSERT_EQ(base::File::FILE_OK
, ReadDirectory(dir
, &entries
));
322 for (size_t i
= 0; i
< entries
.size(); ++i
) {
323 FileSystemURL url
= file_system_context_
->CreateCrackedFileSystemURL(
326 dir
.virtual_path().Append(entries
[i
].name
));
327 base::FilePath relative
;
328 root
.virtual_path().AppendRelativePath(url
.virtual_path(), &relative
);
329 relative
= relative
.NormalizePathSeparators();
330 ASSERT_TRUE(ContainsKey(test_case_map
, relative
));
331 if (entries
[i
].is_directory
) {
332 EXPECT_TRUE(test_case_map
[relative
]->is_directory
);
333 directories
.push(url
);
335 EXPECT_FALSE(test_case_map
[relative
]->is_directory
);
336 EXPECT_TRUE(FileExists(url
, test_case_map
[relative
]->data_file_size
));
338 test_case_map
.erase(relative
);
341 EXPECT_TRUE(test_case_map
.empty());
342 std::map
<base::FilePath
,
343 const FileSystemTestCaseRecord
*>::const_iterator it
;
344 for (it
= test_case_map
.begin(); it
!= test_case_map
.end(); ++it
) {
345 LOG(ERROR
) << "Extra entry: " << it
->first
.LossyDisplayName();
349 base::File::Error
ReadDirectory(const FileSystemURL
& url
,
350 FileEntryList
* entries
) {
351 return AsyncFileTestHelper::ReadDirectory(
352 file_system_context_
.get(), url
, entries
);
355 base::File::Error
CreateDirectory(const FileSystemURL
& url
) {
356 return AsyncFileTestHelper::CreateDirectory(file_system_context_
.get(),
360 base::File::Error
CreateFile(const FileSystemURL
& url
, size_t size
) {
361 base::File::Error result
=
362 AsyncFileTestHelper::CreateFile(file_system_context_
.get(), url
);
363 if (result
!= base::File::FILE_OK
)
365 return AsyncFileTestHelper::TruncateFile(
366 file_system_context_
.get(), url
, size
);
369 bool FileExists(const FileSystemURL
& url
, int64 expected_size
) {
370 return AsyncFileTestHelper::FileExists(
371 file_system_context_
.get(), url
, expected_size
);
374 bool DirectoryExists(const FileSystemURL
& url
) {
375 return AsyncFileTestHelper::DirectoryExists(file_system_context_
.get(),
380 void GetUsageAndQuota(FileSystemType type
, int64
* usage
, int64
* quota
) {
381 quota::QuotaStatusCode status
= AsyncFileTestHelper::GetUsageAndQuota(
382 quota_manager_
.get(), origin_
, type
, usage
, quota
);
383 ASSERT_EQ(quota::kQuotaStatusOk
, status
);
387 base::ScopedTempDir base_
;
390 const FileSystemType src_type_
;
391 const FileSystemType dest_type_
;
393 base::MessageLoopForIO message_loop_
;
394 scoped_refptr
<fileapi::FileSystemContext
> file_system_context_
;
395 scoped_refptr
<quota::MockQuotaManagerProxy
> quota_manager_proxy_
;
396 scoped_refptr
<quota::MockQuotaManager
> quota_manager_
;
398 DISALLOW_COPY_AND_ASSIGN(CopyOrMoveOperationTestHelper
);
401 TEST(LocalFileSystemCopyOrMoveOperationTest
, CopySingleFile
) {
402 CopyOrMoveOperationTestHelper
helper(GURL("http://foo"),
403 fileapi::kFileSystemTypeTemporary
,
404 fileapi::kFileSystemTypePersistent
);
407 FileSystemURL src
= helper
.SourceURL("a");
408 FileSystemURL dest
= helper
.DestURL("b");
409 int64 src_initial_usage
= helper
.GetSourceUsage();
410 int64 dest_initial_usage
= helper
.GetDestUsage();
412 // Set up a source file.
413 ASSERT_EQ(base::File::FILE_OK
, helper
.CreateFile(src
, 10));
414 int64 src_increase
= helper
.GetSourceUsage() - src_initial_usage
;
417 ASSERT_EQ(base::File::FILE_OK
, helper
.Copy(src
, dest
));
420 ASSERT_TRUE(helper
.FileExists(src
, 10));
421 ASSERT_TRUE(helper
.FileExists(dest
, 10));
423 int64 src_new_usage
= helper
.GetSourceUsage();
424 ASSERT_EQ(src_initial_usage
+ src_increase
, src_new_usage
);
426 int64 dest_increase
= helper
.GetDestUsage() - dest_initial_usage
;
427 ASSERT_EQ(src_increase
, dest_increase
);
430 TEST(LocalFileSystemCopyOrMoveOperationTest
, MoveSingleFile
) {
431 CopyOrMoveOperationTestHelper
helper(GURL("http://foo"),
432 fileapi::kFileSystemTypeTemporary
,
433 fileapi::kFileSystemTypePersistent
);
436 FileSystemURL src
= helper
.SourceURL("a");
437 FileSystemURL dest
= helper
.DestURL("b");
438 int64 src_initial_usage
= helper
.GetSourceUsage();
439 int64 dest_initial_usage
= helper
.GetDestUsage();
441 // Set up a source file.
442 ASSERT_EQ(base::File::FILE_OK
, helper
.CreateFile(src
, 10));
443 int64 src_increase
= helper
.GetSourceUsage() - src_initial_usage
;
446 ASSERT_EQ(base::File::FILE_OK
, helper
.Move(src
, dest
));
449 ASSERT_FALSE(helper
.FileExists(src
, AsyncFileTestHelper::kDontCheckSize
));
450 ASSERT_TRUE(helper
.FileExists(dest
, 10));
452 int64 src_new_usage
= helper
.GetSourceUsage();
453 ASSERT_EQ(src_initial_usage
, src_new_usage
);
455 int64 dest_increase
= helper
.GetDestUsage() - dest_initial_usage
;
456 ASSERT_EQ(src_increase
, dest_increase
);
459 TEST(LocalFileSystemCopyOrMoveOperationTest
, CopySingleDirectory
) {
460 CopyOrMoveOperationTestHelper
helper(GURL("http://foo"),
461 fileapi::kFileSystemTypeTemporary
,
462 fileapi::kFileSystemTypePersistent
);
465 FileSystemURL src
= helper
.SourceURL("a");
466 FileSystemURL dest
= helper
.DestURL("b");
467 int64 src_initial_usage
= helper
.GetSourceUsage();
468 int64 dest_initial_usage
= helper
.GetDestUsage();
470 // Set up a source directory.
471 ASSERT_EQ(base::File::FILE_OK
, helper
.CreateDirectory(src
));
472 int64 src_increase
= helper
.GetSourceUsage() - src_initial_usage
;
475 ASSERT_EQ(base::File::FILE_OK
, helper
.Copy(src
, dest
));
478 ASSERT_TRUE(helper
.DirectoryExists(src
));
479 ASSERT_TRUE(helper
.DirectoryExists(dest
));
481 int64 src_new_usage
= helper
.GetSourceUsage();
482 ASSERT_EQ(src_initial_usage
+ src_increase
, src_new_usage
);
484 int64 dest_increase
= helper
.GetDestUsage() - dest_initial_usage
;
485 ASSERT_EQ(src_increase
, dest_increase
);
488 TEST(LocalFileSystemCopyOrMoveOperationTest
, MoveSingleDirectory
) {
489 CopyOrMoveOperationTestHelper
helper(GURL("http://foo"),
490 fileapi::kFileSystemTypeTemporary
,
491 fileapi::kFileSystemTypePersistent
);
494 FileSystemURL src
= helper
.SourceURL("a");
495 FileSystemURL dest
= helper
.DestURL("b");
496 int64 src_initial_usage
= helper
.GetSourceUsage();
497 int64 dest_initial_usage
= helper
.GetDestUsage();
499 // Set up a source directory.
500 ASSERT_EQ(base::File::FILE_OK
, helper
.CreateDirectory(src
));
501 int64 src_increase
= helper
.GetSourceUsage() - src_initial_usage
;
504 ASSERT_EQ(base::File::FILE_OK
, helper
.Move(src
, dest
));
507 ASSERT_FALSE(helper
.DirectoryExists(src
));
508 ASSERT_TRUE(helper
.DirectoryExists(dest
));
510 int64 src_new_usage
= helper
.GetSourceUsage();
511 ASSERT_EQ(src_initial_usage
, src_new_usage
);
513 int64 dest_increase
= helper
.GetDestUsage() - dest_initial_usage
;
514 ASSERT_EQ(src_increase
, dest_increase
);
517 TEST(LocalFileSystemCopyOrMoveOperationTest
, CopyDirectory
) {
518 CopyOrMoveOperationTestHelper
helper(GURL("http://foo"),
519 fileapi::kFileSystemTypeTemporary
,
520 fileapi::kFileSystemTypePersistent
);
523 FileSystemURL src
= helper
.SourceURL("a");
524 FileSystemURL dest
= helper
.DestURL("b");
525 int64 src_initial_usage
= helper
.GetSourceUsage();
526 int64 dest_initial_usage
= helper
.GetDestUsage();
528 // Set up a source directory.
529 ASSERT_EQ(base::File::FILE_OK
, helper
.CreateDirectory(src
));
530 ASSERT_EQ(base::File::FILE_OK
,
531 helper
.SetUpTestCaseFiles(src
,
532 kRegularFileSystemTestCases
,
533 kRegularFileSystemTestCaseSize
));
534 int64 src_increase
= helper
.GetSourceUsage() - src_initial_usage
;
537 ASSERT_EQ(base::File::FILE_OK
,
538 helper
.CopyWithProgress(
540 AsyncFileTestHelper::CopyProgressCallback()));
543 ASSERT_TRUE(helper
.DirectoryExists(src
));
544 ASSERT_TRUE(helper
.DirectoryExists(dest
));
546 helper
.VerifyTestCaseFiles(dest
,
547 kRegularFileSystemTestCases
,
548 kRegularFileSystemTestCaseSize
);
550 int64 src_new_usage
= helper
.GetSourceUsage();
551 ASSERT_EQ(src_initial_usage
+ src_increase
, src_new_usage
);
553 int64 dest_increase
= helper
.GetDestUsage() - dest_initial_usage
;
554 ASSERT_EQ(src_increase
, dest_increase
);
557 TEST(LocalFileSystemCopyOrMoveOperationTest
, MoveDirectory
) {
558 CopyOrMoveOperationTestHelper
helper(GURL("http://foo"),
559 fileapi::kFileSystemTypeTemporary
,
560 fileapi::kFileSystemTypePersistent
);
563 FileSystemURL src
= helper
.SourceURL("a");
564 FileSystemURL dest
= helper
.DestURL("b");
565 int64 src_initial_usage
= helper
.GetSourceUsage();
566 int64 dest_initial_usage
= helper
.GetDestUsage();
568 // Set up a source directory.
569 ASSERT_EQ(base::File::FILE_OK
, helper
.CreateDirectory(src
));
570 ASSERT_EQ(base::File::FILE_OK
,
571 helper
.SetUpTestCaseFiles(src
,
572 kRegularFileSystemTestCases
,
573 kRegularFileSystemTestCaseSize
));
574 int64 src_increase
= helper
.GetSourceUsage() - src_initial_usage
;
577 ASSERT_EQ(base::File::FILE_OK
, helper
.Move(src
, dest
));
580 ASSERT_FALSE(helper
.DirectoryExists(src
));
581 ASSERT_TRUE(helper
.DirectoryExists(dest
));
583 helper
.VerifyTestCaseFiles(dest
,
584 kRegularFileSystemTestCases
,
585 kRegularFileSystemTestCaseSize
);
587 int64 src_new_usage
= helper
.GetSourceUsage();
588 ASSERT_EQ(src_initial_usage
, src_new_usage
);
590 int64 dest_increase
= helper
.GetDestUsage() - dest_initial_usage
;
591 ASSERT_EQ(src_increase
, dest_increase
);
594 TEST(LocalFileSystemCopyOrMoveOperationTest
,
595 MoveDirectoryFailPostWriteValidation
) {
596 CopyOrMoveOperationTestHelper
helper(GURL("http://foo"),
597 fileapi::kFileSystemTypeTemporary
,
598 fileapi::kFileSystemTypeTest
);
601 FileSystemURL src
= helper
.SourceURL("a");
602 FileSystemURL dest
= helper
.DestURL("b");
604 // Set up a source directory.
605 ASSERT_EQ(base::File::FILE_OK
, helper
.CreateDirectory(src
));
606 ASSERT_EQ(base::File::FILE_OK
,
607 helper
.SetUpTestCaseFiles(src
,
608 kRegularFileSystemTestCases
,
609 kRegularFileSystemTestCaseSize
));
612 helper
.Move(src
, dest
);
615 ASSERT_TRUE(helper
.DirectoryExists(src
));
616 ASSERT_TRUE(helper
.DirectoryExists(dest
));
618 FileSystemTestCaseRecord kMoveDirResultCases
[] = {
619 {false, FILE_PATH_LITERAL("file 0"), 38},
620 {false, FILE_PATH_LITERAL("file 3"), 0},
623 helper
.VerifyTestCaseFiles(dest
,
625 arraysize(kMoveDirResultCases
));
628 TEST(LocalFileSystemCopyOrMoveOperationTest
, CopySingleFileNoValidator
) {
629 CopyOrMoveOperationTestHelper
helper(GURL("http://foo"),
630 fileapi::kFileSystemTypeTemporary
,
631 fileapi::kFileSystemTypeTest
);
632 helper
.SetUpNoValidator();
634 FileSystemURL src
= helper
.SourceURL("a");
635 FileSystemURL dest
= helper
.DestURL("b");
637 // Set up a source file.
638 ASSERT_EQ(base::File::FILE_OK
, helper
.CreateFile(src
, 10));
640 // The copy attempt should fail with a security error -- getting
641 // the factory returns a security error, and the copy operation must
643 ASSERT_EQ(base::File::FILE_ERROR_SECURITY
, helper
.Copy(src
, dest
));
646 TEST(LocalFileSystemCopyOrMoveOperationTest
, ProgressCallback
) {
647 CopyOrMoveOperationTestHelper
helper(GURL("http://foo"),
648 fileapi::kFileSystemTypeTemporary
,
649 fileapi::kFileSystemTypePersistent
);
652 FileSystemURL src
= helper
.SourceURL("a");
653 FileSystemURL dest
= helper
.DestURL("b");
655 // Set up a source directory.
656 ASSERT_EQ(base::File::FILE_OK
, helper
.CreateDirectory(src
));
657 ASSERT_EQ(base::File::FILE_OK
,
658 helper
.SetUpTestCaseFiles(src
,
659 kRegularFileSystemTestCases
,
660 kRegularFileSystemTestCaseSize
));
662 std::vector
<ProgressRecord
> records
;
663 ASSERT_EQ(base::File::FILE_OK
,
664 helper
.CopyWithProgress(src
, dest
,
665 base::Bind(&RecordProgressCallback
,
666 base::Unretained(&records
))));
668 // Verify progress callback.
669 for (size_t i
= 0; i
< kRegularFileSystemTestCaseSize
; ++i
) {
670 const FileSystemTestCaseRecord
& test_case
= kRegularFileSystemTestCases
[i
];
672 FileSystemURL src_url
= helper
.SourceURL(
673 std::string("a/") + base::FilePath(test_case
.path
).AsUTF8Unsafe());
674 FileSystemURL dest_url
= helper
.DestURL(
675 std::string("b/") + base::FilePath(test_case
.path
).AsUTF8Unsafe());
677 // Find the first and last progress record.
678 size_t begin_index
= records
.size();
679 size_t end_index
= records
.size();
680 for (size_t j
= 0; j
< records
.size(); ++j
) {
681 if (records
[j
].source_url
== src_url
) {
682 if (begin_index
== records
.size())
688 // The record should be found.
689 ASSERT_NE(begin_index
, records
.size());
690 ASSERT_NE(end_index
, records
.size());
691 ASSERT_NE(begin_index
, end_index
);
693 EXPECT_EQ(FileSystemOperation::BEGIN_COPY_ENTRY
,
694 records
[begin_index
].type
);
695 EXPECT_FALSE(records
[begin_index
].dest_url
.is_valid());
696 EXPECT_EQ(FileSystemOperation::END_COPY_ENTRY
, records
[end_index
].type
);
697 EXPECT_EQ(dest_url
, records
[end_index
].dest_url
);
699 if (test_case
.is_directory
) {
700 // For directory copy, the progress shouldn't be interlaced.
701 EXPECT_EQ(begin_index
+ 1, end_index
);
703 // PROGRESS event's size should be assending order.
704 int64 current_size
= 0;
705 for (size_t j
= begin_index
+ 1; j
< end_index
; ++j
) {
706 if (records
[j
].source_url
== src_url
) {
707 EXPECT_EQ(FileSystemOperation::PROGRESS
, records
[j
].type
);
708 EXPECT_FALSE(records
[j
].dest_url
.is_valid());
709 EXPECT_GE(records
[j
].size
, current_size
);
710 current_size
= records
[j
].size
;
717 TEST(LocalFileSystemCopyOrMoveOperationTest
, StreamCopyHelper
) {
718 base::ScopedTempDir temp_dir
;
719 ASSERT_TRUE(temp_dir
.CreateUniqueTempDir());
720 base::FilePath source_path
= temp_dir
.path().AppendASCII("source");
721 const char kTestData
[] = "abcdefghijklmnopqrstuvwxyz0123456789";
722 file_util::WriteFile(source_path
, kTestData
,
723 arraysize(kTestData
) - 1); // Exclude trailing '\0'.
725 base::FilePath dest_path
= temp_dir
.path().AppendASCII("dest");
726 // LocalFileWriter requires the file exists. So create an empty file here.
727 file_util::WriteFile(dest_path
, "", 0);
729 base::MessageLoopForIO message_loop
;
730 base::Thread
file_thread("file_thread");
731 ASSERT_TRUE(file_thread
.Start());
732 ScopedThreadStopper
thread_stopper(&file_thread
);
733 ASSERT_TRUE(thread_stopper
.is_valid());
735 scoped_refptr
<base::MessageLoopProxy
> task_runner
=
736 file_thread
.message_loop_proxy();
738 scoped_ptr
<webkit_blob::FileStreamReader
> reader(
739 webkit_blob::FileStreamReader::CreateForLocalFile(
740 task_runner
.get(), source_path
, 0, base::Time()));
742 scoped_ptr
<FileStreamWriter
> writer(
743 FileStreamWriter::CreateForLocalFile(task_runner
.get(), dest_path
, 0));
745 std::vector
<int64
> progress
;
746 CopyOrMoveOperationDelegate::StreamCopyHelper
helper(
747 reader
.Pass(), writer
.Pass(),
748 false, // don't need flush
750 base::Bind(&RecordFileProgressCallback
, base::Unretained(&progress
)),
751 base::TimeDelta()); // For testing, we need all the progress.
753 base::File::Error error
= base::File::FILE_ERROR_FAILED
;
754 base::RunLoop run_loop
;
755 helper
.Run(base::Bind(&AssignAndQuit
, &run_loop
, &error
));
758 EXPECT_EQ(base::File::FILE_OK
, error
);
759 ASSERT_EQ(5U, progress
.size());
760 EXPECT_EQ(0, progress
[0]);
761 EXPECT_EQ(10, progress
[1]);
762 EXPECT_EQ(20, progress
[2]);
763 EXPECT_EQ(30, progress
[3]);
764 EXPECT_EQ(36, progress
[4]);
767 ASSERT_TRUE(base::ReadFileToString(dest_path
, &content
));
768 EXPECT_EQ(kTestData
, content
);
771 TEST(LocalFileSystemCopyOrMoveOperationTest
, StreamCopyHelperWithFlush
) {
772 // Testing the same configuration as StreamCopyHelper, but with |need_flush|
773 // parameter set to true. Since it is hard to test that the flush is indeed
774 // taking place, this test just only verifies that the file is correctly
775 // written with or without the flag.
776 base::ScopedTempDir temp_dir
;
777 ASSERT_TRUE(temp_dir
.CreateUniqueTempDir());
778 base::FilePath source_path
= temp_dir
.path().AppendASCII("source");
779 const char kTestData
[] = "abcdefghijklmnopqrstuvwxyz0123456789";
780 file_util::WriteFile(source_path
, kTestData
,
781 arraysize(kTestData
) - 1); // Exclude trailing '\0'.
783 base::FilePath dest_path
= temp_dir
.path().AppendASCII("dest");
784 // LocalFileWriter requires the file exists. So create an empty file here.
785 file_util::WriteFile(dest_path
, "", 0);
787 base::MessageLoopForIO message_loop
;
788 base::Thread
file_thread("file_thread");
789 ASSERT_TRUE(file_thread
.Start());
790 ScopedThreadStopper
thread_stopper(&file_thread
);
791 ASSERT_TRUE(thread_stopper
.is_valid());
793 scoped_refptr
<base::MessageLoopProxy
> task_runner
=
794 file_thread
.message_loop_proxy();
796 scoped_ptr
<webkit_blob::FileStreamReader
> reader(
797 webkit_blob::FileStreamReader::CreateForLocalFile(
798 task_runner
.get(), source_path
, 0, base::Time()));
800 scoped_ptr
<FileStreamWriter
> writer(
801 FileStreamWriter::CreateForLocalFile(task_runner
.get(), dest_path
, 0));
803 std::vector
<int64
> progress
;
804 CopyOrMoveOperationDelegate::StreamCopyHelper
helper(
805 reader
.Pass(), writer
.Pass(),
808 base::Bind(&RecordFileProgressCallback
, base::Unretained(&progress
)),
809 base::TimeDelta()); // For testing, we need all the progress.
811 base::File::Error error
= base::File::FILE_ERROR_FAILED
;
812 base::RunLoop run_loop
;
813 helper
.Run(base::Bind(&AssignAndQuit
, &run_loop
, &error
));
816 EXPECT_EQ(base::File::FILE_OK
, error
);
817 ASSERT_EQ(5U, progress
.size());
818 EXPECT_EQ(0, progress
[0]);
819 EXPECT_EQ(10, progress
[1]);
820 EXPECT_EQ(20, progress
[2]);
821 EXPECT_EQ(30, progress
[3]);
822 EXPECT_EQ(36, progress
[4]);
825 ASSERT_TRUE(base::ReadFileToString(dest_path
, &content
));
826 EXPECT_EQ(kTestData
, content
);
829 TEST(LocalFileSystemCopyOrMoveOperationTest
, StreamCopyHelper_Cancel
) {
830 base::ScopedTempDir temp_dir
;
831 ASSERT_TRUE(temp_dir
.CreateUniqueTempDir());
832 base::FilePath source_path
= temp_dir
.path().AppendASCII("source");
833 const char kTestData
[] = "abcdefghijklmnopqrstuvwxyz0123456789";
834 file_util::WriteFile(source_path
, kTestData
,
835 arraysize(kTestData
) - 1); // Exclude trailing '\0'.
837 base::FilePath dest_path
= temp_dir
.path().AppendASCII("dest");
838 // LocalFileWriter requires the file exists. So create an empty file here.
839 file_util::WriteFile(dest_path
, "", 0);
841 base::MessageLoopForIO message_loop
;
842 base::Thread
file_thread("file_thread");
843 ASSERT_TRUE(file_thread
.Start());
844 ScopedThreadStopper
thread_stopper(&file_thread
);
845 ASSERT_TRUE(thread_stopper
.is_valid());
847 scoped_refptr
<base::MessageLoopProxy
> task_runner
=
848 file_thread
.message_loop_proxy();
850 scoped_ptr
<webkit_blob::FileStreamReader
> reader(
851 webkit_blob::FileStreamReader::CreateForLocalFile(
852 task_runner
.get(), source_path
, 0, base::Time()));
854 scoped_ptr
<FileStreamWriter
> writer(
855 FileStreamWriter::CreateForLocalFile(task_runner
.get(), dest_path
, 0));
857 std::vector
<int64
> progress
;
858 CopyOrMoveOperationDelegate::StreamCopyHelper
helper(
859 reader
.Pass(), writer
.Pass(),
862 base::Bind(&RecordFileProgressCallback
, base::Unretained(&progress
)),
863 base::TimeDelta()); // For testing, we need all the progress.
865 // Call Cancel() later.
866 base::MessageLoopProxy::current()->PostTask(
868 base::Bind(&CopyOrMoveOperationDelegate::StreamCopyHelper::Cancel
,
869 base::Unretained(&helper
)));
871 base::File::Error error
= base::File::FILE_ERROR_FAILED
;
872 base::RunLoop run_loop
;
873 helper
.Run(base::Bind(&AssignAndQuit
, &run_loop
, &error
));
876 EXPECT_EQ(base::File::FILE_ERROR_ABORT
, error
);
879 } // namespace content