copy_or_move_operation_delegate_unittest.cc revision 23730a6e56a168d1879203e4b3819bb36e3d8f1f
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/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" 32 33using content::AsyncFileTestHelper; 34using fileapi::CopyOrMoveOperationDelegate; 35using fileapi::FileStreamWriter; 36using fileapi::FileSystemOperation; 37using fileapi::FileSystemType; 38using fileapi::FileSystemURL; 39 40namespace content { 41 42typedef fileapi::FileSystemOperation::FileEntryList FileEntryList; 43 44namespace { 45 46void ExpectOk(const GURL& origin_url, 47 const std::string& name, 48 base::File::Error error) { 49 ASSERT_EQ(base::File::FILE_OK, error); 50} 51 52class TestValidatorFactory : public fileapi::CopyOrMoveFileValidatorFactory { 53 public: 54 // A factory that creates validators that accept everything or nothing. 55 TestValidatorFactory() {} 56 virtual ~TestValidatorFactory() {} 57 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")); 63 } 64 65 private: 66 class TestValidator : public fileapi::CopyOrMoveFileValidator { 67 public: 68 explicit TestValidator(bool pre_copy_valid, 69 bool post_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) { 76 } 77 virtual ~TestValidator() {} 78 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_)); 84 } 85 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; 93 } 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)); 97 } 98 99 private: 100 base::File::Error result_; 101 base::File::Error write_result_; 102 std::string reject_string_; 103 104 DISALLOW_COPY_AND_ASSIGN(TestValidator); 105 }; 106}; 107 108// Records CopyProgressCallback invocations. 109struct ProgressRecord { 110 fileapi::FileSystemOperation::CopyProgressType type; 111 FileSystemURL source_url; 112 FileSystemURL dest_url; 113 int64 size; 114}; 115 116void RecordProgressCallback(std::vector<ProgressRecord>* records, 117 fileapi::FileSystemOperation::CopyProgressType type, 118 const FileSystemURL& source_url, 119 const FileSystemURL& dest_url, 120 int64 size) { 121 ProgressRecord record; 122 record.type = type; 123 record.source_url = source_url; 124 record.dest_url = dest_url; 125 record.size = size; 126 records->push_back(record); 127} 128 129void RecordFileProgressCallback(std::vector<int64>* records, 130 int64 progress) { 131 records->push_back(progress); 132} 133 134void AssignAndQuit(base::RunLoop* run_loop, 135 base::File::Error* result_out, 136 base::File::Error result) { 137 *result_out = result; 138 run_loop->Quit(); 139} 140 141class ScopedThreadStopper { 142 public: 143 ScopedThreadStopper(base::Thread* thread) : thread_(thread) { 144 } 145 146 ~ScopedThreadStopper() { 147 if (thread_) { 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()); 152 run_loop.Run(); 153 thread_->Stop(); 154 } 155 } 156 157 bool is_valid() const { return thread_; } 158 159 private: 160 base::Thread* thread_; 161 DISALLOW_COPY_AND_ASSIGN(ScopedThreadStopper); 162}; 163 164} // namespace 165 166class CopyOrMoveOperationTestHelper { 167 public: 168 CopyOrMoveOperationTestHelper( 169 const GURL& origin, 170 FileSystemType src_type, 171 FileSystemType dest_type) 172 : origin_(origin), 173 src_type_(src_type), 174 dest_type_(dest_type) {} 175 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(); 182 } 183 184 void SetUp() { 185 SetUp(true, true); 186 } 187 188 void SetUpNoValidator() { 189 SetUp(true, false); 190 } 191 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(); 196 quota_manager_ = 197 new quota::MockQuotaManager(false /* is_incognito */, 198 base_dir, 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); 206 207 // Prepare the origin's root directory. 208 fileapi::FileSystemBackend* backend = 209 file_system_context_->GetFileSystemBackend(src_type_); 210 backend->ResolveURL( 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()); 224 } 225 backend->ResolveURL( 226 FileSystemURL::CreateForTest(origin_, dest_type_, base::FilePath()), 227 fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, 228 base::Bind(&ExpectOk)); 229 base::RunLoop().RunUntilIdle(); 230 231 // Grant relatively big quota initially. 232 quota_manager_->SetQuota( 233 origin_, 234 fileapi::FileSystemTypeToQuotaStorageType(src_type_), 235 1024 * 1024); 236 quota_manager_->SetQuota( 237 origin_, 238 fileapi::FileSystemTypeToQuotaStorageType(dest_type_), 239 1024 * 1024); 240 } 241 242 int64 GetSourceUsage() { 243 int64 usage = 0; 244 GetUsageAndQuota(src_type_, &usage, NULL); 245 return usage; 246 } 247 248 int64 GetDestUsage() { 249 int64 usage = 0; 250 GetUsageAndQuota(dest_type_, &usage, NULL); 251 return usage; 252 } 253 254 FileSystemURL SourceURL(const std::string& path) { 255 return file_system_context_->CreateCrackedFileSystemURL( 256 origin_, src_type_, base::FilePath::FromUTF8Unsafe(path)); 257 } 258 259 FileSystemURL DestURL(const std::string& path) { 260 return file_system_context_->CreateCrackedFileSystemURL( 261 origin_, dest_type_, base::FilePath::FromUTF8Unsafe(path)); 262 } 263 264 base::File::Error Copy(const FileSystemURL& src, 265 const FileSystemURL& dest) { 266 return AsyncFileTestHelper::Copy(file_system_context_.get(), src, dest); 267 } 268 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); 275 } 276 277 base::File::Error Move(const FileSystemURL& src, 278 const FileSystemURL& dest) { 279 return AsyncFileTestHelper::Move(file_system_context_.get(), src, dest); 280 } 281 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( 290 root.origin(), 291 root.mount_type(), 292 root.virtual_path().Append(test_case.path)); 293 if (test_case.is_directory) 294 result = CreateDirectory(url); 295 else 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) 299 return result; 300 } 301 return result; 302 } 303 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) { 310 test_case_map[ 311 base::FilePath(test_cases[i].path).NormalizePathSeparators()] = 312 &test_cases[i]; 313 } 314 315 std::queue<FileSystemURL> directories; 316 FileEntryList entries; 317 directories.push(root); 318 while (!directories.empty()) { 319 FileSystemURL dir = directories.front(); 320 directories.pop(); 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( 324 dir.origin(), 325 dir.mount_type(), 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); 334 } else { 335 EXPECT_FALSE(test_case_map[relative]->is_directory); 336 EXPECT_TRUE(FileExists(url, test_case_map[relative]->data_file_size)); 337 } 338 test_case_map.erase(relative); 339 } 340 } 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(); 346 } 347 } 348 349 base::File::Error ReadDirectory(const FileSystemURL& url, 350 FileEntryList* entries) { 351 return AsyncFileTestHelper::ReadDirectory( 352 file_system_context_.get(), url, entries); 353 } 354 355 base::File::Error CreateDirectory(const FileSystemURL& url) { 356 return AsyncFileTestHelper::CreateDirectory(file_system_context_.get(), 357 url); 358 } 359 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) 364 return result; 365 return AsyncFileTestHelper::TruncateFile( 366 file_system_context_.get(), url, size); 367 } 368 369 bool FileExists(const FileSystemURL& url, int64 expected_size) { 370 return AsyncFileTestHelper::FileExists( 371 file_system_context_.get(), url, expected_size); 372 } 373 374 bool DirectoryExists(const FileSystemURL& url) { 375 return AsyncFileTestHelper::DirectoryExists(file_system_context_.get(), 376 url); 377 } 378 379 private: 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); 384 } 385 386 private: 387 base::ScopedTempDir base_; 388 389 const GURL origin_; 390 const FileSystemType src_type_; 391 const FileSystemType dest_type_; 392 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_; 397 398 DISALLOW_COPY_AND_ASSIGN(CopyOrMoveOperationTestHelper); 399}; 400 401TEST(LocalFileSystemCopyOrMoveOperationTest, CopySingleFile) { 402 CopyOrMoveOperationTestHelper helper(GURL("http://foo"), 403 fileapi::kFileSystemTypeTemporary, 404 fileapi::kFileSystemTypePersistent); 405 helper.SetUp(); 406 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(); 411 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; 415 416 // Copy it. 417 ASSERT_EQ(base::File::FILE_OK, helper.Copy(src, dest)); 418 419 // Verify. 420 ASSERT_TRUE(helper.FileExists(src, 10)); 421 ASSERT_TRUE(helper.FileExists(dest, 10)); 422 423 int64 src_new_usage = helper.GetSourceUsage(); 424 ASSERT_EQ(src_initial_usage + src_increase, src_new_usage); 425 426 int64 dest_increase = helper.GetDestUsage() - dest_initial_usage; 427 ASSERT_EQ(src_increase, dest_increase); 428} 429 430TEST(LocalFileSystemCopyOrMoveOperationTest, MoveSingleFile) { 431 CopyOrMoveOperationTestHelper helper(GURL("http://foo"), 432 fileapi::kFileSystemTypeTemporary, 433 fileapi::kFileSystemTypePersistent); 434 helper.SetUp(); 435 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(); 440 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; 444 445 // Move it. 446 ASSERT_EQ(base::File::FILE_OK, helper.Move(src, dest)); 447 448 // Verify. 449 ASSERT_FALSE(helper.FileExists(src, AsyncFileTestHelper::kDontCheckSize)); 450 ASSERT_TRUE(helper.FileExists(dest, 10)); 451 452 int64 src_new_usage = helper.GetSourceUsage(); 453 ASSERT_EQ(src_initial_usage, src_new_usage); 454 455 int64 dest_increase = helper.GetDestUsage() - dest_initial_usage; 456 ASSERT_EQ(src_increase, dest_increase); 457} 458 459TEST(LocalFileSystemCopyOrMoveOperationTest, CopySingleDirectory) { 460 CopyOrMoveOperationTestHelper helper(GURL("http://foo"), 461 fileapi::kFileSystemTypeTemporary, 462 fileapi::kFileSystemTypePersistent); 463 helper.SetUp(); 464 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(); 469 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; 473 474 // Copy it. 475 ASSERT_EQ(base::File::FILE_OK, helper.Copy(src, dest)); 476 477 // Verify. 478 ASSERT_TRUE(helper.DirectoryExists(src)); 479 ASSERT_TRUE(helper.DirectoryExists(dest)); 480 481 int64 src_new_usage = helper.GetSourceUsage(); 482 ASSERT_EQ(src_initial_usage + src_increase, src_new_usage); 483 484 int64 dest_increase = helper.GetDestUsage() - dest_initial_usage; 485 ASSERT_EQ(src_increase, dest_increase); 486} 487 488TEST(LocalFileSystemCopyOrMoveOperationTest, MoveSingleDirectory) { 489 CopyOrMoveOperationTestHelper helper(GURL("http://foo"), 490 fileapi::kFileSystemTypeTemporary, 491 fileapi::kFileSystemTypePersistent); 492 helper.SetUp(); 493 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(); 498 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; 502 503 // Move it. 504 ASSERT_EQ(base::File::FILE_OK, helper.Move(src, dest)); 505 506 // Verify. 507 ASSERT_FALSE(helper.DirectoryExists(src)); 508 ASSERT_TRUE(helper.DirectoryExists(dest)); 509 510 int64 src_new_usage = helper.GetSourceUsage(); 511 ASSERT_EQ(src_initial_usage, src_new_usage); 512 513 int64 dest_increase = helper.GetDestUsage() - dest_initial_usage; 514 ASSERT_EQ(src_increase, dest_increase); 515} 516 517TEST(LocalFileSystemCopyOrMoveOperationTest, CopyDirectory) { 518 CopyOrMoveOperationTestHelper helper(GURL("http://foo"), 519 fileapi::kFileSystemTypeTemporary, 520 fileapi::kFileSystemTypePersistent); 521 helper.SetUp(); 522 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(); 527 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; 535 536 // Copy it. 537 ASSERT_EQ(base::File::FILE_OK, 538 helper.CopyWithProgress( 539 src, dest, 540 AsyncFileTestHelper::CopyProgressCallback())); 541 542 // Verify. 543 ASSERT_TRUE(helper.DirectoryExists(src)); 544 ASSERT_TRUE(helper.DirectoryExists(dest)); 545 546 helper.VerifyTestCaseFiles(dest, 547 kRegularFileSystemTestCases, 548 kRegularFileSystemTestCaseSize); 549 550 int64 src_new_usage = helper.GetSourceUsage(); 551 ASSERT_EQ(src_initial_usage + src_increase, src_new_usage); 552 553 int64 dest_increase = helper.GetDestUsage() - dest_initial_usage; 554 ASSERT_EQ(src_increase, dest_increase); 555} 556 557TEST(LocalFileSystemCopyOrMoveOperationTest, MoveDirectory) { 558 CopyOrMoveOperationTestHelper helper(GURL("http://foo"), 559 fileapi::kFileSystemTypeTemporary, 560 fileapi::kFileSystemTypePersistent); 561 helper.SetUp(); 562 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(); 567 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; 575 576 // Move it. 577 ASSERT_EQ(base::File::FILE_OK, helper.Move(src, dest)); 578 579 // Verify. 580 ASSERT_FALSE(helper.DirectoryExists(src)); 581 ASSERT_TRUE(helper.DirectoryExists(dest)); 582 583 helper.VerifyTestCaseFiles(dest, 584 kRegularFileSystemTestCases, 585 kRegularFileSystemTestCaseSize); 586 587 int64 src_new_usage = helper.GetSourceUsage(); 588 ASSERT_EQ(src_initial_usage, src_new_usage); 589 590 int64 dest_increase = helper.GetDestUsage() - dest_initial_usage; 591 ASSERT_EQ(src_increase, dest_increase); 592} 593 594TEST(LocalFileSystemCopyOrMoveOperationTest, 595 MoveDirectoryFailPostWriteValidation) { 596 CopyOrMoveOperationTestHelper helper(GURL("http://foo"), 597 fileapi::kFileSystemTypeTemporary, 598 fileapi::kFileSystemTypeTest); 599 helper.SetUp(); 600 601 FileSystemURL src = helper.SourceURL("a"); 602 FileSystemURL dest = helper.DestURL("b"); 603 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)); 610 611 // Move it. 612 helper.Move(src, dest); 613 614 // Verify. 615 ASSERT_TRUE(helper.DirectoryExists(src)); 616 ASSERT_TRUE(helper.DirectoryExists(dest)); 617 618 FileSystemTestCaseRecord kMoveDirResultCases[] = { 619 {false, FILE_PATH_LITERAL("file 0"), 38}, 620 {false, FILE_PATH_LITERAL("file 3"), 0}, 621 }; 622 623 helper.VerifyTestCaseFiles(dest, 624 kMoveDirResultCases, 625 arraysize(kMoveDirResultCases)); 626} 627 628TEST(LocalFileSystemCopyOrMoveOperationTest, CopySingleFileNoValidator) { 629 CopyOrMoveOperationTestHelper helper(GURL("http://foo"), 630 fileapi::kFileSystemTypeTemporary, 631 fileapi::kFileSystemTypeTest); 632 helper.SetUpNoValidator(); 633 634 FileSystemURL src = helper.SourceURL("a"); 635 FileSystemURL dest = helper.DestURL("b"); 636 637 // Set up a source file. 638 ASSERT_EQ(base::File::FILE_OK, helper.CreateFile(src, 10)); 639 640 // The copy attempt should fail with a security error -- getting 641 // the factory returns a security error, and the copy operation must 642 // respect that. 643 ASSERT_EQ(base::File::FILE_ERROR_SECURITY, helper.Copy(src, dest)); 644} 645 646TEST(LocalFileSystemCopyOrMoveOperationTest, ProgressCallback) { 647 CopyOrMoveOperationTestHelper helper(GURL("http://foo"), 648 fileapi::kFileSystemTypeTemporary, 649 fileapi::kFileSystemTypePersistent); 650 helper.SetUp(); 651 652 FileSystemURL src = helper.SourceURL("a"); 653 FileSystemURL dest = helper.DestURL("b"); 654 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)); 661 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)))); 667 668 // Verify progress callback. 669 for (size_t i = 0; i < kRegularFileSystemTestCaseSize; ++i) { 670 const FileSystemTestCaseRecord& test_case = kRegularFileSystemTestCases[i]; 671 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()); 676 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()) 683 begin_index = j; 684 end_index = j; 685 } 686 } 687 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); 692 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); 698 699 if (test_case.is_directory) { 700 // For directory copy, the progress shouldn't be interlaced. 701 EXPECT_EQ(begin_index + 1, end_index); 702 } else { 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; 711 } 712 } 713 } 714 } 715} 716 717TEST(LocalFileSystemCopyOrMoveOperationTest, StreamCopyHelper) { 718 base::ScopedTempDir temp_dir; 719 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); 720 base::FilePath source_path = temp_dir.path().AppendASCII("source"); 721 base::FilePath dest_path = temp_dir.path().AppendASCII("dest"); 722 const char kTestData[] = "abcdefghijklmnopqrstuvwxyz0123456789"; 723 base::WriteFile(source_path, kTestData, 724 arraysize(kTestData) - 1); // Exclude trailing '\0'. 725 726 base::MessageLoopForIO message_loop; 727 base::Thread file_thread("file_thread"); 728 ASSERT_TRUE(file_thread.Start()); 729 ScopedThreadStopper thread_stopper(&file_thread); 730 ASSERT_TRUE(thread_stopper.is_valid()); 731 732 scoped_refptr<base::MessageLoopProxy> task_runner = 733 file_thread.message_loop_proxy(); 734 735 scoped_ptr<webkit_blob::FileStreamReader> reader( 736 webkit_blob::FileStreamReader::CreateForLocalFile( 737 task_runner.get(), source_path, 0, base::Time())); 738 739 scoped_ptr<FileStreamWriter> writer(FileStreamWriter::CreateForLocalFile( 740 task_runner.get(), dest_path, 0, FileStreamWriter::CREATE_NEW_FILE)); 741 742 std::vector<int64> progress; 743 CopyOrMoveOperationDelegate::StreamCopyHelper helper( 744 reader.Pass(), writer.Pass(), 745 false, // don't need flush 746 10, // buffer size 747 base::Bind(&RecordFileProgressCallback, base::Unretained(&progress)), 748 base::TimeDelta()); // For testing, we need all the progress. 749 750 base::File::Error error = base::File::FILE_ERROR_FAILED; 751 base::RunLoop run_loop; 752 helper.Run(base::Bind(&AssignAndQuit, &run_loop, &error)); 753 run_loop.Run(); 754 755 EXPECT_EQ(base::File::FILE_OK, error); 756 ASSERT_EQ(5U, progress.size()); 757 EXPECT_EQ(0, progress[0]); 758 EXPECT_EQ(10, progress[1]); 759 EXPECT_EQ(20, progress[2]); 760 EXPECT_EQ(30, progress[3]); 761 EXPECT_EQ(36, progress[4]); 762 763 std::string content; 764 ASSERT_TRUE(base::ReadFileToString(dest_path, &content)); 765 EXPECT_EQ(kTestData, content); 766} 767 768TEST(LocalFileSystemCopyOrMoveOperationTest, StreamCopyHelperWithFlush) { 769 // Testing the same configuration as StreamCopyHelper, but with |need_flush| 770 // parameter set to true. Since it is hard to test that the flush is indeed 771 // taking place, this test just only verifies that the file is correctly 772 // written with or without the flag. 773 base::ScopedTempDir temp_dir; 774 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); 775 base::FilePath source_path = temp_dir.path().AppendASCII("source"); 776 base::FilePath dest_path = temp_dir.path().AppendASCII("dest"); 777 const char kTestData[] = "abcdefghijklmnopqrstuvwxyz0123456789"; 778 base::WriteFile(source_path, kTestData, 779 arraysize(kTestData) - 1); // Exclude trailing '\0'. 780 781 782 base::MessageLoopForIO message_loop; 783 base::Thread file_thread("file_thread"); 784 ASSERT_TRUE(file_thread.Start()); 785 ScopedThreadStopper thread_stopper(&file_thread); 786 ASSERT_TRUE(thread_stopper.is_valid()); 787 788 scoped_refptr<base::MessageLoopProxy> task_runner = 789 file_thread.message_loop_proxy(); 790 791 scoped_ptr<webkit_blob::FileStreamReader> reader( 792 webkit_blob::FileStreamReader::CreateForLocalFile( 793 task_runner.get(), source_path, 0, base::Time())); 794 795 scoped_ptr<FileStreamWriter> writer(FileStreamWriter::CreateForLocalFile( 796 task_runner.get(), dest_path, 0, FileStreamWriter::CREATE_NEW_FILE)); 797 798 std::vector<int64> progress; 799 CopyOrMoveOperationDelegate::StreamCopyHelper helper( 800 reader.Pass(), writer.Pass(), 801 true, // need flush 802 10, // buffer size 803 base::Bind(&RecordFileProgressCallback, base::Unretained(&progress)), 804 base::TimeDelta()); // For testing, we need all the progress. 805 806 base::File::Error error = base::File::FILE_ERROR_FAILED; 807 base::RunLoop run_loop; 808 helper.Run(base::Bind(&AssignAndQuit, &run_loop, &error)); 809 run_loop.Run(); 810 811 EXPECT_EQ(base::File::FILE_OK, error); 812 ASSERT_EQ(5U, progress.size()); 813 EXPECT_EQ(0, progress[0]); 814 EXPECT_EQ(10, progress[1]); 815 EXPECT_EQ(20, progress[2]); 816 EXPECT_EQ(30, progress[3]); 817 EXPECT_EQ(36, progress[4]); 818 819 std::string content; 820 ASSERT_TRUE(base::ReadFileToString(dest_path, &content)); 821 EXPECT_EQ(kTestData, content); 822} 823 824TEST(LocalFileSystemCopyOrMoveOperationTest, StreamCopyHelper_Cancel) { 825 base::ScopedTempDir temp_dir; 826 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); 827 base::FilePath source_path = temp_dir.path().AppendASCII("source"); 828 base::FilePath dest_path = temp_dir.path().AppendASCII("dest"); 829 const char kTestData[] = "abcdefghijklmnopqrstuvwxyz0123456789"; 830 base::WriteFile(source_path, kTestData, 831 arraysize(kTestData) - 1); // Exclude trailing '\0'. 832 833 base::MessageLoopForIO message_loop; 834 base::Thread file_thread("file_thread"); 835 ASSERT_TRUE(file_thread.Start()); 836 ScopedThreadStopper thread_stopper(&file_thread); 837 ASSERT_TRUE(thread_stopper.is_valid()); 838 839 scoped_refptr<base::MessageLoopProxy> task_runner = 840 file_thread.message_loop_proxy(); 841 842 scoped_ptr<webkit_blob::FileStreamReader> reader( 843 webkit_blob::FileStreamReader::CreateForLocalFile( 844 task_runner.get(), source_path, 0, base::Time())); 845 846 scoped_ptr<FileStreamWriter> writer(FileStreamWriter::CreateForLocalFile( 847 task_runner.get(), dest_path, 0, FileStreamWriter::CREATE_NEW_FILE)); 848 849 std::vector<int64> progress; 850 CopyOrMoveOperationDelegate::StreamCopyHelper helper( 851 reader.Pass(), writer.Pass(), 852 false, // need_flush 853 10, // buffer size 854 base::Bind(&RecordFileProgressCallback, base::Unretained(&progress)), 855 base::TimeDelta()); // For testing, we need all the progress. 856 857 // Call Cancel() later. 858 base::MessageLoopProxy::current()->PostTask( 859 FROM_HERE, 860 base::Bind(&CopyOrMoveOperationDelegate::StreamCopyHelper::Cancel, 861 base::Unretained(&helper))); 862 863 base::File::Error error = base::File::FILE_ERROR_FAILED; 864 base::RunLoop run_loop; 865 helper.Run(base::Bind(&AssignAndQuit, &run_loop, &error)); 866 run_loop.Run(); 867 868 EXPECT_EQ(base::File::FILE_ERROR_ABORT, error); 869} 870 871} // namespace content 872