Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/command_line.h" | |
| 5 #include "base/files/file.h" | 6 #include "base/files/file.h" |
| 6 #include "base/files/file_util.h" | 7 #include "base/files/file_util.h" |
| 7 #include "base/files/scoped_temp_dir.h" | 8 #include "base/files/scoped_temp_dir.h" |
| 9 #include "base/test/multiprocess_test.h" | |
| 10 #include "base/test/test_timeouts.h" | |
| 11 #include "base/threading/platform_thread.h" | |
| 8 #include "base/time/time.h" | 12 #include "base/time/time.h" |
| 9 #include "testing/gtest/include/gtest/gtest.h" | 13 #include "testing/gtest/include/gtest/gtest.h" |
| 14 #include "testing/multiprocess_func_list.h" | |
| 10 | 15 |
| 11 using base::File; | 16 using base::File; |
| 12 using base::FilePath; | 17 using base::FilePath; |
| 13 | 18 |
| 14 TEST(FileTest, Create) { | 19 TEST(FileTest, Create) { |
| 15 base::ScopedTempDir temp_dir; | 20 base::ScopedTempDir temp_dir; |
| 16 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); | 21 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| 17 FilePath file_path = temp_dir.path().AppendASCII("create_file_1"); | 22 FilePath file_path = temp_dir.path().AppendASCII("create_file_1"); |
| 18 | 23 |
| 19 { | 24 { |
| (...skipping 482 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 502 NULL)); | 507 NULL)); |
| 503 ASSERT_TRUE(dir.IsValid()); | 508 ASSERT_TRUE(dir.IsValid()); |
| 504 | 509 |
| 505 base::File::Info info; | 510 base::File::Info info; |
| 506 EXPECT_TRUE(dir.GetInfo(&info)); | 511 EXPECT_TRUE(dir.GetInfo(&info)); |
| 507 EXPECT_TRUE(info.is_directory); | 512 EXPECT_TRUE(info.is_directory); |
| 508 EXPECT_FALSE(info.is_symbolic_link); | 513 EXPECT_FALSE(info.is_symbolic_link); |
| 509 EXPECT_EQ(0, info.size); | 514 EXPECT_EQ(0, info.size); |
| 510 } | 515 } |
| 511 #endif // defined(OS_WIN) | 516 #endif // defined(OS_WIN) |
| 517 | |
| 518 // Constants and helper function for lock/unlock tests. | |
| 519 namespace { | |
| 520 | |
| 521 // Flag for the parent to share a temp dir to the child. | |
| 522 const char kTempDirFlag[] = "temp-dir"; | |
| 523 | |
| 524 // File to lock in temp dir. | |
| 525 const char kLockFile[] = "lockfile"; | |
| 526 | |
| 527 // Constants for various requests and responses, used as |signal_file| parameter | |
| 528 // to signal/wait helpers. | |
| 529 const char kSignalLockFileLock[] = "lock.signal"; | |
| 530 const char kSignalLockFileLocked[] = "locked.signal"; | |
| 531 const char kSignalLockFileClose[] = "close.signal"; | |
| 532 const char kSignalLockFileClosed[] = "closed.signal"; | |
| 533 const char kSignalLockFileUnlock[] = "unlock.signal"; | |
| 534 const char kSignalLockFileUnlocked[] = "unlocked.signal"; | |
| 535 const char kSignalExit[] = "exit.signal"; | |
| 536 | |
| 537 // Signal an event by creating a file which didn't previously exist. | |
| 538 bool SignalEvent(const base::FilePath& signal_dir, | |
| 539 const base::StringPiece& signal_file) { | |
| 540 File file(signal_dir.AppendASCII(signal_file), | |
| 541 File::FLAG_CREATE | File::FLAG_APPEND); | |
| 542 return file.IsValid(); | |
| 543 } | |
| 544 | |
| 545 // Check whether an event was signaled. | |
| 546 bool CheckEvent(const base::FilePath& signal_dir, | |
| 547 const base::StringPiece& signal_file) { | |
| 548 File file(signal_dir.AppendASCII(signal_file), | |
| 549 File::FLAG_OPEN | File::FLAG_READ); | |
| 550 return file.IsValid(); | |
| 551 } | |
| 552 | |
| 553 // Wait for an event to be signaled, returning false if it times out. | |
| 554 bool WaitForEventWithTimeout(const base::FilePath& signal_dir, | |
| 555 const base::StringPiece& signal_file, | |
| 556 const base::TimeDelta& timeout) { | |
| 557 const base::Time finish_by = base::Time::Now() + timeout; | |
| 558 while (!CheckEvent(signal_dir, signal_file)) { | |
| 559 if (base::Time::Now() > finish_by) | |
| 560 return false; | |
| 561 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10)); | |
| 562 } | |
| 563 return true; | |
| 564 } | |
| 565 | |
| 566 // Wait forever for the event to be signaled (should never return false). | |
| 567 bool WaitForEvent(const base::FilePath& signal_dir, | |
| 568 const base::StringPiece& signal_file) { | |
| 569 return WaitForEventWithTimeout(signal_dir, signal_file, | |
| 570 base::TimeDelta::Max()); | |
| 571 } | |
| 572 | |
| 573 // Wait for an event or the action timeout, whichever comes first. | |
| 574 bool WaitForEventOrTimeout(const base::FilePath& signal_dir, | |
| 575 const base::StringPiece& signal_file) { | |
| 576 return WaitForEventWithTimeout(signal_dir, signal_file, | |
| 577 TestTimeouts::action_timeout()); | |
| 578 } | |
| 579 | |
| 580 } // namespace | |
| 581 | |
| 582 // Subprocess to test getting a file lock then releasing it. | |
| 583 MULTIPROCESS_TEST_MAIN(ChildLockUnlock) { | |
| 584 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); | |
| 585 const base::FilePath temp_path = | |
| 586 command_line->GetSwitchValuePath(kTempDirFlag); | |
| 587 CHECK(base::DirectoryExists(temp_path)); | |
| 588 | |
| 589 // Wait for signal to lock, then lock the file. | |
| 590 CHECK(WaitForEvent(temp_path, kSignalLockFileLock)); | |
| 591 File file(temp_path.AppendASCII(kLockFile), | |
| 592 File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE); | |
| 593 CHECK(file.IsValid()); | |
| 594 CHECK_EQ(File::FILE_OK, file.Lock()); | |
| 595 CHECK(SignalEvent(temp_path, kSignalLockFileLocked)); | |
| 596 | |
| 597 // Wait for signal to unlock, then unlock the file. | |
| 598 CHECK(WaitForEvent(temp_path, kSignalLockFileUnlock)); | |
| 599 CHECK_EQ(File::FILE_OK, file.Unlock()); | |
| 600 CHECK(SignalEvent(temp_path, kSignalLockFileUnlocked)); | |
| 601 | |
| 602 // Wait for signal to exit, so that the unlock can be distinguished from exit. | |
| 603 CHECK(WaitForEvent(temp_path, kSignalExit)); | |
| 604 return 0; | |
| 605 } | |
| 606 | |
| 607 // Test that the child's lock prevents the parent from getting the lock, and | |
| 608 // that the child's unlock allows the parent to get the lock. | |
| 609 TEST(FileTest, LockAndUnlock) { | |
| 610 // Create a temporary dir and spin up a ChildLockExit subprocess against it. | |
| 611 base::ScopedTempDir temp_dir; | |
| 612 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); | |
| 613 const base::FilePath temp_path = temp_dir.path(); | |
| 614 base::CommandLine child_command_line( | |
| 615 base::GetMultiProcessTestChildBaseCommandLine()); | |
| 616 child_command_line.AppendSwitchPath(kTempDirFlag, temp_path); | |
| 617 base::Process test_child_process = | |
|
Lei Zhang
2015/12/04 03:23:03
Check |test_child_process| is valid before continu
| |
| 618 base::SpawnMultiProcessTestChild("ChildLockUnlock", child_command_line, | |
| 619 base::LaunchOptions()); | |
| 620 | |
| 621 // Create the lock file and verify that it can be locked and unlocked. | |
| 622 File file(temp_path.AppendASCII(kLockFile), | |
| 623 File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE); | |
| 624 ASSERT_TRUE(file.IsValid()); | |
| 625 ASSERT_EQ(File::FILE_OK, file.Lock()); | |
| 626 ASSERT_EQ(File::FILE_OK, file.Unlock()); | |
| 627 | |
| 628 // Signal child to lock the file. | |
| 629 ASSERT_TRUE(SignalEvent(temp_path, kSignalLockFileLock)); | |
| 630 | |
| 631 // After child has locked the file, test the lock, then signal child to unlock | |
| 632 // the file. | |
| 633 ASSERT_TRUE(WaitForEventOrTimeout(temp_path, kSignalLockFileLocked)); | |
| 634 ASSERT_NE(File::FILE_OK, file.Lock()); | |
| 635 ASSERT_TRUE(SignalEvent(temp_path, kSignalLockFileUnlock)); | |
| 636 | |
| 637 // After child has unlocked, test that the file can be locked and signal the | |
| 638 // child to exit. | |
| 639 ASSERT_TRUE(WaitForEventOrTimeout(temp_path, kSignalLockFileUnlocked)); | |
| 640 ASSERT_EQ(File::FILE_OK, file.Lock()); | |
| 641 ASSERT_EQ(File::FILE_OK, file.Unlock()); | |
| 642 ASSERT_TRUE(SignalEvent(temp_path, kSignalExit)); | |
| 643 | |
| 644 int rv = -1; | |
| 645 ASSERT_TRUE(test_child_process.WaitForExitWithTimeout( | |
| 646 TestTimeouts::action_timeout(), &rv)); | |
| 647 ASSERT_EQ(0, rv); | |
| 648 } | |
| 649 | |
| 650 // Subprocess to test getting a lock then releasing it implicitly on close. | |
| 651 MULTIPROCESS_TEST_MAIN(ChildLockClose) { | |
| 652 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); | |
| 653 const base::FilePath temp_path = | |
| 654 command_line->GetSwitchValuePath(kTempDirFlag); | |
| 655 CHECK(base::DirectoryExists(temp_path)); | |
| 656 | |
| 657 // Wait for signal to lock, then lock the file. | |
| 658 CHECK(WaitForEvent(temp_path, kSignalLockFileLock)); | |
| 659 File file(temp_path.AppendASCII(kLockFile), | |
| 660 File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE); | |
| 661 CHECK(file.IsValid()); | |
| 662 CHECK_EQ(File::FILE_OK, file.Lock()); | |
| 663 CHECK(SignalEvent(temp_path, kSignalLockFileLocked)); | |
| 664 | |
| 665 // Wait for the signal to close, then close the file. | |
| 666 CHECK(WaitForEvent(temp_path, kSignalLockFileClose)); | |
|
Lei Zhang
2015/12/04 03:23:03
Would it easier to have one child process impl tha
Scott Hess - ex-Googler
2015/12/04 06:40:34
Just to clarify, you mean to merge the different c
Lei Zhang
2015/12/04 16:57:54
If it's easy to implement and it reduces the amoun
| |
| 667 file.Close(); | |
| 668 CHECK(SignalEvent(temp_path, kSignalLockFileClosed)); | |
| 669 | |
| 670 // Wait for signal to exit, so that close can be distinguished from exit. | |
| 671 CHECK(WaitForEvent(temp_path, kSignalExit)); | |
| 672 return 0; | |
| 673 } | |
| 674 | |
| 675 // Test that closing the handle in the child implicitly releases the lock. | |
| 676 TEST(FileTest, UnlockOnClose) { | |
| 677 // Create a temporary dir and spin up a ChildLockExit subprocess against it. | |
| 678 base::ScopedTempDir temp_dir; | |
| 679 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); | |
| 680 const base::FilePath temp_path = temp_dir.path(); | |
| 681 base::CommandLine child_command_line( | |
| 682 base::GetMultiProcessTestChildBaseCommandLine()); | |
| 683 child_command_line.AppendSwitchPath(kTempDirFlag, temp_path); | |
| 684 base::Process test_child_process = | |
| 685 base::SpawnMultiProcessTestChild("ChildLockClose", child_command_line, | |
| 686 base::LaunchOptions()); | |
| 687 | |
| 688 // Create the lock file and verify that it can be locked and unlocked. | |
| 689 File file(temp_path.AppendASCII(kLockFile), | |
| 690 File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE); | |
| 691 ASSERT_TRUE(file.IsValid()); | |
| 692 ASSERT_EQ(File::FILE_OK, file.Lock()); | |
| 693 ASSERT_EQ(File::FILE_OK, file.Unlock()); | |
| 694 | |
| 695 // Signal child to lock the file. | |
| 696 ASSERT_TRUE(SignalEvent(temp_path, kSignalLockFileLock)); | |
| 697 | |
| 698 // After child has locked the file, test the lock, then signal child to close | |
| 699 // the file. | |
| 700 ASSERT_TRUE(WaitForEventOrTimeout(temp_path, kSignalLockFileLocked)); | |
| 701 ASSERT_NE(File::FILE_OK, file.Lock()); | |
| 702 ASSERT_TRUE(SignalEvent(temp_path, kSignalLockFileClose)); | |
| 703 | |
| 704 // After child has closed, test that the file can be locked and signal the | |
| 705 // child to exit. | |
| 706 ASSERT_TRUE(WaitForEventOrTimeout(temp_path, kSignalLockFileClosed)); | |
| 707 ASSERT_EQ(File::FILE_OK, file.Lock()); | |
| 708 ASSERT_EQ(File::FILE_OK, file.Unlock()); | |
| 709 ASSERT_TRUE(SignalEvent(temp_path, kSignalExit)); | |
| 710 | |
| 711 int rv = -1; | |
| 712 ASSERT_TRUE(test_child_process.WaitForExitWithTimeout( | |
| 713 TestTimeouts::action_timeout(), &rv)); | |
| 714 ASSERT_EQ(0, rv); | |
| 715 } | |
| 716 | |
| 717 // Subprocess to test getting a lock then releasing it implicitly on exit or | |
| 718 // termination. | |
| 719 MULTIPROCESS_TEST_MAIN(ChildLockExit) { | |
| 720 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); | |
| 721 const base::FilePath temp_path = | |
| 722 command_line->GetSwitchValuePath(kTempDirFlag); | |
| 723 CHECK(base::DirectoryExists(temp_path)); | |
| 724 | |
| 725 // Wait for signal to lock, then lock the file. | |
| 726 CHECK(WaitForEvent(temp_path, kSignalLockFileLock)); | |
| 727 File file(temp_path.AppendASCII(kLockFile), | |
| 728 File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE); | |
| 729 CHECK(file.IsValid()); | |
| 730 CHECK_EQ(File::FILE_OK, file.Lock()); | |
| 731 CHECK(SignalEvent(temp_path, kSignalLockFileLocked)); | |
| 732 | |
| 733 // Wait for signal to exit, so that exit can be distinguished from kill. | |
| 734 CHECK(WaitForEvent(temp_path, kSignalExit)); | |
| 735 return 0; | |
| 736 } | |
| 737 | |
| 738 // Test that locks are released on exit. | |
| 739 TEST(FileTest, UnlockOnExit) { | |
| 740 // Create a temporary dir and spin up a ChildLockExit subprocess against it. | |
| 741 base::ScopedTempDir temp_dir; | |
| 742 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); | |
| 743 const base::FilePath temp_path = temp_dir.path(); | |
| 744 base::CommandLine child_command_line( | |
| 745 base::GetMultiProcessTestChildBaseCommandLine()); | |
| 746 child_command_line.AppendSwitchPath(kTempDirFlag, temp_path); | |
| 747 base::Process test_child_process = | |
| 748 base::SpawnMultiProcessTestChild("ChildLockExit", child_command_line, | |
| 749 base::LaunchOptions()); | |
| 750 | |
| 751 // Create the lock file and verify that it can be locked and unlocked. | |
| 752 File file(temp_path.AppendASCII(kLockFile), | |
| 753 File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE); | |
| 754 ASSERT_TRUE(file.IsValid()); | |
| 755 ASSERT_EQ(File::FILE_OK, file.Lock()); | |
| 756 ASSERT_EQ(File::FILE_OK, file.Unlock()); | |
| 757 | |
| 758 // Signal child to lock the file. | |
| 759 ASSERT_TRUE(SignalEvent(temp_path, kSignalLockFileLock)); | |
| 760 | |
| 761 // After child has locked the file, test the lock, then signal child to exit. | |
| 762 ASSERT_TRUE(WaitForEventOrTimeout(temp_path, kSignalLockFileLocked)); | |
| 763 ASSERT_NE(File::FILE_OK, file.Lock()); | |
| 764 ASSERT_TRUE(SignalEvent(temp_path, kSignalExit)); | |
| 765 | |
| 766 int rv = -1; | |
| 767 ASSERT_TRUE(test_child_process.WaitForExitWithTimeout( | |
| 768 TestTimeouts::action_timeout(), &rv)); | |
| 769 ASSERT_EQ(0, rv); | |
| 770 | |
| 771 // Exit released the child's lock. | |
| 772 ASSERT_EQ(File::FILE_OK, file.Lock()); | |
| 773 ASSERT_EQ(File::FILE_OK, file.Unlock()); | |
| 774 } | |
| 775 | |
| 776 // Test that locks are released when a process is terminated. This should cover | |
| 777 // the case of crashing, also. | |
| 778 TEST(FileTest, UnlockOnTerminate) { | |
| 779 // Create a temporary dir and spin up a ChildLockExit subprocess against it. | |
| 780 base::ScopedTempDir temp_dir; | |
| 781 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); | |
| 782 const base::FilePath temp_path = temp_dir.path(); | |
| 783 base::CommandLine child_command_line( | |
| 784 base::GetMultiProcessTestChildBaseCommandLine()); | |
| 785 child_command_line.AppendSwitchPath(kTempDirFlag, temp_path); | |
| 786 base::Process test_child_process = | |
| 787 base::SpawnMultiProcessTestChild("ChildLockExit", child_command_line, | |
| 788 base::LaunchOptions()); | |
| 789 | |
| 790 // Create the lock file and verify that it can be locked and unlocked. | |
| 791 File file(temp_path.AppendASCII(kLockFile), | |
| 792 File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE); | |
| 793 ASSERT_TRUE(file.IsValid()); | |
| 794 ASSERT_EQ(File::FILE_OK, file.Lock()); | |
| 795 ASSERT_EQ(File::FILE_OK, file.Unlock()); | |
| 796 | |
| 797 // Signal child to lock the file. | |
| 798 ASSERT_TRUE(SignalEvent(temp_path, kSignalLockFileLock)); | |
| 799 | |
| 800 // After child has locked the file, test the lock, then terminate the child. | |
| 801 ASSERT_TRUE(WaitForEventOrTimeout(temp_path, kSignalLockFileLocked)); | |
| 802 ASSERT_NE(File::FILE_OK, file.Lock()); | |
| 803 ASSERT_TRUE(test_child_process.Terminate(0, true)); | |
| 804 | |
| 805 // Termination released the child's lock. | |
| 806 ASSERT_EQ(File::FILE_OK, file.Lock()); | |
| 807 ASSERT_EQ(File::FILE_OK, file.Unlock()); | |
| 808 } | |
| OLD | NEW |