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