From b5e2eb2a03f14c89ecf1e82d27121d1fd16d8400 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 8 Nov 2024 19:42:40 +0000 Subject: [PATCH 01/27] Update blueprint.json for branch/tag --- assets/playground/blueprint.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/playground/blueprint.json b/assets/playground/blueprint.json index d9e8453..193f5b9 100644 --- a/assets/playground/blueprint.json +++ b/assets/playground/blueprint.json @@ -17,7 +17,7 @@ "blogname": "AspireUpdate Demo Site" }, "plugins": [ - "https://github-proxy.com/proxy/?repo=AspirePress/AspireUpdate&branch=playground-ready", + "https://github-proxy.com/proxy/?repo=namithj/AspireUpdate&branch=main", "error-log-viewer", "plugin-check" ], From a042efcbc3da9c7e232609e3a9b8b009d8352816 Mon Sep 17 00:00:00 2001 From: Namith Jawahar <48271037+namithj@users.noreply.github.com> Date: Fri, 29 Nov 2024 20:31:34 +0530 Subject: [PATCH 02/27] Extend File System Class Extend File System Class --- includes/class-debug.php | 29 +++++--------- includes/class-filesystem-direct.php | 57 ++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 19 deletions(-) create mode 100644 includes/class-filesystem-direct.php diff --git a/includes/class-debug.php b/includes/class-debug.php index eba51fd..e90c338 100644 --- a/includes/class-debug.php +++ b/includes/class-debug.php @@ -31,23 +31,20 @@ private static function get_file_path() { /** * Initializes the WordPress Filesystem. * - * @return WP_Filesystem_Base|false The filesystem object or false on failure. + * @return WP_Filesystem_Direct|false The filesystem object or false on failure. */ private static function init_filesystem() { - global $wp_filesystem; - - if ( ! $wp_filesystem ) { - require_once ABSPATH . 'wp-admin/includes/file.php'; - WP_Filesystem(); - } - - return $wp_filesystem; + require_once ABSPATH . 'wp-admin/includes/file.php'; + require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php'; + require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php'; + WP_Filesystem(); + return new Filesystem_Direct( false ); } /** * Checks the filesystem status and logs error to debug log. * - * @param WP_Filesystem_Base $wp_filesystem The filesystem object. + * @param WP_Filesystem_Direct $wp_filesystem The filesystem object. * * @return boolean true on success and false on failure. */ @@ -144,17 +141,11 @@ public static function log( $message, $type = 'string' ) { ) . PHP_EOL; $file_path = self::get_file_path(); - - $content = ''; - if ( $wp_filesystem->exists( $file_path ) ) { - if ( $wp_filesystem->is_readable( $file_path ) ) { - $content = $wp_filesystem->get_contents( $file_path ); - } - } $wp_filesystem->put_contents( $file_path, - $formatted_message . $content, - FS_CHMOD_FILE + $formatted_message, + FS_CHMOD_FILE, + 'a' ); } } diff --git a/includes/class-filesystem-direct.php b/includes/class-filesystem-direct.php new file mode 100644 index 0000000..61c9cb8 --- /dev/null +++ b/includes/class-filesystem-direct.php @@ -0,0 +1,57 @@ +chmod( $file, $mode ); + + return true; + } +} From 246c8a0d7d572682bd5dbcdb34c8138bc5305a81 Mon Sep 17 00:00:00 2001 From: Namith Jawahar <48271037+namithj@users.noreply.github.com> Date: Wed, 4 Dec 2024 20:23:02 +0530 Subject: [PATCH 03/27] The Read Routine The Read Routine --- assets/js/aspire-update.js | 2 +- includes/class-debug.php | 24 ++------ includes/class-filesystem-direct.php | 83 +++++++++++++++++++++++++--- 3 files changed, 83 insertions(+), 26 deletions(-) diff --git a/assets/js/aspire-update.js b/assets/js/aspire-update.js index eeaa95c..0c0ca1b 100644 --- a/assets/js/aspire-update.js +++ b/assets/js/aspire-update.js @@ -97,7 +97,7 @@ class ViewLog { jQuery.ajax(parameters) .done(function (response) { if ((true == response.success) && ('' != response.data.content)) { - let lines = response.data.content.split(aspireupdate.line_ending); + let lines = response.data.content; jQuery.each(lines, function (index, line) { jQuery('
') .append( diff --git a/includes/class-debug.php b/includes/class-debug.php index e90c338..fb1bdd7 100644 --- a/includes/class-debug.php +++ b/includes/class-debug.php @@ -82,25 +82,13 @@ public static function read( $limit = 1000 ) { return new \WP_Error( 'not_readable', __( 'Error: Unable to read the log file.', 'aspireupdate' ) ); } - $file_content = $wp_filesystem->get_contents_array( $file_path ); - $content = ''; - $index = 0; - foreach ( $file_content as $file_content_lines ) { - if ( ( $index < $limit ) ) { - $content .= $file_content_lines . PHP_EOL; - ++$index; - } - } - if ( '' === trim( $content ) ) { - $content = esc_html__( '*****Log file is empty.*****', 'aspireupdate' ); - } elseif ( $limit < count( $file_content ) ) { - $content .= PHP_EOL . sprintf( - /* translators: 1: The number of lines at which the content was truncated. */ - esc_html__( '*****Log truncated at %s lines.*****', 'aspireupdate' ), - $limit - ); + $file_content = $wp_filesystem->get_contents_array( $file_path, $limit, true ); + + if ( ( false === $file_content ) || ( 0 === count( $file_content ) ) ) { + $file_content = [ esc_html__( '*****Log file is empty.*****', 'aspireupdate' ) ]; } - return $content; + + return $file_content; } /** diff --git a/includes/class-filesystem-direct.php b/includes/class-filesystem-direct.php index 61c9cb8..0d74668 100644 --- a/includes/class-filesystem-direct.php +++ b/includes/class-filesystem-direct.php @@ -11,6 +11,63 @@ * The Class for WordPress Direct Filesystem with optimized read and write routines. */ class Filesystem_Direct extends \WP_Filesystem_Direct { + + /** + * Reads entire file into an array with options for limiting the number of lines and direction from the the lines are counted. + * + * @since 2.5.0 + * + * @param string $file Path to the file. + * @param int $number_of_lines The number of lines to read. Default is -1 (read all lines). + * @param bool $count_bottom_to_up Count the lines from the bottom up. Default is false (count from top to bottom). + * + * @return array|false File contents in an array on success, false on failure. + */ + public function get_contents_array( $file, $number_of_lines = -1, $count_bottom_to_up = false ) { + if ( ! $this->exists( $file ) ) { + return false; + } + + if ( -1 === $number_of_lines ) { + return @file( $file ); + } + + // phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_fopen + /** + * Extending WP_Filesystem methods for efficiency. This is a valid use case. + */ + $handle = @fopen( $file, 'r' ); + // phpcs:enable + if ( ! $handle ) { + return false; + } + + $lines = []; + $line_count = 0; + + while ( ( $line = fgets( $handle ) ) !== false ) { + $lines[] = rtrim( $line, "\r\n" ); + ++$line_count; + + if ( $count_bottom_to_up ) { + if ( $number_of_lines > 0 && $line_count > $number_of_lines ) { + array_shift( $lines ); + } + } elseif ( $number_of_lines > 0 && $line_count >= $number_of_lines ) { + break; + } + } + + // phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_fclose + /** + * Extending WP_Filesystem methods for efficiency. This is a valid use case. + */ + fclose( $handle ); + // phpcs:enable + + return $lines; + } + /** * Write contents to a file with additional modes. * @@ -30,21 +87,33 @@ public function put_contents( $file, $contents, $mode = false, $write_mode = 'w' if ( ! in_array( $write_mode, $valid_write_modes, true ) ) { return false; } - $fp = @fopen( $file, $write_mode ); + // phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_fopen + /** + * Extending WP_Filesystem methods for efficiency. This is a valid use case. + */ + $handle = @fopen( $file, $write_mode ); + // phpcs:enable - if ( ! $fp ) { + if ( ! $handle ) { return false; } mbstring_binary_safe_encoding(); - $data_length = strlen( $contents ); - - $bytes_written = fwrite( $fp, $contents ); - + // phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_fwrite + /** + * Extending WP_Filesystem methods for efficiency. This is a valid use case. + */ + $bytes_written = fwrite( $handle, $contents ); + // phpcs:enable reset_mbstring_encoding(); - fclose( $fp ); + // phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_fclose + /** + * Extending WP_Filesystem methods for efficiency. This is a valid use case. + */ + fclose( $handle ); + // phpcs:enable if ( $data_length !== $bytes_written ) { return false; From 35491189fcbb98f318ca1d6af43c3bc3a74ceaa2 Mon Sep 17 00:00:00 2001 From: Namith Jawahar <48271037+namithj@users.noreply.github.com> Date: Wed, 4 Dec 2024 20:26:59 +0530 Subject: [PATCH 04/27] PHP Doc Alignment PHP Doc Alignment --- includes/class-filesystem-direct.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/includes/class-filesystem-direct.php b/includes/class-filesystem-direct.php index 0d74668..78d0e10 100644 --- a/includes/class-filesystem-direct.php +++ b/includes/class-filesystem-direct.php @@ -71,11 +71,11 @@ public function get_contents_array( $file, $number_of_lines = -1, $count_bottom_ /** * Write contents to a file with additional modes. * - * @param string $file The path to the file. - * @param string $contents The content to write. + * @param string $file The path to the file. + * @param string $contents The content to write. * @param int|false $mode Optional. The file permissions as octal number, usually 0644. * Default false. - * @param string $write_mode The write mode: + * @param string $write_mode The write mode: * 'w' - Overwrite the file (default). * 'a' - Append to the file. * 'x' - Create a new file and write, fail if the file exists. From 4819d5965a69a8fe5716b0b53f4197ef95e85b76 Mon Sep 17 00:00:00 2001 From: Namith Jawahar <48271037+namithj@users.noreply.github.com> Date: Fri, 20 Dec 2024 23:16:55 +0530 Subject: [PATCH 05/27] Code review suggestions Code review suggestions --- includes/class-filesystem-direct.php | 40 +++++++--------------------- phpcs.xml.dist | 17 ++++++++++++ 2 files changed, 27 insertions(+), 30 deletions(-) diff --git a/includes/class-filesystem-direct.php b/includes/class-filesystem-direct.php index 78d0e10..4edfae7 100644 --- a/includes/class-filesystem-direct.php +++ b/includes/class-filesystem-direct.php @@ -19,11 +19,11 @@ class Filesystem_Direct extends \WP_Filesystem_Direct { * * @param string $file Path to the file. * @param int $number_of_lines The number of lines to read. Default is -1 (read all lines). - * @param bool $count_bottom_to_up Count the lines from the bottom up. Default is false (count from top to bottom). + * @param bool $count_bottom_to_top Count the lines from the bottom up. Default is false (count from top to bottom). * * @return array|false File contents in an array on success, false on failure. */ - public function get_contents_array( $file, $number_of_lines = -1, $count_bottom_to_up = false ) { + public function get_contents_array( $file, $number_of_lines = -1, $count_bottom_to_top = false ) { if ( ! $this->exists( $file ) ) { return false; } @@ -32,12 +32,7 @@ public function get_contents_array( $file, $number_of_lines = -1, $count_bottom_ return @file( $file ); } - // phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_fopen - /** - * Extending WP_Filesystem methods for efficiency. This is a valid use case. - */ $handle = @fopen( $file, 'r' ); - // phpcs:enable if ( ! $handle ) { return false; } @@ -45,11 +40,15 @@ public function get_contents_array( $file, $number_of_lines = -1, $count_bottom_ $lines = []; $line_count = 0; + // phpcs:disable Generic.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition + /** + * This is a valid and intentional use. + */ while ( ( $line = fgets( $handle ) ) !== false ) { $lines[] = rtrim( $line, "\r\n" ); ++$line_count; - if ( $count_bottom_to_up ) { + if ( $count_bottom_to_top ) { if ( $number_of_lines > 0 && $line_count > $number_of_lines ) { array_shift( $lines ); } @@ -57,13 +56,9 @@ public function get_contents_array( $file, $number_of_lines = -1, $count_bottom_ break; } } + // phpcs:enable - // phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_fclose - /** - * Extending WP_Filesystem methods for efficiency. This is a valid use case. - */ fclose( $handle ); - // phpcs:enable return $lines; } @@ -87,33 +82,18 @@ public function put_contents( $file, $contents, $mode = false, $write_mode = 'w' if ( ! in_array( $write_mode, $valid_write_modes, true ) ) { return false; } - // phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_fopen - /** - * Extending WP_Filesystem methods for efficiency. This is a valid use case. - */ - $handle = @fopen( $file, $write_mode ); - // phpcs:enable + $handle = @fopen( $file, $write_mode ); if ( ! $handle ) { return false; } mbstring_binary_safe_encoding(); - $data_length = strlen( $contents ); - // phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_fwrite - /** - * Extending WP_Filesystem methods for efficiency. This is a valid use case. - */ + $data_length = strlen( $contents ); $bytes_written = fwrite( $handle, $contents ); - // phpcs:enable reset_mbstring_encoding(); - // phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_fclose - /** - * Extending WP_Filesystem methods for efficiency. This is a valid use case. - */ fclose( $handle ); - // phpcs:enable if ( $data_length !== $bytes_written ) { return false; diff --git a/phpcs.xml.dist b/phpcs.xml.dist index af33bc2..a89b952 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -58,4 +58,21 @@ + + + + includes/class-filesystem-direct\.php + + + + includes/class-filesystem-direct\.php + + + + includes/class-filesystem-direct\.php + + + + includes/class-filesystem-direct\.php + From a91bae8bdeb8d3d775c0e7b7d846c5f10279b523 Mon Sep 17 00:00:00 2001 From: Namith Jawahar <48271037+namithj@users.noreply.github.com> Date: Mon, 30 Dec 2024 13:11:11 +0530 Subject: [PATCH 06/27] Update includes/class-filesystem-direct.php Co-authored-by: Colin Stewart <79332690+costdev@users.noreply.github.com> Signed-off-by: Namith Jawahar <48271037+namithj@users.noreply.github.com> --- includes/class-filesystem-direct.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/includes/class-filesystem-direct.php b/includes/class-filesystem-direct.php index 4edfae7..a6b6da9 100644 --- a/includes/class-filesystem-direct.php +++ b/includes/class-filesystem-direct.php @@ -15,8 +15,6 @@ class Filesystem_Direct extends \WP_Filesystem_Direct { /** * Reads entire file into an array with options for limiting the number of lines and direction from the the lines are counted. * - * @since 2.5.0 - * * @param string $file Path to the file. * @param int $number_of_lines The number of lines to read. Default is -1 (read all lines). * @param bool $count_bottom_to_top Count the lines from the bottom up. Default is false (count from top to bottom). From 911864eec4ad75c922acd0ce1e036384125a7401 Mon Sep 17 00:00:00 2001 From: Namith Jawahar <48271037+namithj@users.noreply.github.com> Date: Mon, 30 Dec 2024 13:11:19 +0530 Subject: [PATCH 07/27] Update includes/class-debug.php Co-authored-by: Colin Stewart <79332690+costdev@users.noreply.github.com> Signed-off-by: Namith Jawahar <48271037+namithj@users.noreply.github.com> --- includes/class-debug.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-debug.php b/includes/class-debug.php index e2f9114..6fe5fc7 100644 --- a/includes/class-debug.php +++ b/includes/class-debug.php @@ -31,7 +31,7 @@ private static function get_file_path() { /** * Initializes the WordPress Filesystem. * - * @return WP_Filesystem_Direct|false The filesystem object or false on failure. + * @return Filesystem_Direct The filesystem object. */ private static function init_filesystem() { require_once ABSPATH . 'wp-admin/includes/file.php'; From 4752f2724f3cbb5d0264d479ef91eadc09dd4c39 Mon Sep 17 00:00:00 2001 From: Namith Jawahar <48271037+namithj@users.noreply.github.com> Date: Mon, 30 Dec 2024 13:28:48 +0530 Subject: [PATCH 08/27] Singleton Filesystem Object Singleton Filesystem Object --- includes/class-debug.php | 87 ++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 56 deletions(-) diff --git a/includes/class-debug.php b/includes/class-debug.php index 6fe5fc7..c7c679e 100644 --- a/includes/class-debug.php +++ b/includes/class-debug.php @@ -19,6 +19,13 @@ class Debug { */ private static $log_file = 'debug-aspire-update.log'; + /** + * The filesystem. + * + * @var Filesystem_Direct + */ + private static $filesystem; + /** * Get the Log file path. * @@ -34,38 +41,14 @@ private static function get_file_path() { * @return Filesystem_Direct The filesystem object. */ private static function init_filesystem() { - require_once ABSPATH . 'wp-admin/includes/file.php'; - require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php'; - require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php'; - WP_Filesystem(); - return new Filesystem_Direct( false ); - } - - /** - * Checks the filesystem status and logs error to debug log. - * - * @param WP_Filesystem_Direct $wp_filesystem The filesystem object. - * - * @return boolean true on success and false on failure. - */ - private static function verify_filesystem( $wp_filesystem ) { - if ( ! $wp_filesystem ) { - if ( - defined( 'WP_DEBUG' ) && - ( true === WP_DEBUG ) && - defined( 'WP_DEBUG_LOG' ) && - ( true === WP_DEBUG_LOG ) - ) { - // phpcs:disable WordPress.PHP.DevelopmentFunctions - /** - * Log error in file write fails only if debug is set to true. This is a valid use case. - */ - error_log( 'AspireUpdate - Could not open or write to the file system. Check file system permissions to debug log directory.' ); // @codeCoverageIgnore - // phpcs:enable - } - return false; + if ( ! self::$filesystem instanceof Filesystem_Direct ) { + require_once ABSPATH . 'wp-admin/includes/file.php'; + require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php'; + require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php'; + WP_Filesystem(); + self::$filesystem = new Filesystem_Direct( false ); } - return true; + return self::$filesystem; } /** @@ -73,14 +56,11 @@ private static function verify_filesystem( $wp_filesystem ) { * * @param integer $limit Max no of lines to return. Defaults to a 1000 lines. * - * @return string|WP_Error The File content truncate upto the number of lines set in the limit parameter. + * @return array|WP_Error An array of lines in the file, limited to $limit, or a WP_Error object on failure. */ public static function read( $limit = 1000 ) { $wp_filesystem = self::init_filesystem(); $file_path = self::get_file_path(); - if ( ! self::verify_filesystem( $wp_filesystem ) || ! $wp_filesystem->exists( $file_path ) || ! $wp_filesystem->is_readable( $file_path ) ) { - return new \WP_Error( 'not_readable', __( 'Error: Unable to read the log file.', 'aspireupdate' ) ); - } $file_content = $wp_filesystem->get_contents_array( $file_path, $limit, true ); @@ -99,9 +79,6 @@ public static function read( $limit = 1000 ) { public static function clear() { $wp_filesystem = self::init_filesystem(); $file_path = self::get_file_path(); - if ( ! self::verify_filesystem( $wp_filesystem ) || ! $wp_filesystem->exists( $file_path ) || ! $wp_filesystem->is_writable( $file_path ) ) { - return new \WP_Error( 'not_accessible', __( 'Error: Unable to access the log file.', 'aspireupdate' ) ); - } $wp_filesystem->put_contents( $file_path, @@ -118,24 +95,22 @@ public static function clear() { * @param string $type The log level ('string', 'request', 'response'). */ public static function log( $message, $type = 'string' ) { - $wp_filesystem = self::init_filesystem(); - if ( self::verify_filesystem( $wp_filesystem ) ) { - $timestamp = gmdate( 'Y-m-d H:i:s' ); - $formatted_message = sprintf( - '[%s] [%s]: %s', - $timestamp, - strtoupper( $type ), - self::format_message( $message ) - ) . PHP_EOL; - - $file_path = self::get_file_path(); - $wp_filesystem->put_contents( - $file_path, - $formatted_message, - FS_CHMOD_FILE, - 'a' - ); - } + $wp_filesystem = self::init_filesystem(); + $timestamp = gmdate( 'Y-m-d H:i:s' ); + $formatted_message = sprintf( + '[%s] [%s]: %s', + $timestamp, + strtoupper( $type ), + self::format_message( $message ) + ) . PHP_EOL; + + $file_path = self::get_file_path(); + $wp_filesystem->put_contents( + $file_path, + $formatted_message, + FS_CHMOD_FILE, + 'a' + ); } /** From 2ef854026f40b894c5fd3a51c9a26006091b7595 Mon Sep 17 00:00:00 2001 From: Namith Jawahar <48271037+namithj@users.noreply.github.com> Date: Sat, 4 Jan 2025 12:59:31 +0530 Subject: [PATCH 09/27] Add File Exists and readable or writable checks Add File Exists and readable or writable checks --- includes/class-debug.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/includes/class-debug.php b/includes/class-debug.php index c7c679e..3977a3e 100644 --- a/includes/class-debug.php +++ b/includes/class-debug.php @@ -62,6 +62,10 @@ public static function read( $limit = 1000 ) { $wp_filesystem = self::init_filesystem(); $file_path = self::get_file_path(); + if ( ! $wp_filesystem->exists( $file_path ) || ! $wp_filesystem->is_readable( $file_path ) ) { + return new \WP_Error( 'not_readable', __( 'Error: Unable to read the log file.', 'aspireupdate' ) ); + } + $file_content = $wp_filesystem->get_contents_array( $file_path, $limit, true ); if ( ( false === $file_content ) || ( 0 === count( $file_content ) ) ) { @@ -80,6 +84,10 @@ public static function clear() { $wp_filesystem = self::init_filesystem(); $file_path = self::get_file_path(); + if ( ! $wp_filesystem->exists( $file_path ) || ! $wp_filesystem->is_writable( $file_path ) ) { + return new \WP_Error( 'not_accessible', __( 'Error: Unable to access the log file.', 'aspireupdate' ) ); + } + $wp_filesystem->put_contents( $file_path, '', From f6c47e3ebabc9b3edebee1ab7cd6368f8fe68e9b Mon Sep 17 00:00:00 2001 From: costdev Date: Wed, 8 Jan 2025 14:39:20 +0000 Subject: [PATCH 10/27] Tests: Extend `Filesystem_Direct` in `AP_FakeFilesystem`. --- tests/phpunit/includes/class-ap-fake-filesystem.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/phpunit/includes/class-ap-fake-filesystem.php b/tests/phpunit/includes/class-ap-fake-filesystem.php index d2de759..0f09ab8 100644 --- a/tests/phpunit/includes/class-ap-fake-filesystem.php +++ b/tests/phpunit/includes/class-ap-fake-filesystem.php @@ -3,7 +3,7 @@ require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php'; require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php'; -class AP_FakeFilesystem extends WP_Filesystem_Direct { +class AP_FakeFilesystem extends AspireUpdate\Filesystem_Direct { /** * Whether paths should exist. * From 6f2b1f93ff41ea98a1611ce8852b463d06996ef4 Mon Sep 17 00:00:00 2001 From: costdev Date: Wed, 8 Jan 2025 14:40:36 +0000 Subject: [PATCH 11/27] Tests: Account for the new static `$filesystem` property. --- tests/phpunit/includes/Debug_UnitTestCase.php | 30 +++++++++---------- tests/phpunit/tests/Debug/Debug_ClearTest.php | 6 ++-- tests/phpunit/tests/Debug/Debug_ReadTest.php | 4 +-- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/tests/phpunit/includes/Debug_UnitTestCase.php b/tests/phpunit/includes/Debug_UnitTestCase.php index 0058fd0..a5a9df0 100644 --- a/tests/phpunit/includes/Debug_UnitTestCase.php +++ b/tests/phpunit/includes/Debug_UnitTestCase.php @@ -13,19 +13,26 @@ abstract class Debug_UnitTestCase extends WP_UnitTestCase { protected static $log_file; /** - * Previously created filesystems. + * The class for use with the Reflection API. * - * @var array + * @var \ReflectionClass */ - protected static $filesystems = []; + protected static $reflection; /** - * The original value of $wp_filesystem before any tests run. + * The default filesystem. * - * @var WP_Filesystem_Base|null|false False if not already set. + * @var \AspireUpdate\Filesystem_Direct|null */ protected static $default_filesystem; + /** + * Previously created filesystems. + * + * @var array + */ + protected static $filesystems = []; + /** * Gets the log file's path, and deletes if it exists before any tests run. * Backs up the default filesystem. @@ -48,11 +55,8 @@ public static function set_up_before_class() { unlink( self::$log_file ); } - if ( isset( $GLOBALS['wp_filesystem'] ) ) { - self::$default_filesystem = $GLOBALS['wp_filesystem']; - } else { - self::$default_filesystem = false; - } + self::$reflection = new ReflectionClass( '\AspireUpdate\Debug' ); + self::$default_filesystem = self::$reflection->getStaticPropertyValue( 'filesystem' ); } /** @@ -83,11 +87,7 @@ public function tear_down() { unlink( self::$log_file ); } - if ( false === self::$default_filesystem ) { - unset( $GLOBALS['wp_filesystem'] ); - } else { - $GLOBALS['wp_filesystem'] = self::$default_filesystem; - } + self::$reflection->setStaticPropertyValue( 'filesystem', self::$default_filesystem ); parent::tear_down(); } diff --git a/tests/phpunit/tests/Debug/Debug_ClearTest.php b/tests/phpunit/tests/Debug/Debug_ClearTest.php index d6882c9..6c2cb91 100644 --- a/tests/phpunit/tests/Debug/Debug_ClearTest.php +++ b/tests/phpunit/tests/Debug/Debug_ClearTest.php @@ -58,10 +58,8 @@ public function test_should_return_wp_error_when_log_file_does_not_exist() { * @covers \AspireUpdate\Debug::get_file_path */ public function test_should_return_wp_error_when_log_file_is_not_writable() { - global $wp_filesystem; - - // Backup and replace the filesystem object. - $wp_filesystem = $this->get_fake_filesystem( true, true, false ); + // Replace the filesystem object. + self::$reflection->setStaticPropertyValue( 'filesystem', $this->get_fake_filesystem( true, true, false ) ); $actual = AspireUpdate\Debug::clear(); diff --git a/tests/phpunit/tests/Debug/Debug_ReadTest.php b/tests/phpunit/tests/Debug/Debug_ReadTest.php index 5f275e1..d9ca064 100644 --- a/tests/phpunit/tests/Debug/Debug_ReadTest.php +++ b/tests/phpunit/tests/Debug/Debug_ReadTest.php @@ -46,8 +46,8 @@ public function test_should_return_wp_error_when_log_file_is_not_readable() { // Create the log file. file_put_contents( self::$log_file, '' ); - // Backup and replace the filesystem object. - $wp_filesystem = $this->get_fake_filesystem( true, false, true ); + // Replace the filesystem object. + self::$reflection->setStaticPropertyValue( 'filesystem', $this->get_fake_filesystem( true, false, true ) ); $actual = AspireUpdate\Debug::read(); From 472f5d1fdffaff72e453dae400b32f5aa071a9a4 Mon Sep 17 00:00:00 2001 From: costdev Date: Wed, 8 Jan 2025 14:41:07 +0000 Subject: [PATCH 12/27] Tests: Make some test classes run in separate processes. --- tests/phpunit/tests/Debug/Debug_ClearTest.php | 6 ++++++ tests/phpunit/tests/Debug/Debug_LogTest.php | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/tests/phpunit/tests/Debug/Debug_ClearTest.php b/tests/phpunit/tests/Debug/Debug_ClearTest.php index 6c2cb91..30d8332 100644 --- a/tests/phpunit/tests/Debug/Debug_ClearTest.php +++ b/tests/phpunit/tests/Debug/Debug_ClearTest.php @@ -8,6 +8,12 @@ /** * Tests for Debug::clear() * + * These tests cause constants to be defined. + * They must run in separate processes and must not preserve global state. + * + * @runTestsInSeparateProcesses + * @preserveGlobalState disabled + * * @covers \AspireUpdate\Debug::clear */ class Debug_ClearTest extends Debug_UnitTestCase { diff --git a/tests/phpunit/tests/Debug/Debug_LogTest.php b/tests/phpunit/tests/Debug/Debug_LogTest.php index c95036f..3634305 100644 --- a/tests/phpunit/tests/Debug/Debug_LogTest.php +++ b/tests/phpunit/tests/Debug/Debug_LogTest.php @@ -8,6 +8,12 @@ /** * Tests for Debug::log() * + * These tests cause constants to be defined. + * They must run in separate processes and must not preserve global state. + * + * @runTestsInSeparateProcesses + * @preserveGlobalState disabled + * * @covers \AspireUpdate\Debug::log */ class Debug_LogTest extends Debug_UnitTestCase { From be13988335904c57e25c5fb85e09b3b30ef2e50f Mon Sep 17 00:00:00 2001 From: costdev Date: Wed, 8 Jan 2025 14:42:05 +0000 Subject: [PATCH 13/27] Tests: Remove coverage annotations for `Debug::verify_filesystem()`. This method no longer exists. --- tests/phpunit/tests/Debug/Debug_ClearTest.php | 3 --- tests/phpunit/tests/Debug/Debug_ReadTest.php | 3 --- 2 files changed, 6 deletions(-) diff --git a/tests/phpunit/tests/Debug/Debug_ClearTest.php b/tests/phpunit/tests/Debug/Debug_ClearTest.php index 30d8332..ed5aed6 100644 --- a/tests/phpunit/tests/Debug/Debug_ClearTest.php +++ b/tests/phpunit/tests/Debug/Debug_ClearTest.php @@ -21,7 +21,6 @@ class Debug_ClearTest extends Debug_UnitTestCase { * Test that a WP_Error object is returned when the filesystem isn't available. * * @covers \AspireUpdate\Debug::init_filesystem - * @covers \AspireUpdate\Debug::verify_filesystem */ public function test_should_return_wp_error_when_filesystem_is_not_available() { add_filter( 'filesystem_method', '__return_false' ); @@ -41,7 +40,6 @@ public function test_should_return_wp_error_when_filesystem_is_not_available() { * Test that a WP_Error object is returned when the log file doesn't exist. * * @covers \AspireUpdate\Debug::init_filesystem - * @covers \AspireUpdate\Debug::verify_filesystem * @covers \AspireUpdate\Debug::get_file_path */ public function test_should_return_wp_error_when_log_file_does_not_exist() { @@ -60,7 +58,6 @@ public function test_should_return_wp_error_when_log_file_does_not_exist() { * Test that a WP_Error object is returned when the log file isn't writable. * * @covers \AspireUpdate\Debug::init_filesystem - * @covers \AspireUpdate\Debug::verify_filesystem * @covers \AspireUpdate\Debug::get_file_path */ public function test_should_return_wp_error_when_log_file_is_not_writable() { diff --git a/tests/phpunit/tests/Debug/Debug_ReadTest.php b/tests/phpunit/tests/Debug/Debug_ReadTest.php index d9ca064..a2a4956 100644 --- a/tests/phpunit/tests/Debug/Debug_ReadTest.php +++ b/tests/phpunit/tests/Debug/Debug_ReadTest.php @@ -15,7 +15,6 @@ class Debug_ReadTest extends Debug_UnitTestCase { * Test that a WP_Error object is returned when the filesystem isn't available. * * @covers \AspireUpdate\Debug::init_filesystem - * @covers \AspireUpdate\Debug::verify_filesystem */ public function test_should_return_wp_error_when_filesystem_is_not_available() { add_filter( 'filesystem_method', '__return_false' ); @@ -26,7 +25,6 @@ public function test_should_return_wp_error_when_filesystem_is_not_available() { * Test that a WP_Error object is returned when the log file doesn't exist. * * @covers \AspireUpdate\Debug::init_filesystem - * @covers \AspireUpdate\Debug::verify_filesystem * @covers \AspireUpdate\Debug::get_file_path */ public function test_should_return_wp_error_when_log_file_does_not_exist() { @@ -37,7 +35,6 @@ public function test_should_return_wp_error_when_log_file_does_not_exist() { * Test that a WP_Error object is returned when the log file isn't readable. * * @covers \AspireUpdate\Debug::init_filesystem - * @covers \AspireUpdate\Debug::verify_filesystem * @covers \AspireUpdate\Debug::get_file_path */ public function test_should_return_wp_error_when_log_file_is_not_readable() { From 40933ba7e8efa795ba7bc05781bec34035e0f3e0 Mon Sep 17 00:00:00 2001 From: costdev Date: Wed, 8 Jan 2025 20:06:45 +0000 Subject: [PATCH 14/27] Tests: Remove references to the `$wp_filesystem` global. --- tests/phpunit/tests/Debug/Debug_ReadTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/phpunit/tests/Debug/Debug_ReadTest.php b/tests/phpunit/tests/Debug/Debug_ReadTest.php index a2a4956..8f1d735 100644 --- a/tests/phpunit/tests/Debug/Debug_ReadTest.php +++ b/tests/phpunit/tests/Debug/Debug_ReadTest.php @@ -38,8 +38,6 @@ public function test_should_return_wp_error_when_log_file_does_not_exist() { * @covers \AspireUpdate\Debug::get_file_path */ public function test_should_return_wp_error_when_log_file_is_not_readable() { - global $wp_filesystem; - // Create the log file. file_put_contents( self::$log_file, '' ); From 451214be547eff539033e4ea1f8ec538ae378ca2 Mon Sep 17 00:00:00 2001 From: costdev Date: Wed, 8 Jan 2025 20:09:07 +0000 Subject: [PATCH 15/27] Tests: Remove testing for an unavailable filesystem. --- tests/phpunit/tests/Debug/Debug_LogTest.php | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/tests/phpunit/tests/Debug/Debug_LogTest.php b/tests/phpunit/tests/Debug/Debug_LogTest.php index 3634305..86d329f 100644 --- a/tests/phpunit/tests/Debug/Debug_LogTest.php +++ b/tests/phpunit/tests/Debug/Debug_LogTest.php @@ -17,22 +17,6 @@ * @covers \AspireUpdate\Debug::log */ class Debug_LogTest extends Debug_UnitTestCase { - /** - * Test that nothing is written to the log file when the filesystem isn't available. - * - * @covers \AspireUpdate\Debug::init_filesystem - * @covers \AspireUpdate\Debug::verify_filesystem - */ - public function test_should_not_write_to_log_file_when_filesystem_is_not_available() { - add_filter( 'filesystem_method', '__return_false' ); - - AspireUpdate\Debug::log( 'Test log message.' ); - - $this->assertFileDoesNotExist( - self::$log_file, - 'The log file was created.' - ); - } /** * Test that the log file is created when it doesn't already exist. From a4bb48e91a99d18014c01c9ddcd863a1a0c575ed Mon Sep 17 00:00:00 2001 From: costdev Date: Wed, 8 Jan 2025 20:10:25 +0000 Subject: [PATCH 16/27] Tests: Account for log lines being retrieved as an array. --- tests/phpunit/tests/Debug/Debug_ReadTest.php | 96 ++++++++++++++++---- 1 file changed, 78 insertions(+), 18 deletions(-) diff --git a/tests/phpunit/tests/Debug/Debug_ReadTest.php b/tests/phpunit/tests/Debug/Debug_ReadTest.php index 8f1d735..fe98088 100644 --- a/tests/phpunit/tests/Debug/Debug_ReadTest.php +++ b/tests/phpunit/tests/Debug/Debug_ReadTest.php @@ -56,14 +56,26 @@ public function test_should_return_an_empty_log_message_when_log_file_is_empty() file_put_contents( self::$log_file, '' ); $actual = AspireUpdate\Debug::read(); - $this->assertIsString( + $this->assertIsArray( + $actual, + 'An array was not returned.' + ); + + $this->assertCount( + 1, $actual, - 'A string was not returned.' + 'An incorrect number of entries was returned.' + ); + + $entry = reset( $actual ); + $this->assertIsString( + $entry, + 'The entry is not a string.' ); $this->assertStringContainsString( 'Log file is empty', - $actual, + $entry, 'The empty log file message was not returned.' ); } @@ -76,14 +88,26 @@ public function test_should_return_an_empty_log_message_when_log_file_only_has_e file_put_contents( self::$log_file, " \n\r\t\v\x00" ); $actual = AspireUpdate\Debug::read(); - $this->assertIsString( + $this->assertIsArray( $actual, - 'A string was not returned.' + 'An array was not returned.' + ); + + $this->assertCount( + 1, + $actual, + 'An incorrect number of entries was returned.' + ); + + $entry = reset( $actual ); + $this->assertIsString( + $entry, + 'The entry is not a string.' ); $this->assertStringContainsString( 'Log file is empty', - $actual, + $entry, 'The empty log file message was not returned.' ); } @@ -96,14 +120,26 @@ public function test_should_not_return_an_empty_log_message_when_log_file_has_co file_put_contents( self::$log_file, 'Some contents' ); $actual = AspireUpdate\Debug::read(); - $this->assertIsString( + $this->assertIsArray( $actual, - 'A string was not returned.' + 'An array was not returned.' + ); + + $this->assertCount( + 1, + $actual, + 'An incorrect number of entries was returned.' + ); + + $entry = reset( $actual ); + $this->assertIsString( + $entry, + 'The entry is not a string.' ); $this->assertStringNotContainsString( 'Log file is empty', - $actual, + $entry, 'The empty log file message was returned.' ); } @@ -120,14 +156,26 @@ public function test_should_add_a_truncation_message_when_log_file_has_more_line $actual = AspireUpdate\Debug::read( 2 ); - $this->assertIsString( + $this->assertIsArray( $actual, - 'A string was not returned.' + 'An array was not returned.' + ); + + $this->assertCount( + 2, + $actual, + 'An incorrect number of entries was returned.' + ); + + $entry = reset( $actual ); + $this->assertIsString( + $entry, + 'The entry is not a string.' ); $this->assertStringContainsString( 'Log truncated', - $actual, + $entry, 'The truncation message was not returned.' ); } @@ -144,12 +192,18 @@ public function test_should_not_add_a_truncation_message_when_log_file_has_the_s $actual = AspireUpdate\Debug::read( 3 ); - $this->assertIsString( + $this->assertIsArray( $actual, - 'A string was not returned.' + 'An array was not returned.' ); - $this->assertStringNotContainsString( + $this->assertCount( + 3, + $actual, + 'An incorrect number of entries was returned.' + ); + + $this->assertNotContains( 'Log truncated', $actual, 'The truncation message was added.' @@ -168,12 +222,18 @@ public function test_should_not_add_a_truncation_message_when_log_file_has_fewer $actual = AspireUpdate\Debug::read( 4 ); - $this->assertIsString( + $this->assertIsArray( $actual, - 'A string was not returned.' + 'An array was not returned.' ); - $this->assertStringNotContainsString( + $this->assertCount( + 3, + $actual, + 'An incorrect number of entries was returned.' + ); + + $this->assertNotContains( 'Log truncated', $actual, 'The truncation message was added.' From b8ac7b54fcc61e0daf0441cd02f0d22b6a8fede9 Mon Sep 17 00:00:00 2001 From: costdev Date: Wed, 8 Jan 2025 20:12:17 +0000 Subject: [PATCH 17/27] Tests: Refactor test that checks the order of messages. Previously, new messages were prepended to a log file. They are now appended to the log file. This updates the relevant test to address the new order and contents of the log file. --- tests/phpunit/tests/Debug/Debug_LogTest.php | 32 +++++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/tests/phpunit/tests/Debug/Debug_LogTest.php b/tests/phpunit/tests/Debug/Debug_LogTest.php index 86d329f..4876d20 100644 --- a/tests/phpunit/tests/Debug/Debug_LogTest.php +++ b/tests/phpunit/tests/Debug/Debug_LogTest.php @@ -65,27 +65,41 @@ public function test_should_add_message_to_log_file() { } /** - * Test that the message is prepended to an existing log file. + * Test that the message is appended to an existing log file. * * @covers \AspireUpdate\Debug::format_message */ - public function test_should_add_message_to_an_existing_log_file() { - $existing_content = 'An existing log file.'; - file_put_contents( self::$log_file, $existing_content ); + public function test_should_append_message_to_an_existing_log_file() { + $previous_message = "A previously logged message.\n"; + file_put_contents( self::$log_file, $previous_message ); - $message = 'Test log message.'; + $new_message = 'New log message.'; - AspireUpdate\Debug::log( $message ); + AspireUpdate\Debug::log( $new_message ); $this->assertFileExists( self::$log_file, 'The log file was not created.' ); + $actual = file_get_contents( self::$log_file ); + $this->assertStringContainsString( - "$message\n$existing_content", - file_get_contents( self::$log_file ), - 'The message was not prepended to the log file.' + $previous_message, + $actual, + 'The previous message does not exist.' + ); + + $this->assertStringContainsString( + $new_message, + $actual, + 'The new message does not exist.' + ); + + $this->assertLessThan( + strpos( $actual, $new_message ), + strpos( $actual, $previous_message ), + 'The new message was not appended to the log file.' ); } From bf6db5c8452777cf790e5f66e541aea1fd969cfb Mon Sep 17 00:00:00 2001 From: costdev Date: Wed, 8 Jan 2025 20:31:28 +0000 Subject: [PATCH 18/27] Tests: Remove truncation message assertions. --- tests/phpunit/tests/Debug/Debug_ReadTest.php | 24 -------------------- 1 file changed, 24 deletions(-) diff --git a/tests/phpunit/tests/Debug/Debug_ReadTest.php b/tests/phpunit/tests/Debug/Debug_ReadTest.php index fe98088..8d6f0b2 100644 --- a/tests/phpunit/tests/Debug/Debug_ReadTest.php +++ b/tests/phpunit/tests/Debug/Debug_ReadTest.php @@ -166,18 +166,6 @@ public function test_should_add_a_truncation_message_when_log_file_has_more_line $actual, 'An incorrect number of entries was returned.' ); - - $entry = reset( $actual ); - $this->assertIsString( - $entry, - 'The entry is not a string.' - ); - - $this->assertStringContainsString( - 'Log truncated', - $entry, - 'The truncation message was not returned.' - ); } /** @@ -202,12 +190,6 @@ public function test_should_not_add_a_truncation_message_when_log_file_has_the_s $actual, 'An incorrect number of entries was returned.' ); - - $this->assertNotContains( - 'Log truncated', - $actual, - 'The truncation message was added.' - ); } /** @@ -232,11 +214,5 @@ public function test_should_not_add_a_truncation_message_when_log_file_has_fewer $actual, 'An incorrect number of entries was returned.' ); - - $this->assertNotContains( - 'Log truncated', - $actual, - 'The truncation message was added.' - ); } } From 3aa921879cb368a3ffa1caab4949f8f1a16a108b Mon Sep 17 00:00:00 2001 From: costdev Date: Wed, 8 Jan 2025 21:01:47 +0000 Subject: [PATCH 19/27] Workflows: Install SVN before running tests. Subversion was removed from GitHub Actions' default packages for Ubuntu 24. The tests installer uses Subversion internally. This changes installs Subversion. --- .github/workflows/phpunit-tests.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/phpunit-tests.yml b/.github/workflows/phpunit-tests.yml index 9aabbb7..d433dd4 100644 --- a/.github/workflows/phpunit-tests.yml +++ b/.github/workflows/phpunit-tests.yml @@ -35,6 +35,9 @@ jobs: php-version: ${{ matrix.php-version }} tools: phpunit-polyfills:1.1 + - name: Install SVN + run: sudo apt install -y subversion + - name: Setup tests run: bash bin/install-wp-tests.sh wordpress_tests root root 127.0.0.1 latest true From 746f364ab1646c0d5bf775f60d522861c8c57292 Mon Sep 17 00:00:00 2001 From: costdev Date: Wed, 8 Jan 2025 21:05:34 +0000 Subject: [PATCH 20/27] Debug: Filter the contents array to check for an empty file. --- includes/class-debug.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-debug.php b/includes/class-debug.php index 3977a3e..cab628e 100644 --- a/includes/class-debug.php +++ b/includes/class-debug.php @@ -68,7 +68,7 @@ public static function read( $limit = 1000 ) { $file_content = $wp_filesystem->get_contents_array( $file_path, $limit, true ); - if ( ( false === $file_content ) || ( 0 === count( $file_content ) ) ) { + if ( ( false === $file_content ) || ( 0 === count( array_filter( $file_content ) ) ) ) { $file_content = [ esc_html__( '*****Log file is empty.*****', 'aspireupdate' ) ]; } From 7870801063eb2965ac0414c9b8a4374412a81eee Mon Sep 17 00:00:00 2001 From: costdev Date: Wed, 8 Jan 2025 21:05:55 +0000 Subject: [PATCH 21/27] Filesystem: Trim all space characters to check for empty lines. --- includes/class-filesystem-direct.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-filesystem-direct.php b/includes/class-filesystem-direct.php index a6b6da9..9f7e097 100644 --- a/includes/class-filesystem-direct.php +++ b/includes/class-filesystem-direct.php @@ -43,7 +43,7 @@ public function get_contents_array( $file, $number_of_lines = -1, $count_bottom_ * This is a valid and intentional use. */ while ( ( $line = fgets( $handle ) ) !== false ) { - $lines[] = rtrim( $line, "\r\n" ); + $lines[] = trim( $line ); ++$line_count; if ( $count_bottom_to_top ) { From 02d4f91b0fcf8828167ba3d00bd2d4695e6fb3a8 Mon Sep 17 00:00:00 2001 From: costdev Date: Fri, 10 Jan 2025 04:50:44 +0000 Subject: [PATCH 22/27] Tests: Remove unneeded tests. --- tests/phpunit/tests/Debug/Debug_ClearTest.php | 19 ------------------- tests/phpunit/tests/Debug/Debug_ReadTest.php | 10 ---------- 2 files changed, 29 deletions(-) diff --git a/tests/phpunit/tests/Debug/Debug_ClearTest.php b/tests/phpunit/tests/Debug/Debug_ClearTest.php index ed5aed6..7bd77b5 100644 --- a/tests/phpunit/tests/Debug/Debug_ClearTest.php +++ b/tests/phpunit/tests/Debug/Debug_ClearTest.php @@ -17,25 +17,6 @@ * @covers \AspireUpdate\Debug::clear */ class Debug_ClearTest extends Debug_UnitTestCase { - /** - * Test that a WP_Error object is returned when the filesystem isn't available. - * - * @covers \AspireUpdate\Debug::init_filesystem - */ - public function test_should_return_wp_error_when_filesystem_is_not_available() { - add_filter( 'filesystem_method', '__return_false' ); - - $this->assertWPError( - AspireUpdate\Debug::clear(), - 'A WP_Error object was not returned.' - ); - - $this->assertFileDoesNotExist( - self::$log_file, - 'The log file was created.' - ); - } - /** * Test that a WP_Error object is returned when the log file doesn't exist. * diff --git a/tests/phpunit/tests/Debug/Debug_ReadTest.php b/tests/phpunit/tests/Debug/Debug_ReadTest.php index 8d6f0b2..4f69853 100644 --- a/tests/phpunit/tests/Debug/Debug_ReadTest.php +++ b/tests/phpunit/tests/Debug/Debug_ReadTest.php @@ -11,16 +11,6 @@ * @covers \AspireUpdate\Debug::read */ class Debug_ReadTest extends Debug_UnitTestCase { - /** - * Test that a WP_Error object is returned when the filesystem isn't available. - * - * @covers \AspireUpdate\Debug::init_filesystem - */ - public function test_should_return_wp_error_when_filesystem_is_not_available() { - add_filter( 'filesystem_method', '__return_false' ); - $this->assertWPError( AspireUpdate\Debug::read() ); - } - /** * Test that a WP_Error object is returned when the log file doesn't exist. * From f0062befc2b954095e69e917592f8a5c6525b54a Mon Sep 17 00:00:00 2001 From: costdev Date: Fri, 10 Jan 2025 04:52:30 +0000 Subject: [PATCH 23/27] Tests: Ensure the file exists before testing its writability. --- tests/phpunit/tests/Debug/Debug_ClearTest.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/phpunit/tests/Debug/Debug_ClearTest.php b/tests/phpunit/tests/Debug/Debug_ClearTest.php index 7bd77b5..3bc1bba 100644 --- a/tests/phpunit/tests/Debug/Debug_ClearTest.php +++ b/tests/phpunit/tests/Debug/Debug_ClearTest.php @@ -42,6 +42,8 @@ public function test_should_return_wp_error_when_log_file_does_not_exist() { * @covers \AspireUpdate\Debug::get_file_path */ public function test_should_return_wp_error_when_log_file_is_not_writable() { + file_put_contents( self::$log_file, '' ); + // Replace the filesystem object. self::$reflection->setStaticPropertyValue( 'filesystem', $this->get_fake_filesystem( true, true, false ) ); @@ -51,11 +53,6 @@ public function test_should_return_wp_error_when_log_file_is_not_writable() { $actual, 'A WP_Error was not returned.' ); - - $this->assertFileDoesNotExist( - self::$log_file, - 'The log file was created.' - ); } /** From 6c7e8a233e1c2d7ac6e31606057ebd9992dca6a6 Mon Sep 17 00:00:00 2001 From: costdev Date: Fri, 10 Jan 2025 04:53:08 +0000 Subject: [PATCH 24/27] Tests: Make some test names more accurate. --- tests/phpunit/tests/Debug/Debug_ReadTest.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/phpunit/tests/Debug/Debug_ReadTest.php b/tests/phpunit/tests/Debug/Debug_ReadTest.php index 4f69853..591a354 100644 --- a/tests/phpunit/tests/Debug/Debug_ReadTest.php +++ b/tests/phpunit/tests/Debug/Debug_ReadTest.php @@ -135,10 +135,10 @@ public function test_should_not_return_an_empty_log_message_when_log_file_has_co } /** - * Test that a truncation message is added when the log file has more + * Test that truncation is performed when the log file has more * lines than requested. */ - public function test_should_add_a_truncation_message_when_log_file_has_more_lines_than_requested() { + public function test_should_add_truncate_when_log_file_has_more_lines_than_requested() { file_put_contents( self::$log_file, "First line\r\nSecond line\r\nThird line" @@ -159,10 +159,10 @@ public function test_should_add_a_truncation_message_when_log_file_has_more_line } /** - * Test that no truncation message is added when the log file has the same + * Test that the whole log is returned when the log file has the same * number of lines as requested. */ - public function test_should_not_add_a_truncation_message_when_log_file_has_the_same_number_of_lines_as_requested() { + public function test_should_return_whole_log_when_log_file_has_the_same_number_of_lines_as_requested() { file_put_contents( self::$log_file, "First line\r\nSecond line\r\nThird line" @@ -183,10 +183,10 @@ public function test_should_not_add_a_truncation_message_when_log_file_has_the_s } /** - * Test that no truncation message is added when the log file has fewer than + * Test that truncation is not performed when the log file has fewer than * lines than requested. */ - public function test_should_not_add_a_truncation_message_when_log_file_has_fewer_lines_than_requested() { + public function test_should_not_truncate_when_log_file_has_fewer_lines_than_requested() { file_put_contents( self::$log_file, "First line\r\nSecond line\r\nThird line" From 038c97c411f68d06b24ac1f9864addb673ea38a4 Mon Sep 17 00:00:00 2001 From: costdev Date: Fri, 10 Jan 2025 04:53:30 +0000 Subject: [PATCH 25/27] Tests: Add tests for `Filesystem_Direct::get_contents_array()`. --- .../FilesystemDirect/GetContentsArrayTest.php | 263 ++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 tests/phpunit/tests/FilesystemDirect/GetContentsArrayTest.php diff --git a/tests/phpunit/tests/FilesystemDirect/GetContentsArrayTest.php b/tests/phpunit/tests/FilesystemDirect/GetContentsArrayTest.php new file mode 100644 index 0000000..73ba579 --- /dev/null +++ b/tests/phpunit/tests/FilesystemDirect/GetContentsArrayTest.php @@ -0,0 +1,263 @@ +assertFalse( $filesystem->get_contents_array( 'non_existent_file.txt', 1 ) ); + } + + /** + * Test that false is returned when the file is not readable. + * + * This test fakes a true exists() result, despite the file not existing. + * + * Since the file doesn't exist, read checks should fail. + */ + public function test_should_return_false_when_the_file_cannot_be_read() { + $filesystem = new AP_FakeFilesystem( true, false, false ); + + $this->assertFalse( $filesystem->get_contents_array( self::$test_file, 1 ) ); + } + + /** + * Test that the entire log is returned if the number of requested lines is -1. + */ + public function test_should_return_the_entire_log_if_number_of_requested_lines_is_minus_one() { + $filesystem = new AP_FakeFilesystem( true, true, true ); + $contents = 'First line' . PHP_EOL . 'Second line' . PHP_EOL . 'Third line'; + file_put_contents( self::$test_file, $contents ); + + $actual = $filesystem->get_contents_array( self::$test_file, -1 ); + + $this->assertIsArray( + $actual, + 'The contents were not read into an array.' + ); + + $this->assertSame( + [ + 'First line' . PHP_EOL, + 'Second line' . PHP_EOL, + 'Third line', + ], + $actual, + 'The entire log was not read.' + ); + } + + /** + * Test that the lines returned are from the bottom of the log file up when requested. + * + * @dataProvider data_count_bottom_to_top_enabled + * + * @param mixed $count_bottom_to_top Whether to count the lines from the bottom up. + */ + public function test_should_read_from_bottom_to_top_when_requested( $count_bottom_to_top ) { + $filesystem = new AP_FakeFilesystem( true, true, true ); + $contents = 'First line' . PHP_EOL . 'Second line' . PHP_EOL . 'Third line'; + file_put_contents( self::$test_file, $contents ); + + $actual = $filesystem->get_contents_array( self::$test_file, 2, $count_bottom_to_top ); + + $this->assertIsArray( + $actual, + 'The contents were not read into an array.' + ); + + $this->assertCount( + 2, + $actual, + 'The number of lines read does not match the requested number of lines.' + ); + + $this->assertSame( + [ + 'Second line', + 'Third line', + ], + $actual, + 'The lines were not read from the bottom up.' + ); + } + + /** + * Test that the whole log is returned when the log file has the same + * number of lines as requested. + * + * @dataProvider data_count_bottom_to_top_enabled + * @dataProvider data_count_bottom_to_top_disabled + * + * @param mixed $count_bottom_to_top Whether to count the lines from the bottom up. + */ + public function test_should_return_whole_log_when_log_file_has_the_same_number_of_lines_as_requested( $count_bottom_to_top ) { + $filesystem = new AP_FakeFilesystem( true, true, true ); + $contents = 'First line' . PHP_EOL . 'Second line' . PHP_EOL . 'Third line'; + file_put_contents( self::$test_file, $contents ); + + $actual = $filesystem->get_contents_array( self::$test_file, 3, $count_bottom_to_top ); + + $this->assertIsArray( + $actual, + 'The contents were not read into an array.' + ); + + $this->assertSame( + [ + 'First line', + 'Second line', + 'Third line', + ], + $actual, + 'The entire log was not read.' + ); + } + + /** + * Test that only the requested number of lines is read. + * + * @dataProvider data_count_bottom_to_top_enabled + * @dataProvider data_count_bottom_to_top_disabled + * + * @param mixed $count_bottom_to_top Whether to count the lines from the bottom up. + */ + public function test_should_only_read_the_requested_number_of_lines( $count_bottom_to_top ) { + $filesystem = new AP_FakeFilesystem( true, true, true ); + $contents = 'First line' . PHP_EOL . 'Second line' . PHP_EOL . 'Third line'; + file_put_contents( self::$test_file, $contents ); + + $actual = $filesystem->get_contents_array( self::$test_file, 2, $count_bottom_to_top ); + + $this->assertIsArray( + $actual, + 'The contents were not read into an array.' + ); + + if ( $count_bottom_to_top ) { + $expected = [ + 'Second line', + 'Third line', + ]; + } else { + $expected = [ + 'First line', + 'Second line', + ]; + } + + $this->assertSame( + $expected, + $actual, + 'The lines read do not match the expected lines.' + ); + } + + /** + * Data provider. + * + * @return array[] + */ + public function data_count_bottom_to_top_enabled() { + return [ + '$count_bottom_to_top as (bool) true' => [ + 'count_bottom_to_top' => true, + ], + '$count_bottom_to_top as (int) 1' => [ + 'count_bottom_to_top' => 1, + ], + '$count_bottom_to_top as (float) 1.0' => [ + 'count_bottom_to_top' => 1.0, + ], + '$count_bottom_to_top as (float) -1.0' => [ + 'count_bottom_to_top' => -1.0, + ], + '$count_bottom_to_top as (string) "1"' => [ + 'count_bottom_to_top' => '1', + ], + '$count_bottom_to_top as a string with spaces' => [ + 'count_bottom_to_top' => " \t\r\n", + ], + '$count_bottom_to_top as a non-empty array' => [ + 'count_bottom_to_top' => [ 'not empty' ], + ], + '$count_bottom_to_top as an object' => [ + 'count_bottom_to_top' => new stdClass(), + ], + '$count_bottom_to_top as NAN' => [ + 'count_bottom_to_top' => NAN, + ], + '$count_bottom_to_top as INF' => [ + 'count_bottom_to_top' => INF, + ], + ]; + } + + /** + * Data provider. + * + * @return array[] + */ + public function data_count_bottom_to_top_disabled() { + return [ + '$count_bottom_to_top as (bool) false' => [ + 'count_bottom_to_top' => false, + ], + '$count_bottom_to_top as (int) 0' => [ + 'count_bottom_to_top' => 0, + ], + '$count_bottom_to_top as (string) "0"' => [ + 'count_bottom_to_top' => '0', + ], + '$count_bottom_to_top as (float) 0.0' => [ + 'count_bottom_to_top' => 0.0, + ], + '$count_bottom_to_top as (float) -0.0' => [ + 'count_bottom_to_top' => -0.0, + ], + '$count_bottom_to_top as an empty string' => [ + 'count_bottom_to_top' => '', + ], + '$count_bottom_to_top as an empty array' => [ + 'count_bottom_to_top' => [], + ], + '$count_bottom_to_top as NULL' => [ + 'count_bottom_to_top' => null, + ], + ]; + } +} From cf34bbd1782529a62314197f00e70443e18bdb84 Mon Sep 17 00:00:00 2001 From: costdev Date: Fri, 10 Jan 2025 04:53:37 +0000 Subject: [PATCH 26/27] Tests: Add tests for `Filesystem_Direct::put_contents()`. --- .../FilesystemDirect/PutContentsTest.php | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 tests/phpunit/tests/FilesystemDirect/PutContentsTest.php diff --git a/tests/phpunit/tests/FilesystemDirect/PutContentsTest.php b/tests/phpunit/tests/FilesystemDirect/PutContentsTest.php new file mode 100644 index 0000000..5c6e50d --- /dev/null +++ b/tests/phpunit/tests/FilesystemDirect/PutContentsTest.php @@ -0,0 +1,131 @@ +assertFalse( $filesystem->put_contents( self::$test_file, '', false, 'g' ) ); + } + + /** + * Test that the log file is created when it doesn't already exist. + * + * This test causes constants to be defined. + * It must run in a separate process and must not preserve global state. + * + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function test_should_create_log_file_if_it_does_not_already_exist() { + define( 'FS_CHMOD_FILE', 0644 ); + + $filesystem = new AP_FakeFilesystem( false, true, true ); + $filesystem->put_contents( self::$test_file, '', false, 'w' ); + + $this->assertFileExists( + self::$test_file, + 'The log file was not created.' + ); + } + + /** + * Test that false is returned when the path is a directory. + */ + public function test_should_return_false_when_the_path_is_a_directory() { + $test_dir = '/tmp/aspireupdate-putcontents-test-dir'; + $filesystem = new AP_FakeFilesystem( false, true, true ); + mkdir( $test_dir ); + + $this->assertDirectoryExists( + $test_dir, + 'The test directory was not created.' + ); + + $actual = $filesystem->put_contents( $test_dir, '', false, 'w' ); + rmdir( $test_dir ); + + $this->assertFalse( + $actual, + 'Passing a directory path did not return false.' + ); + } + + /** + * Test that content is appended to the file when the write mode is 'a'. + * + * This test causes constants to be defined. + * It must run in a separate process and must not preserve global state. + * + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function test_should_append_to_file_when_the_write_mode_is_a() { + define( 'FS_CHMOD_FILE', 0644 ); + + $existing_content = 'This is existing content.'; + $new_content = PHP_EOL . 'This is new content'; + file_put_contents( self::$test_file, $existing_content ); + + $this->assertFileExists( + self::$test_file, + 'The file was not created before testing.' + ); + + $this->assertSame( + $existing_content, + file_get_contents( self::$test_file ), + 'The contents of the test file are not correct before testing.' + ); + + $filesystem = new AP_FakeFilesystem( true, true, true ); + $filesystem->put_contents( self::$test_file, $new_content, false, 'a' ); + $contents = file_get_contents( self::$test_file ); + + $this->assertSame( + $contents, + $existing_content . $new_content, + 'The contents of the file are unexpected.' + ); + + $this->assertLessThan( + strpos( $contents, $new_content ), + strpos( $contents, $existing_content ), + 'The new content was not appended to the file.' + ); + } +} From 500676eacbbc8e4c3413c1790aaaddf7c93b9e89 Mon Sep 17 00:00:00 2001 From: costdev Date: Fri, 10 Jan 2025 04:57:49 +0000 Subject: [PATCH 27/27] Coding Standards: Add `mkdir()` and `rmdir()` exceptions for test files. --- phpcs.xml.dist | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/phpcs.xml.dist b/phpcs.xml.dist index 4a54373..75aaf4e 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -75,7 +75,7 @@ includes/class-filesystem-direct\.php - + /tests/phpunit/* @@ -99,4 +99,10 @@ /tests/phpunit/* + + /tests/phpunit/* + + + /tests/phpunit/* +