From 5bf25d881f1a0db5aae1ad9dc53b1044e2d6257e Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Sat, 2 Mar 2024 14:05:38 +0000 Subject: [PATCH] Build/Test Tools: Add initial tests for the `WP_Filesystem_Direct` class. Since `WP_Filesystem_Direct` is by far the most used filesystem abstraction class, this facilitates future changes with sufficient test coverage. Props swissspidy, costdev, mukesh27. Fixes #57774. git-svn-id: https://develop.svn.wordpress.org/trunk@57753 602fd350-edb4-49c9-b593-d223f7449a82 --- .../filesystem/wpFilesystemDirect/atime.php | 50 +++++ .../filesystem/wpFilesystemDirect/base.php | 197 ++++++++++++++++ .../filesystem/wpFilesystemDirect/chdir.php | 95 ++++++++ .../filesystem/wpFilesystemDirect/chgrp.php | 32 +++ .../filesystem/wpFilesystemDirect/chmod.php | 77 +++++++ .../filesystem/wpFilesystemDirect/chown.php | 32 +++ .../wpFilesystemDirect/construct.php | 39 ++++ .../filesystem/wpFilesystemDirect/copy.php | 72 ++++++ .../filesystem/wpFilesystemDirect/cwd.php | 26 +++ .../filesystem/wpFilesystemDirect/delete.php | 204 +++++++++++++++++ .../filesystem/wpFilesystemDirect/dirlist.php | 130 +++++++++++ .../filesystem/wpFilesystemDirect/exists.php | 44 ++++ .../wpFilesystemDirect/getContents.php | 47 ++++ .../wpFilesystemDirect/getContentsArray.php | 57 +++++ .../wpFilesystemDirect/getchmod.php | 48 ++++ .../filesystem/wpFilesystemDirect/isDir.php | 63 ++++++ .../filesystem/wpFilesystemDirect/isFile.php | 61 +++++ .../wpFilesystemDirect/isReadable.php | 46 ++++ .../wpFilesystemDirect/isWritable.php | 46 ++++ .../filesystem/wpFilesystemDirect/mkdir.php | 211 ++++++++++++++++++ .../filesystem/wpFilesystemDirect/move.php | 150 +++++++++++++ .../filesystem/wpFilesystemDirect/mtime.php | 68 ++++++ .../wpFilesystemDirect/putContents.php | 42 ++++ .../filesystem/wpFilesystemDirect/rmdir.php | 200 +++++++++++++++++ .../filesystem/wpFilesystemDirect/size.php | 63 ++++++ .../filesystem/wpFilesystemDirect/touch.php | 93 ++++++++ 26 files changed, 2193 insertions(+) create mode 100644 tests/phpunit/tests/filesystem/wpFilesystemDirect/atime.php create mode 100644 tests/phpunit/tests/filesystem/wpFilesystemDirect/base.php create mode 100644 tests/phpunit/tests/filesystem/wpFilesystemDirect/chdir.php create mode 100644 tests/phpunit/tests/filesystem/wpFilesystemDirect/chgrp.php create mode 100644 tests/phpunit/tests/filesystem/wpFilesystemDirect/chmod.php create mode 100644 tests/phpunit/tests/filesystem/wpFilesystemDirect/chown.php create mode 100644 tests/phpunit/tests/filesystem/wpFilesystemDirect/construct.php create mode 100644 tests/phpunit/tests/filesystem/wpFilesystemDirect/copy.php create mode 100644 tests/phpunit/tests/filesystem/wpFilesystemDirect/cwd.php create mode 100644 tests/phpunit/tests/filesystem/wpFilesystemDirect/delete.php create mode 100644 tests/phpunit/tests/filesystem/wpFilesystemDirect/dirlist.php create mode 100644 tests/phpunit/tests/filesystem/wpFilesystemDirect/exists.php create mode 100644 tests/phpunit/tests/filesystem/wpFilesystemDirect/getContents.php create mode 100644 tests/phpunit/tests/filesystem/wpFilesystemDirect/getContentsArray.php create mode 100644 tests/phpunit/tests/filesystem/wpFilesystemDirect/getchmod.php create mode 100644 tests/phpunit/tests/filesystem/wpFilesystemDirect/isDir.php create mode 100644 tests/phpunit/tests/filesystem/wpFilesystemDirect/isFile.php create mode 100644 tests/phpunit/tests/filesystem/wpFilesystemDirect/isReadable.php create mode 100644 tests/phpunit/tests/filesystem/wpFilesystemDirect/isWritable.php create mode 100644 tests/phpunit/tests/filesystem/wpFilesystemDirect/mkdir.php create mode 100644 tests/phpunit/tests/filesystem/wpFilesystemDirect/move.php create mode 100644 tests/phpunit/tests/filesystem/wpFilesystemDirect/mtime.php create mode 100644 tests/phpunit/tests/filesystem/wpFilesystemDirect/putContents.php create mode 100644 tests/phpunit/tests/filesystem/wpFilesystemDirect/rmdir.php create mode 100644 tests/phpunit/tests/filesystem/wpFilesystemDirect/size.php create mode 100644 tests/phpunit/tests/filesystem/wpFilesystemDirect/touch.php diff --git a/tests/phpunit/tests/filesystem/wpFilesystemDirect/atime.php b/tests/phpunit/tests/filesystem/wpFilesystemDirect/atime.php new file mode 100644 index 0000000000000..c6f57780c01c5 --- /dev/null +++ b/tests/phpunit/tests/filesystem/wpFilesystemDirect/atime.php @@ -0,0 +1,50 @@ +assertIsInt( self::$filesystem->atime( $path ) ); + } + + /** + * Tests that `WP_Filesystem_Direct::atime()` + * returns false for a path that does not exist. + * + * @ticket 57774 + * + * @dataProvider data_paths_that_do_not_exist + * + * @param string $path The path. + */ + public function test_should_return_false_for_a_path_that_does_not_exist( $path ) { + $path = self::$file_structure['test_dir']['path'] . $path; + + $this->assertFalse( self::$filesystem->atime( $path ) ); + } +} diff --git a/tests/phpunit/tests/filesystem/wpFilesystemDirect/base.php b/tests/phpunit/tests/filesystem/wpFilesystemDirect/base.php new file mode 100644 index 0000000000000..dc1252bd3a29c --- /dev/null +++ b/tests/phpunit/tests/filesystem/wpFilesystemDirect/base.php @@ -0,0 +1,197 @@ + array( + 'type' => 'd', + 'path' => $test_data_root_dir, + ), + 'test_dir' => array( + 'type' => 'd', + 'path' => $test_data_dir, + ), + 'subdir' => array( + 'type' => 'd', + 'path' => $test_data_dir . 'subdir/', + ), + + // Then files. + 'visible_file' => array( + 'type' => 'f', + 'path' => $test_data_dir . 'a_file_that_exists.txt', + 'contents' => "Contents of a file.\r\nNext line of a file.\r\n", + ), + 'hidden_file' => array( + 'type' => 'f', + 'path' => $test_data_dir . '.a_hidden_file', + 'contents' => "A hidden file.\r\n", + ), + 'subfile' => array( + 'type' => 'f', + 'path' => $test_data_dir . 'subdir/subfile.txt', + 'contents' => "A file in a subdirectory.\r\n", + ), + ); + } + + /** + * Creates any missing test assets before each test. + */ + public function set_up() { + parent::set_up(); + + foreach ( self::$file_structure as $entry ) { + if ( 'd' === $entry['type'] ) { + $this->create_directory_if_needed( $entry['path'] ); + } elseif ( 'f' === $entry['type'] ) { + $this->create_file_if_needed( + $entry['path'], + isset( $entry['contents'] ) ? $entry['contents'] : '' + ); + } + } + } + + /** + * Removes any existing test assets after each test. + */ + public function tear_down() { + foreach ( array_reverse( self::$file_structure ) as $entry ) { + if ( ! file_exists( $entry['path'] ) ) { + continue; + } + + if ( 'f' === $entry['type'] ) { + unlink( $entry['path'] ); + } elseif ( 'd' === $entry['type'] ) { + rmdir( $entry['path'] ); + } + } + + parent::tear_down(); + } + + /** + * Creates a directory if it doesn't already exist. + * + * @throws Exception If the path already exists as a file. + * + * @param string $path The path to the directory. + */ + public function create_directory_if_needed( $path ) { + if ( file_exists( $path ) ) { + if ( is_file( $path ) ) { + throw new Exception( "$path already exists as a file." ); + } + + return; + } + + mkdir( $path ); + } + + /** + * Creates a file if it doesn't already exist. + * + * @throws Exception If the path already exists as a directory. + * + * @param string $path The path to the file. + * @param string $contents Optional. The contents of the file. Default empty string. + */ + public function create_file_if_needed( $path, $contents = '' ) { + if ( file_exists( $path ) ) { + if ( is_dir( $path ) ) { + throw new Exception( "$path already exists as a directory." ); + } + + return; + } + + file_put_contents( $path, $contents ); + } + + /** + * Determines whether the operating system is Windows. + * + * @return bool Whether the operating system is Windows. + */ + public static function is_windows() { + return 'WIN' === substr( PHP_OS, 0, 3 ); + } + + /** + * Data provider. + * + * @return array[] + */ + public function data_paths_that_exist() { + return array( + 'a file that exists' => array( + 'path' => 'a_file_that_exists.txt', + ), + 'a directory that exists' => array( + 'path' => '', + ), + ); + } + + /** + * Data provider. + * + * @return array[] + */ + public function data_paths_that_do_not_exist() { + return array( + 'a file that does not exist' => array( + 'path' => 'a_file_that_does_not_exist.txt', + ), + 'a directory that does not exist' => array( + 'path' => 'a_directory_that_does_not_exist', + ), + ); + } +} diff --git a/tests/phpunit/tests/filesystem/wpFilesystemDirect/chdir.php b/tests/phpunit/tests/filesystem/wpFilesystemDirect/chdir.php new file mode 100644 index 0000000000000..cdea47928b2f2 --- /dev/null +++ b/tests/phpunit/tests/filesystem/wpFilesystemDirect/chdir.php @@ -0,0 +1,95 @@ +cwd(); + $path = wp_normalize_path( realpath( self::$file_structure['test_dir']['path'] ) ) . $path; + $chdir_result = self::$filesystem->chdir( $path ); + $cwd_result = self::$filesystem->cwd(); + + // Reset the current working directory. + self::$filesystem->chdir( $original_cwd ); + + $this->assertFalse( + $chdir_result, + 'Changing working directory succeeded.' + ); + + $this->assertSame( + $original_cwd, + $cwd_result, + 'The current working directory was changed.' + ); + } + + /** + * Data provider. + * + * @return array[] + */ + public function data_should_fail_to_change_directory() { + return array( + 'a file that exists' => array( + 'path' => 'a_file_that_exists.txt', + ), + 'a file that does not exist' => array( + 'path' => 'a_file_that_does_not_exist.txt', + ), + 'a directory that does not exist' => array( + 'path' => 'a_directory_that_does_not_exist', + ), + ); + } + + /** + * Tests that `WP_Filesystem_Direct::chdir()` changes to + * an existing directory. + * + * @ticket 57774 + */ + public function test_should_change_directory() { + $original_cwd = self::$filesystem->cwd(); + $path = wp_normalize_path( realpath( self::$file_structure['test_dir']['path'] ) ); + $chdir_result = self::$filesystem->chdir( $path ); + $cwd_result = self::$filesystem->cwd(); + + // Reset the current working directory. + self::$filesystem->chdir( $original_cwd ); + + $this->assertTrue( + $chdir_result, + 'Changing working directory failed.' + ); + + $this->assertSame( + $path, + $cwd_result, + 'The current working directory was incorrect.' + ); + } +} diff --git a/tests/phpunit/tests/filesystem/wpFilesystemDirect/chgrp.php b/tests/phpunit/tests/filesystem/wpFilesystemDirect/chgrp.php new file mode 100644 index 0000000000000..a56e5c5ac2b41 --- /dev/null +++ b/tests/phpunit/tests/filesystem/wpFilesystemDirect/chgrp.php @@ -0,0 +1,32 @@ +assertFalse( self::$filesystem->chgrp( self::$file_structure['test_dir']['path'] . $path, 0 ) ); + } +} diff --git a/tests/phpunit/tests/filesystem/wpFilesystemDirect/chmod.php b/tests/phpunit/tests/filesystem/wpFilesystemDirect/chmod.php new file mode 100644 index 0000000000000..4deb47c4f09fb --- /dev/null +++ b/tests/phpunit/tests/filesystem/wpFilesystemDirect/chmod.php @@ -0,0 +1,77 @@ +assertFalse( self::$filesystem->chmod( $path ) ); + } + + /** + * Tests that `WP_Filesystem_Direct::chmod()` should set + * $mode when it is not passed. + * + * This test runs in a separate process so that it can define + * constants without impacting other tests. + * + * This test does not preserve global state to prevent the exception + * "Serialization of 'Closure' is not allowed." when running in a + * separate process. + * + * @ticket 57774 + * + * @dataProvider data_should_set_mode_when_not_passed + * + * @runInSeparateProcess + * @preserveGlobalState disabled + * + * @param string $path The path. + * @param string $type The type of path. "FILE" for file, "DIR" for directory. + */ + public function test_should_handle_set_mode_when_not_passed( $path, $type ) { + define( 'FS_CHMOD_' . $type, ( 'FILE' === $type ? 0644 : 0755 ) ); + + $this->assertTrue( self::$filesystem->chmod( self::$file_structure['test_dir']['path'] . $path, false ) ); + } + + /** + * Data provider. + * + * @return array[] + */ + public function data_should_set_mode_when_not_passed() { + return array( + 'a file' => array( + 'path' => 'a_file_that_exists.txt', + 'type' => 'FILE', + ), + 'a directory' => array( + 'path' => '', + 'type' => 'DIR', + ), + ); + } +} diff --git a/tests/phpunit/tests/filesystem/wpFilesystemDirect/chown.php b/tests/phpunit/tests/filesystem/wpFilesystemDirect/chown.php new file mode 100644 index 0000000000000..040693b03c54c --- /dev/null +++ b/tests/phpunit/tests/filesystem/wpFilesystemDirect/chown.php @@ -0,0 +1,32 @@ +assertFalse( self::$filesystem->chown( $path, fileowner( __FILE__ ) ) ); + } +} diff --git a/tests/phpunit/tests/filesystem/wpFilesystemDirect/construct.php b/tests/phpunit/tests/filesystem/wpFilesystemDirect/construct.php new file mode 100644 index 0000000000000..e8c475a9ee044 --- /dev/null +++ b/tests/phpunit/tests/filesystem/wpFilesystemDirect/construct.php @@ -0,0 +1,39 @@ +assertSame( + 'direct', + $filesystem->method, + 'The "$method" property is not set to "direct".' + ); + + $this->assertInstanceOf( + 'WP_Error', + $filesystem->errors, + 'The "$errors" property is not set to a WP_Error object.' + ); + } +} diff --git a/tests/phpunit/tests/filesystem/wpFilesystemDirect/copy.php b/tests/phpunit/tests/filesystem/wpFilesystemDirect/copy.php new file mode 100644 index 0000000000000..d0355c20e5662 --- /dev/null +++ b/tests/phpunit/tests/filesystem/wpFilesystemDirect/copy.php @@ -0,0 +1,72 @@ +copy( $source, $destination, true ); + + unlink( $destination ); + + $this->assertTrue( $actual ); + } + + /** + * Tests that `WP_Filesystem_Direct::copy()` does not overwrite + * an existing destination when overwriting is disabled. + * + * @ticket 57774 + */ + public function test_should_not_overwrite_an_existing_file_when_overwriting_is_disabled() { + $source = self::$file_structure['test_dir']['path'] . 'a_file_that_exists.txt'; + $destination = self::$file_structure['test_dir']['path'] . 'a_file_that_exists.dest'; + + if ( ! file_exists( $destination ) ) { + touch( $destination ); + } + + $actual = self::$filesystem->copy( $source, $destination ); + + unlink( $destination ); + + $this->assertFalse( $actual ); + } + + /** + * Tests that `WP_Filesystem_Direct::copy()` does not overwrite an existing + * destination when overwriting is enabled and the source and destination + * are the same. + * + * @ticket 57774 + */ + public function test_should_not_overwrite_when_overwriting_is_enabled_and_source_and_destination_are_the_same() { + $source = self::$file_structure['test_dir']['path'] . 'a_file_that_exists.txt'; + $this->assertFalse( self::$filesystem->copy( $source, $source, true ) ); + } +} diff --git a/tests/phpunit/tests/filesystem/wpFilesystemDirect/cwd.php b/tests/phpunit/tests/filesystem/wpFilesystemDirect/cwd.php new file mode 100644 index 0000000000000..c4ce9617314cb --- /dev/null +++ b/tests/phpunit/tests/filesystem/wpFilesystemDirect/cwd.php @@ -0,0 +1,26 @@ +assertSame( wp_normalize_path( dirname( ABSPATH ) ), wp_normalize_path( self::$filesystem->cwd() ) ); + } +} diff --git a/tests/phpunit/tests/filesystem/wpFilesystemDirect/delete.php b/tests/phpunit/tests/filesystem/wpFilesystemDirect/delete.php new file mode 100644 index 0000000000000..4afe54890a5f3 --- /dev/null +++ b/tests/phpunit/tests/filesystem/wpFilesystemDirect/delete.php @@ -0,0 +1,204 @@ +assertFalse( self::$filesystem->delete( '' ) ); + } + + /** + * Tests that `WP_Filesystem_Direct::delete()` deletes an empty directory. + * + * @ticket 57774 + */ + public function test_should_delete_an_empty_directory() { + $dir = self::$file_structure['test_dir']['path'] . 'directory-to-delete'; + + $this->assertTrue( + mkdir( $dir ), + 'The directory was not created.' + ); + + $this->assertTrue( + self::$filesystem->delete( $dir ), + 'The directory was not deleted.' + ); + } + + /** + * Tests that `WP_Filesystem_Direct::delete()` deletes a directory with contents. + * + * @ticket 57774 + */ + public function test_should_delete_a_directory_with_contents() { + $this->assertTrue( + self::$filesystem->delete( self::$file_structure['test_dir']['path'], true ), + 'Directory deletion failed.' + ); + + $this->assertDirectoryDoesNotExist( + self::$file_structure['test_dir']['path'], + 'The directory was not deleted.' + ); + } + + /** + * Tests that `WP_Filesystem_Direct::delete()` deletes a file. + * + * @ticket 57774 + * + * @dataProvider data_should_delete_a_file + * + * @param string $key The key for the file in `self::$filesystem_structure`. + */ + public function test_should_delete_a_file( $file ) { + $file = self::$file_structure[ $file ]['path'] . $file; + + $this->assertTrue( self::$filesystem->delete( $file ), 'File deletion failed.' ); + $this->assertFileDoesNotExist( $file, 'The file was not deleted.' ); + } + + /** + * Data provider. + * + * @return array[] + */ + public function data_should_delete_a_file() { + return array( + 'A visible file' => array( + 'key' => 'visible_file', + ), + 'A hidden file' => array( + 'key' => 'hidden_file', + ), + ); + } + + /** + * Tests that `WP_Filesystem_Direct::delete()` + * returns true when deleting a path that does not exist. + * + * @ticket 57774 + * + * @dataProvider data_paths_that_do_not_exist + * + * @param string $path The path. + */ + public function test_should_return_true_when_deleting_path_that_does_not_exist( $path ) { + $path = self::$file_structure['test_dir']['path'] . $path; + + /* + * Verify that the path doesn't exist before testing. + * + * assertFileDoesNotExist() uses file_exists(), which returns the same result for both + * files and directories. + * assertDirectoryDoesNotExist() uses is_dir(), which tests strictly for a directory. + * + * For more useful debugging in the event of a failure, test for a directory first. + */ + $this->assertDirectoryDoesNotExist( $path, "$path already existed as a directory before testing." ); + $this->assertFileDoesNotExist( $path, "$path already existed as a file before testing." ); + + $this->assertTrue( self::$filesystem->delete( $path ), 'Attempting to delete a non-existent path should return true.' ); + } + + /** + * Tests that `WP_Filesystem_Direct::delete()` + * returns false when a directory's contents cannot be deleted. + * + * @ticket 57774 + */ + public function test_should_return_false_when_contents_cannot_be_deleted() { + global $wp_filesystem; + + $wp_filesystem = new WP_Filesystem_Direct( array() ); + + $path = self::$file_structure['test_dir']['path'] . 'dir-to-delete/'; + + if ( ! is_dir( $path ) ) { + mkdir( $path ); + } + + // Set up mock filesystem. + $filesystem_mock = $this->getMockBuilder( 'WP_Filesystem_Direct' ) + ->setConstructorArgs( array( null ) ) + // Note: setMethods() is deprecated in PHPUnit 9, but still supported. + ->setMethods( array( 'dirlist' ) ) + ->getMock(); + + $filesystem_mock->expects( $this->once() ) + ->method( 'dirlist' ) + ->willReturn( + array( 'a_file_that_does_not_exist.txt' => array( 'type' => 'f' ) ) + ); + + $wp_filesystem_backup = $wp_filesystem; + $wp_filesystem = $filesystem_mock; + + $actual = $filesystem_mock->delete( $path, true ); + + if ( $actual ) { + rmdir( $path ); + } + + $wp_filesystem = $wp_filesystem_backup; + + $this->assertFalse( $actual ); + } + + /** + * Tests that `WP_Filesystem_Direct::delete()` + * returns false when the path is not a file or directory, but exists. + * + * @ticket 57774 + */ + public function test_should_return_false_when_path_exists_but_is_not_a_file_or_directory() { + global $wp_filesystem; + + $wp_filesystem = new WP_Filesystem_Direct( array() ); + + // Set up mock filesystem. + $filesystem_mock = $this->getMockBuilder( 'WP_Filesystem_Direct' ) + ->setConstructorArgs( array( null ) ) + // Note: setMethods() is deprecated in PHPUnit 9, but still supported. + ->setMethods( array( 'is_file', 'dirlist' ) ) + ->getMock(); + + $filesystem_mock->expects( $this->once() ) + ->method( 'is_file' ) + ->willReturn( false ); + + $filesystem_mock->expects( $this->once() ) + ->method( 'dirlist' ) + ->willReturn( false ); + + $wp_filesystem_backup = $wp_filesystem; + $wp_filesystem = $filesystem_mock; + + $actual = $filesystem_mock->delete( self::$file_structure['subdir']['path'], true ); + + $wp_filesystem = $wp_filesystem_backup; + + $this->assertFalse( $actual ); + } +} diff --git a/tests/phpunit/tests/filesystem/wpFilesystemDirect/dirlist.php b/tests/phpunit/tests/filesystem/wpFilesystemDirect/dirlist.php new file mode 100644 index 0000000000000..04dab2a48a612 --- /dev/null +++ b/tests/phpunit/tests/filesystem/wpFilesystemDirect/dirlist.php @@ -0,0 +1,130 @@ +dirlist( self::$file_structure['test_dir']['path'] . $path, $include_hidden, $recursive ); + + if ( is_array( $expected ) ) { + $this->assertSameSets( + $expected, + array_keys( $actual ), + 'The array keys do not match.' + ); + } else { + $this->assertFalse( + $actual, + '`WP_Filesystem_Direct::dirlist()` did not return false.' + ); + } + } + + /** + * Data provider. + * + * @return array[] + */ + public function data_should_get_dirlist() { + return array( + 'a directory that exists excluding hidden files' => array( + 'path' => '', + 'include_hidden' => false, + 'recursive' => false, + 'expected' => array( + 'a_file_that_exists.txt', + 'subdir', + ), + ), + 'a directory that exists including hidden files' => array( + 'path' => '', + 'include_hidden' => true, + 'recursive' => false, + 'expected' => array( + 'a_file_that_exists.txt', + '.a_hidden_file', + 'subdir', + ), + ), + 'a directory that does not exist' => array( + 'path' => 'a_directory_that_does_not_exist/', + 'include_hidden' => true, + 'recursive' => false, + 'expected' => false, + ), + 'a file that exists' => array( + 'path' => 'a_file_that_exists.txt', + 'include_hidden' => true, + 'recursive' => false, + 'expected' => array( + 'a_file_that_exists.txt', + ), + ), + 'a file that does not exist' => array( + 'path' => 'a_file_that_does_not_exist.txt', + 'include_hidden' => true, + 'recursive' => false, + 'expected' => false, + ), + ); + } + + /** + * Tests that `WP_Filesystem_Direct::dirlist()` recurses + * into a subdirectory. + * + * @ticket 57774 + */ + public function test_should_recurse_into_subdirectory() { + $actual = self::$filesystem->dirlist( self::$file_structure['test_dir']['path'], true, true ); + + $this->assertIsArray( $actual, 'Did not return an array.' ); + $this->assertArrayHasKey( 'subdir', $actual, 'The subdirectory was not detected.' ); + $this->assertArrayHasKey( 'files', $actual['subdir'], 'The subdirectory does not have a "files" key.' ); + $this->assertNotEmpty( $actual['subdir']['files'], "The subdirectory's contents were not retrieved." ); + $this->assertArrayHasKey( 'subfile.txt', $actual['subdir']['files'], 'The subfile was not detected.' ); + } + + /** + * Tests that `WP_Filesystem_Direct::dirlist()` should not recurse + * into a subdirectory. + * + * @ticket 57774 + */ + public function test_should_not_recurse_into_subdirectory() { + + $actual = self::$filesystem->dirlist( self::$file_structure['test_dir']['path'], true, false ); + + $this->assertIsArray( $actual, 'Did not return an array.' ); + $this->assertArrayHasKey( 'subdir', $actual, 'The subdirectory was not detected.' ); + $this->assertArrayHasKey( 'files', $actual['subdir'], 'The "files" key was not set.' ); + $this->assertIsArray( $actual['subdir']['files'], 'The "files" key was not set to an array.' ); + $this->assertEmpty( $actual['subdir']['files'], 'The "files" array was not empty.' ); + } +} diff --git a/tests/phpunit/tests/filesystem/wpFilesystemDirect/exists.php b/tests/phpunit/tests/filesystem/wpFilesystemDirect/exists.php new file mode 100644 index 0000000000000..11eb32621da6a --- /dev/null +++ b/tests/phpunit/tests/filesystem/wpFilesystemDirect/exists.php @@ -0,0 +1,44 @@ +assertTrue( self::$filesystem->exists( self::$file_structure['test_dir']['path'] . $path ) ); + } + + /** + * Tests that `WP_Filesystem_Direct::exists()` determines that + * a path does not exist. + * + * @ticket 57774 + * + * @dataProvider data_paths_that_do_not_exist + * + * @param string $path The path to check. + */ + public function test_should_determine_that_a_path_does_not_exist( $path ) { + $this->assertFalse( self::$filesystem->exists( self::$file_structure['test_dir']['path'] . $path ) ); + } +} diff --git a/tests/phpunit/tests/filesystem/wpFilesystemDirect/getContents.php b/tests/phpunit/tests/filesystem/wpFilesystemDirect/getContents.php new file mode 100644 index 0000000000000..fed713239a48e --- /dev/null +++ b/tests/phpunit/tests/filesystem/wpFilesystemDirect/getContents.php @@ -0,0 +1,47 @@ +assertSame( + "Contents of a file.\r\nNext line of a file.\r\n", + self::$filesystem->get_contents( $file ) + ); + } + + /** + * Tests that `WP_Filesystem_Direct::get_contents()` + * returns false for a file that does not exist. + * + * @ticket 57774 + * + * @dataProvider data_paths_that_do_not_exist + * + * @param string $path The path. + */ + public function test_should_return_false( $path ) { + $this->assertFalse( self::$filesystem->get_contents( self::$file_structure['test_dir']['path'] . $path ) ); + } +} diff --git a/tests/phpunit/tests/filesystem/wpFilesystemDirect/getContentsArray.php b/tests/phpunit/tests/filesystem/wpFilesystemDirect/getContentsArray.php new file mode 100644 index 0000000000000..0e09822c07328 --- /dev/null +++ b/tests/phpunit/tests/filesystem/wpFilesystemDirect/getContentsArray.php @@ -0,0 +1,57 @@ +get_contents_array( $file ); + + $this->assertIsArray( + $contents, + 'The file contents are not an array.' + ); + + $this->assertSameSetsWithIndex( + array( + "Contents of a file.\r\n", + "Next line of a file.\r\n", + ), + $contents, + 'The file contents do not match the expected value.' + ); + } + + /** + * Tests that `WP_Filesystem_Direct::get_contents_array()` + * returns false for a path that does not exist. + * + * @ticket 57774 + * + * @dataProvider data_paths_that_do_not_exist + * + * @param string $path The path. + */ + public function test_should_return_false( $path ) { + $this->assertFalse( self::$filesystem->get_contents_array( self::$file_structure['test_dir']['path'] . $path ) ); + } +} diff --git a/tests/phpunit/tests/filesystem/wpFilesystemDirect/getchmod.php b/tests/phpunit/tests/filesystem/wpFilesystemDirect/getchmod.php new file mode 100644 index 0000000000000..2b0c2ce327afc --- /dev/null +++ b/tests/phpunit/tests/filesystem/wpFilesystemDirect/getchmod.php @@ -0,0 +1,48 @@ +getchmod( self::$file_structure['test_dir']['path'] . $path ); + $this->assertNotSame( '', $actual ); + } + + /** + * Tests that `WP_Filesystem_Direct::getchmod()` returns + * the permissions for a path that does not exist. + * + * @dataProvider data_paths_that_do_not_exist + * + * @ticket 57774 + * + * @param string $path The path. + */ + public function test_should_get_chmod_for_a_path_that_does_not_exist( $path ) { + $actual = self::$filesystem->getchmod( self::$file_structure['test_dir']['path'] . $path ); + $this->assertNotSame( '', $actual ); + } +} diff --git a/tests/phpunit/tests/filesystem/wpFilesystemDirect/isDir.php b/tests/phpunit/tests/filesystem/wpFilesystemDirect/isDir.php new file mode 100644 index 0000000000000..1a367851f6a78 --- /dev/null +++ b/tests/phpunit/tests/filesystem/wpFilesystemDirect/isDir.php @@ -0,0 +1,63 @@ +assertTrue( self::$filesystem->is_dir( self::$file_structure['test_dir']['path'] ) ); + } + + /** + * Tests that `WP_Filesystem_Direct::is_directory()` determines that + * a path is not a directory. + * + * @ticket 57774 + * + * @dataProvider data_should_determine_that_a_path_is_not_a_directory + * + * @param string $path The path to check. + * @param string $type The type of resource. Accepts 'f' or 'd'. + * Used to invert $expected due to data provider setup. + */ + public function test_should_determine_that_a_path_is_not_a_directory( $path ) { + $this->assertFalse( self::$filesystem->is_dir( self::$file_structure['test_dir']['path'] . $path ) ); + } + + /** + * Data provider. + * + * @return array[] + */ + public function data_should_determine_that_a_path_is_not_a_directory() { + return array( + 'a file that exists' => array( + 'path' => 'a_file_that_exists.txt', + ), + 'a file that does not exist' => array( + 'path' => 'a_file_that_does_not_exist.txt', + ), + 'a directory that does not exist' => array( + 'path' => 'a_directory_that_does_not_exist', + ), + ); + } +} diff --git a/tests/phpunit/tests/filesystem/wpFilesystemDirect/isFile.php b/tests/phpunit/tests/filesystem/wpFilesystemDirect/isFile.php new file mode 100644 index 0000000000000..e0471f309b62f --- /dev/null +++ b/tests/phpunit/tests/filesystem/wpFilesystemDirect/isFile.php @@ -0,0 +1,61 @@ +assertTrue( self::$filesystem->is_file( self::$file_structure['test_dir']['path'] . 'a_file_that_exists.txt' ) ); + } + + /** + * Tests that `WP_Filesystem_Direct::is_file()` determies that + * a path is not a file. + * + * @ticket 57774 + * + * @dataProvider data_should_determine_if_a_path_is_not_a_file + * + * @param string $path The path to check. + */ + public function test_should_determine_that_a_path_is_not_a_file( $path ) { + $this->assertFalse( self::$filesystem->is_file( self::$file_structure['test_dir']['path'] . $path ) ); + } + + /** + * Data provider. + * + * @return array[] + */ + public function data_should_determine_if_a_path_is_not_a_file() { + return array( + 'a file that does not exist' => array( + 'path' => 'a_file_that_does_not_exist.txt', + ), + 'a directory that exists' => array( + 'path' => '', + ), + 'a directory that does not exist' => array( + 'path' => 'a_directory_that_does_not_exist', + ), + ); + } +} diff --git a/tests/phpunit/tests/filesystem/wpFilesystemDirect/isReadable.php b/tests/phpunit/tests/filesystem/wpFilesystemDirect/isReadable.php new file mode 100644 index 0000000000000..9ccde92c709ac --- /dev/null +++ b/tests/phpunit/tests/filesystem/wpFilesystemDirect/isReadable.php @@ -0,0 +1,46 @@ +assertTrue( self::$filesystem->is_readable( self::$file_structure['test_dir']['path'] . $path ) ); + } + + /** + * Tests that `WP_Filesystem_Direct::is_readable()` determines that + * a path is not readable. + * + * @ticket 57774 + * + * @dataProvider data_paths_that_do_not_exist + * + * @param string $path The path. + */ + public function test_should_determine_that_a_path_is_not_readable( $path ) { + $this->assertFalse( self::$filesystem->is_readable( self::$file_structure['test_dir']['path'] . $path ) ); + } +} diff --git a/tests/phpunit/tests/filesystem/wpFilesystemDirect/isWritable.php b/tests/phpunit/tests/filesystem/wpFilesystemDirect/isWritable.php new file mode 100644 index 0000000000000..f3a1e0ea757de --- /dev/null +++ b/tests/phpunit/tests/filesystem/wpFilesystemDirect/isWritable.php @@ -0,0 +1,46 @@ +assertTrue( self::$filesystem->is_writable( self::$file_structure['test_dir']['path'] . $path ) ); + } + + /** + * Tests that `WP_Filesystem_Direct::is_writable()` determines that + * a path is not writable. + * + * @ticket 57774 + * + * @dataProvider data_paths_that_do_not_exist + * + * @param string $path The path. + */ + public function test_should_determine_that_a_path_is_not_writable( $path ) { + $this->assertFalse( self::$filesystem->is_writable( self::$file_structure['test_dir']['path'] . $path ) ); + } +} diff --git a/tests/phpunit/tests/filesystem/wpFilesystemDirect/mkdir.php b/tests/phpunit/tests/filesystem/wpFilesystemDirect/mkdir.php new file mode 100644 index 0000000000000..53fc1575f32c7 --- /dev/null +++ b/tests/phpunit/tests/filesystem/wpFilesystemDirect/mkdir.php @@ -0,0 +1,211 @@ +mkdir( $path ); + + if ( $path !== self::$file_structure['test_dir']['path'] && is_dir( $path ) ) { + rmdir( $path ); + } + + $this->assertTrue( $actual ); + } + + /** + * Data provider. + * + * @return array[] + */ + public function data_should_create_directory() { + return array( + 'no trailing slash' => array( + 'path' => 'TEST_DIR/directory-to-create', + ), + 'a trailing slash' => array( + 'path' => 'TEST_DIR/directory-to-create/', + ), + ); + } + + /** + * Tests that `WP_Filesystem_Direct::mkdir()` does not create a directory. + * + * This test runs in a separate process so that it can define + * constants without impacting other tests. + * + * This test does not preserve global state to prevent the exception + * "Serialization of 'Closure' is not allowed." when running in a + * separate process. + * + * @ticket 57774 + * + * @dataProvider data_should_not_create_directory + * + * @runInSeparateProcess + * @preserveGlobalState disabled + * + * @param mixed $path The path to create. + */ + public function test_should_not_create_directory( $path ) { + define( 'FS_CHMOD_DIR', 0755 ); + + $path = str_replace( 'TEST_DIR', self::$file_structure['test_dir']['path'], $path ); + $actual = self::$filesystem->mkdir( $path ); + + if ( $path !== self::$file_structure['test_dir']['path'] && is_dir( $path ) ) { + rmdir( $path ); + } + + $this->assertFalse( $actual ); + } + + /** + * Data provider. + * + * @return array[] + */ + public function data_should_not_create_directory() { + return array( + 'empty path' => array( + 'path' => '', + ), + 'a path that exists' => array( + 'path' => 'TEST_DIR', + ), + ); + } + + /** + * Tests that `WP_Filesystem_Direct::mkdir()` sets chmod. + * + * @ticket 57774 + */ + public function test_should_set_chmod() { + $path = self::$file_structure['test_dir']['path'] . 'directory-to-create'; + + $created = self::$filesystem->mkdir( $path, 0644 ); + $chmod = substr( sprintf( '%o', fileperms( $path ) ), -4 ); + + if ( $path !== self::$file_structure['test_dir']['path'] && is_dir( $path ) ) { + rmdir( $path ); + } + + $expected_permissions = $this->is_windows() ? '0777' : '0644'; + + $this->assertTrue( $created, 'The directory was not created.' ); + $this->assertSame( $expected_permissions, $chmod, 'The permissions are incorrect.' ); + } + + /** + * Tests that `WP_Filesystem_Direct::mkdir()` sets the owner. + * + * This test runs in a separate process so that it can define + * constants without impacting other tests. + * + * This test does not preserve global state to prevent the exception + * "Serialization of 'Closure' is not allowed." when running in a + * separate process. + * + * @ticket 57774 + * + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function test_should_set_owner() { + define( 'FS_CHMOD_DIR', 0755 ); + + $path = self::$file_structure['test_dir']['path'] . 'directory-to-create'; + + // Get the default owner. + self::$filesystem->mkdir( $path ); + $original_owner = fileowner( $path ); + + rmdir( $path ); + + $expected_group = $this->is_windows() ? $original_owner : $original_owner + 1; + $created = self::$filesystem->mkdir( $path, 0755, $expected_group ); + $owner = fileowner( $path ); + + if ( $path !== self::$file_structure['test_dir']['path'] && is_dir( $path ) ) { + rmdir( $path ); + } + + $this->assertTrue( $created, 'The directory was not created.' ); + $this->assertSame( $expected_group, $owner, 'The owner is incorrect.' ); + } + + /** + * Tests that `WP_Filesystem_Direct::mkdir()` sets the group. + * + * This test runs in a separate process so that it can define + * constants without impacting other tests. + * + * This test does not preserve global state to prevent the exception + * "Serialization of 'Closure' is not allowed." when running in a + * separate process. + * + * @ticket 57774 + * + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function test_should_set_group() { + define( 'FS_CHMOD_DIR', 0755 ); + + $path = self::$file_structure['test_dir']['path'] . 'directory-to-create'; + + // Get the default group. + self::$filesystem->mkdir( $path ); + $original_group = filegroup( $path ); + + rmdir( $path ); + + $expected_group = $this->is_windows() ? $original_group : $original_group + 1; + $created = self::$filesystem->mkdir( $path, 0755, false, $expected_group ); + $group = filegroup( $path ); + + if ( $path !== self::$file_structure['test_dir']['path'] && is_dir( $path ) ) { + rmdir( $path ); + } + + $this->assertTrue( $created, 'The directory was not created.' ); + $this->assertSame( $expected_group, $group, 'The group is incorrect.' ); + } +} diff --git a/tests/phpunit/tests/filesystem/wpFilesystemDirect/move.php b/tests/phpunit/tests/filesystem/wpFilesystemDirect/move.php new file mode 100644 index 0000000000000..a6822a90f418b --- /dev/null +++ b/tests/phpunit/tests/filesystem/wpFilesystemDirect/move.php @@ -0,0 +1,150 @@ +move( $source, $destination, true ); + + rename( $destination, $source ); + + $this->assertTrue( $actual ); + } + + /** + * Tests that `WP_Filesystem_Direct::move()` does not overwrite + * an existing destination when overwriting is disabled. + * + * @ticket 57774 + */ + public function test_should_not_overwrite_an_existing_file_when_overwriting_is_disabled() { + $source = self::$file_structure['visible_file']['path']; + $destination = self::$file_structure['subfile']['path']; + $actual = self::$filesystem->move( $source, $destination ); + + $this->assertFalse( $actual ); + } + + /** + * Tests that `WP_Filesystem_Direct::move()` moves directories. + * + * @ticket 57774 + */ + public function test_should_move_directories() { + $source = self::$file_structure['test_dir']['path']; + $destination = untrailingslashit( self::$file_structure['test_dir']['path'] ) . '-dest'; + $actual = self::$filesystem->move( $source, $destination, true ); + + $source_exists = is_dir( $source ); + $destination_exists = is_dir( $destination ); + + if ( $actual ) { + $restored = rename( $destination, $source ); + } + + $this->assertTrue( $actual, 'The directory was not moved.' ); + $this->assertFalse( $source_exists, 'The source still exists.' ); + $this->assertTrue( $destination_exists, 'The destination does not exist.' ); + $this->assertTrue( $restored, 'The test assets were not cleaned up after the test.' ); + } + + /** + * Tests that `WP_Filesystem_Direct::move()` returns false for an + * invalid destination. + * + * @ticket 57774 + */ + public function test_should_return_false_for_invalid_destination() { + $source = self::$file_structure['test_dir']['path']; + $destination = 'http://example.org'; + + $this->assertFalse( self::$filesystem->move( $source, $destination, true ) ); + } + + /** + * Tests that `WP_Filesystem_Direct::move()` returns false for an + * invalid destination. + * + * @ticket 57774 + */ + public function test_should_return_false_when_overwriting_is_enabled_the_destination_exists_but_cannot_be_deleted() { + global $wp_filesystem; + $wpfilesystem_backup = $wp_filesystem; + + // Force failure conditions. + $filesystem_mock = $this->getMockBuilder( 'WP_Filesystem_Direct' ) + // Note: setMethods() is deprecated in PHPUnit 9, but still supported. + ->setMethods( array( 'exists', 'delete' ) ) + ->setConstructorArgs( array( null ) ) + ->getMock(); + + $filesystem_mock->expects( $this->once() )->method( 'exists' )->willReturn( true ); + $filesystem_mock->expects( $this->once() )->method( 'delete' )->willReturn( false ); + $wp_filesystem = $filesystem_mock; + + $actual = $wp_filesystem->move( + self::$file_structure['test_dir']['path'], + self::$file_structure['subdir']['path'], + true + ); + + // Restore the filesystem. + $wp_filesystem = $wpfilesystem_backup; + + $this->assertFalse( $actual ); + } + + /** + * Tests that `WP_Filesystem_Direct::move()` falls back to a single + * file copy when the source and destination do not exist. + * + * @ticket 57774 + */ + public function test_should_fall_back_to_single_file_copy_when_source_and_destination_do_not_exist() { + global $wp_filesystem; + + $source = self::$file_structure['test_dir']['path'] . 'a_file_that_does_not_exist.txt'; + $destination = self::$file_structure['test_dir']['path'] . 'another_file_that_does_not_exist.txt'; + + // Set up mock filesystem. + $filesystem_mock = $this->getMockBuilder( 'WP_Filesystem_Direct' ) + ->setConstructorArgs( array( null ) ) + // Note: setMethods() is deprecated in PHPUnit 9, but still supported. + ->setMethods( array( 'exists', 'delete', 'is_file', 'copy' ) ) + ->getMock(); + + $filesystem_mock->expects( $this->exactly( 2 ) )->method( 'exists' )->willReturn( array( true, true ) ); + $filesystem_mock->expects( $this->exactly( 2 ) )->method( 'delete' )->willReturn( array( true, false ) ); + $filesystem_mock->expects( $this->once() )->method( 'is_file' )->willReturn( true ); + $filesystem_mock->expects( $this->once() )->method( 'copy' )->willReturn( true ); + + $wp_filesystem_backup = $wp_filesystem; + $wp_filesystem = $filesystem_mock; + + $actual = $filesystem_mock->move( $source, $destination, true ); + $wp_filesystem = $wp_filesystem_backup; + + $this->assertTrue( $actual ); + } +} diff --git a/tests/phpunit/tests/filesystem/wpFilesystemDirect/mtime.php b/tests/phpunit/tests/filesystem/wpFilesystemDirect/mtime.php new file mode 100644 index 0000000000000..57d6f92bbc6f6 --- /dev/null +++ b/tests/phpunit/tests/filesystem/wpFilesystemDirect/mtime.php @@ -0,0 +1,68 @@ +mtime( self::$file_structure['test_dir']['path'] . $path ); + $has_mtime = false !== $result; + + $this->assertTrue( + $has_mtime, + 'The mtime was not determined.' + ); + + $this->assertIsInt( + $result, + 'The mtime is not an integer.' + ); + } + + /** + * Tests that `WP_Filesystem_Direct::mtime()` does not determine + * the mtime of a path. + * + * @ticket 57774 + * + * @dataProvider data_paths_that_do_not_exist + * + * @param string $path The path. + */ + public function test_should_not_determine_file_modified_time( $path ) { + $result = self::$filesystem->mtime( self::$file_structure['test_dir']['path'] . $path ); + $has_mtime = false !== $result; + + $this->assertFalse( + $has_mtime, + 'An mtime was determined.' + ); + + $this->assertIsNotInt( + $result, + 'The mtime is an integer.' + ); + } +} diff --git a/tests/phpunit/tests/filesystem/wpFilesystemDirect/putContents.php b/tests/phpunit/tests/filesystem/wpFilesystemDirect/putContents.php new file mode 100644 index 0000000000000..8eabfffdb2f6a --- /dev/null +++ b/tests/phpunit/tests/filesystem/wpFilesystemDirect/putContents.php @@ -0,0 +1,42 @@ +assertFalse( self::$filesystem->put_contents( self::$file_structure['test_dir']['path'], 'New content.' ) ); + } + + /** + * Tests that `WP_Filesystem_Direct::put_contents()` inserts + * content into the provided file. + * + * @ticket 57774 + */ + public function test_should_insert_contents_into_file() { + $file = self::$file_structure['test_dir']['path'] . 'file-to-create.txt'; + $actual = self::$filesystem->put_contents( $file, 'New content.', 0644 ); + unlink( $file ); + + $this->assertTrue( $actual, 'The contents were not inserted.' ); + } +} diff --git a/tests/phpunit/tests/filesystem/wpFilesystemDirect/rmdir.php b/tests/phpunit/tests/filesystem/wpFilesystemDirect/rmdir.php new file mode 100644 index 0000000000000..9186fcf2e03ce --- /dev/null +++ b/tests/phpunit/tests/filesystem/wpFilesystemDirect/rmdir.php @@ -0,0 +1,200 @@ +assertFalse( self::$filesystem->rmdir( '' ) ); + } + + /** + * Tests that `WP_Filesystem_Direct::rmdir()` deletes an empty directory. + * + * @ticket 57774 + */ + public function test_should_delete_an_empty_directory() { + $dir = self::$file_structure['test_dir']['path'] . 'directory-to-delete/'; + + if ( ! is_dir( $dir ) ) { + mkdir( $dir ); + } + + $actual = self::$filesystem->rmdir( $dir ); + + if ( ! $actual ) { + rmdir( $dir ); + } + + $this->assertTrue( $actual, 'The directory was not deleted.' ); + } + + /** + * Tests that `WP_Filesystem_Direct::rmdir()` recursively deletes + * a directory with contents. + * + * @ticket 57774 + */ + public function test_should_recursively_delete_a_directory() { + $dir = self::$file_structure['test_dir']['path'] . 'directory-to-delete/'; + $file = $dir . 'file-to-delete.txt'; + $subdir = $dir . 'subdirectory-to-delete/'; + $subfile = $subdir . 'subfile-to-delete.txt'; + + mkdir( $dir, 0755 ); + mkdir( $subdir, 0755 ); + touch( $file, 0644 ); + touch( $subfile, 0644 ); + + $actual = self::$filesystem->rmdir( self::$file_structure['test_dir']['path'], true ); + + if ( ! $actual ) { + unlink( $file ); + unlink( $subfile ); + rmdir( $subdir ); + rmdir( $dir ); + } + + $this->assertTrue( $actual, 'The directory was deleted.' ); + } + + /** + * Tests that `WP_Filesystem_Direct::rmdir()` deletes a file. + * + * @ticket 57774 + */ + public function test_should_delete_a_file() { + $file = self::$file_structure['test_dir']['path'] . 'file-to-delete.txt'; + + touch( $file ); + + $actual = self::$filesystem->rmdir( $file ); + + if ( ! $actual ) { + unlink( $file ); + } + + $this->assertTrue( $actual, 'The directory was not deleted.' ); + } + + /** + * Tests that `WP_Filesystem_Direct::rmdir()` + * returns true when deleting a path that does not exist. + * + * @ticket 57774 + * + * @dataProvider data_paths_that_do_not_exist + * + * @param string $path The path. + */ + public function test_should_return_true_when_deleting_path_that_does_not_exist( $path ) { + if ( + '' === $path + || str_starts_with( $path, '.' ) + || str_starts_with( $path, '/' ) + ) { + $this->markTestSkipped( 'Dangerous delete path.' ); + } + + $this->assertTrue( self::$filesystem->rmdir( self::$file_structure['test_dir']['path'] . $path ) ); + } + + /** + * Tests that `WP_Filesystem_Direct::rmdir()` + * returns false when a directory's contents cannot be deleted. + * + * @ticket 57774 + */ + public function test_should_return_false_when_contents_cannot_be_deleted() { + + global $wp_filesystem; + + $wp_filesystem = new WP_Filesystem_Direct( array() ); + + $path = self::$file_structure['test_dir']['path'] . 'dir-to-delete/'; + + if ( ! is_dir( $path ) ) { + mkdir( $path ); + } + + // Set up mock filesystem. + $filesystem_mock = $this->getMockBuilder( 'WP_Filesystem_Direct' ) + ->setConstructorArgs( array( null ) ) + // Note: setMethods() is deprecated in PHPUnit 9, but still supported. + ->setMethods( array( 'dirlist' ) ) + ->getMock(); + + $filesystem_mock->expects( $this->once() ) + ->method( 'dirlist' ) + ->willReturn( + array( 'a_file_that_does_not_exist.txt' => array( 'type' => 'f' ) ) + ); + + $wp_filesystem_backup = $wp_filesystem; + $wp_filesystem = $filesystem_mock; + + $actual = $filesystem_mock->rmdir( $path, true ); + + if ( $actual ) { + rmdir( $path ); + } + + $wp_filesystem = $wp_filesystem_backup; + + $this->assertFalse( $actual ); + } + + /** + * Tests that `WP_Filesystem_Direct::rmdir()` + * returns false when the path is not a file or directory, but exists. + * + * @ticket 57774 + */ + public function test_should_return_false_when_path_exists_but_is_not_a_file_or_directory() { + global $wp_filesystem; + + $wp_filesystem = new WP_Filesystem_Direct( array() ); + + // Set up mock filesystem. + $filesystem_mock = $this->getMockBuilder( 'WP_Filesystem_Direct' ) + ->setConstructorArgs( array( null ) ) + // Note: setMethods() is deprecated in PHPUnit 9, but still supported. + ->setMethods( array( 'is_file', 'dirlist' ) ) + ->getMock(); + + $filesystem_mock->expects( $this->once() ) + ->method( 'is_file' ) + ->willReturn( false ); + + $filesystem_mock->expects( $this->once() ) + ->method( 'dirlist' ) + ->willReturn( false ); + + $wp_filesystem_backup = $wp_filesystem; + $wp_filesystem = $filesystem_mock; + + $actual = $filesystem_mock->rmdir( self::$file_structure['subdir']['path'], true ); + + $wp_filesystem = $wp_filesystem_backup; + + $this->assertFalse( $actual ); + } +} diff --git a/tests/phpunit/tests/filesystem/wpFilesystemDirect/size.php b/tests/phpunit/tests/filesystem/wpFilesystemDirect/size.php new file mode 100644 index 0000000000000..8870b8ac8cdec --- /dev/null +++ b/tests/phpunit/tests/filesystem/wpFilesystemDirect/size.php @@ -0,0 +1,63 @@ +size( self::$file_structure['test_dir']['path'] . $path ); + $has_filesize = false !== $result; + + $this->assertTrue( + $has_filesize, + 'The file size was not determined.' + ); + + $this->assertIsInt( + $result, + 'The file size is not an integer.' + ); + } + + /** + * Tests that `WP_Filesystem_Direct::size()` does not determine + * the filesize of a path that does not exist. + * + * @ticket 57774 + * + * @dataProvider data_paths_that_do_not_exist + * + * @param string $path The path. + */ + public function test_should_not_determine_file_size( $path ) { + $result = self::$filesystem->size( self::$file_structure['test_dir']['path'] . $path ); + $has_filesize = false !== $result; + + $this->assertFalse( + $has_filesize, + 'A file size was determined.' + ); + } +} diff --git a/tests/phpunit/tests/filesystem/wpFilesystemDirect/touch.php b/tests/phpunit/tests/filesystem/wpFilesystemDirect/touch.php new file mode 100644 index 0000000000000..6fc494479c61f --- /dev/null +++ b/tests/phpunit/tests/filesystem/wpFilesystemDirect/touch.php @@ -0,0 +1,93 @@ +touch( $file, $mtime, $atime ); + + $actual_atime = fileatime( $file ); + $actual_exists = file_exists( $file ); + $actual_mtime = filemtime( $file ); + + if ( $actual_exists ) { + unlink( $file ); + } + + $this->assertTrue( $result, 'WP_Filesystem_Direct::touch() did not return true.' ); + $this->assertTrue( $actual_exists, 'The file does not exist.' ); + $this->assertSame( $actual_atime, $expected_atime, 'The file does not have the expected atime.' ); + $this->assertSame( $actual_mtime, $expected_mtime, 'The file does not have the expected mtime.' ); + } + + /** + * Data provider. + * + * @return array[] + */ + public function data_should_create_file() { + return array( + 'default mtime or atime' => array( + 'file' => 'TEST_DATA/file-to-create.txt', + 'mtime' => 0, + 'atime' => 0, + ), + 'set mtime and default atime' => array( + 'file' => 'TEST_DATA/file-to-create.txt', + 'mtime' => 'time plus one minute', + 'atime' => 'time', + ), + 'default mtime and set atime' => array( + 'file' => 'TEST_DATA/file-to-create.txt', + 'mtime' => 'time', + 'atime' => 'time plus one minute', + ), + ); + } +}