From 775ce380380428f1dbaacf440bd92dcd5923b86c Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Sat, 16 Mar 2024 17:16:31 +0100 Subject: [PATCH 01/30] environment for multi-php --- .env.default | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.env.default b/.env.default index 84de84c..12cf091 100644 --- a/.env.default +++ b/.env.default @@ -38,6 +38,9 @@ export WPT_TABLE_PREFIX=${WPT_TABLE_PREFIX-wptests_} # (Optionally) define the PHP executable to be called export WPT_PHP_EXECUTABLE=${WPT_PHP_EXECUTABLE-php} +# (Optionally) array of versions (like: 8.0+/bin/php8.0,8.1+/bin/php8.1) +export WPT_PHP_EXECUTABLE_MULTI= + # (Optionally) define the PHPUnit command execution call. # Use if `php phpunit.phar` can't be called directly for some reason. export WPT_PHPUNIT_CMD= From 80b179a6ea1bcab10e20f88210a70b0316204cdc Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Sat, 16 Mar 2024 17:16:49 +0100 Subject: [PATCH 02/30] prepare for multi-php --- prepare.php | 463 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 334 insertions(+), 129 deletions(-) diff --git a/prepare.php b/prepare.php index 2948b70..8e35863 100644 --- a/prepare.php +++ b/prepare.php @@ -27,6 +27,7 @@ $WPT_SSH_OPTIONS = trim( getenv( 'WPT_SSH_OPTIONS' ) ) ? : '-o StrictHostKeyChecking=no'; $WPT_TEST_DIR = trim( getenv( 'WPT_TEST_DIR' ) ); $WPT_PHP_EXECUTABLE = trim( getenv( 'WPT_PHP_EXECUTABLE' ) ) ? : 'php'; +$WPT_PHP_EXECUTABLE_MULTI = trim( getenv( 'WPT_PHP_EXECUTABLE_MULTI' ) ) ? : ''; $WPT_CERTIFICATE_VALIDATION = trim( getenv( 'WPT_CERTIFICATE_VALIDATION' ) ); /** @@ -50,6 +51,27 @@ } unset( $WPT_DEBUG_INI ); +/** + */ +$WPT_PHP_EXECUTABLE_MULTI_ARRAY = array(); +if ( '' !== $WPT_PHP_EXECUTABLE_MULTI ) { + + $php_multi_versions = explode( ',', $WPT_PHP_EXECUTABLE_MULTI ); + + foreach( $php_multi_versions as $php_multi_version ) { + + $php_multi_v = explode( '+', $php_multi_version ); + + if( isset( $php_multi_v[0] ) && $php_multi_v[0] && isset( $php_multi_v[1] ) && $php_multi_v[1] ) { + $WPT_PHP_EXECUTABLE_MULTI_ARRAY[] = array( 'version' => trim( $php_multi_v[0] ), 'bin' => trim( $php_multi_v[1] ) ); + } + + unset( $php_multi_version ); + } + + unset( $php_multi_versions ); +} + /** * Sets up the SSH private key for use in the test environment if provided. * The private key is expected to be in base64-encoded form in the environment variable 'WPT_SSH_PRIVATE_KEY_BASE64'. @@ -105,41 +127,6 @@ $certificate_validation .= ' --no-check-certificate'; } -/** - * Performs a series of operations to set up the test environment. This includes creating a preparation directory, - * cloning the WordPress development repository, downloading the WordPress importer plugin, and preparing the environment with npm. - */ -// Prepare an array of shell commands to set up the testing environment. -perform_operations( array( - - // Create the preparation directory if it doesn't exist. The '-p' flag creates intermediate directories as required. - 'mkdir -p ' . escapeshellarg( $WPT_PREPARE_DIR ), - - // Clone the WordPress develop repository from GitHub into the preparation directory. - // The '--depth=1' flag creates a shallow clone with a history truncated to the last commit. - 'git clone --depth=1 https://github.com/WordPress/wordpress-develop.git ' . escapeshellarg( $WPT_PREPARE_DIR ), - - // Download the WordPress importer plugin zip file to the specified plugins directory. - 'wget -O ' . escapeshellarg( $WPT_PREPARE_DIR . '/tests/phpunit/data/plugins/wordpress-importer.zip' ) . ' https://downloads.wordpress.org/plugin/wordpress-importer.zip' . $certificate_validation, - - // Change directory to the plugin directory, unzip the WordPress importer plugin, and remove the zip file. - 'cd ' . escapeshellarg( $WPT_PREPARE_DIR . '/tests/phpunit/data/plugins/' ) . '; unzip wordpress-importer.zip; rm wordpress-importer.zip', - - // Change directory to the preparation directory, install npm dependencies, and build the project. - 'cd ' . escapeshellarg( $WPT_PREPARE_DIR ) . '; npm install && npm run build' - -) ); - -// Log a message indicating the start of the variable replacement process for configuration. -log_message( 'Replacing variables in wp-tests-config.php' ); - -/** - * Reads the contents of the WordPress test configuration sample file. - * This file contains template placeholders that need to be replaced with actual values - * from environment variables to configure the WordPress test environment. - */ -$contents = file_get_contents( $WPT_PREPARE_DIR . '/wp-tests-config-sample.php' ); - /** * Prepares a script to log system information relevant to the testing environment. * The script checks for the existence of the log directory and creates it if it does not exist. @@ -245,128 +232,346 @@ function curl_selected_bits(\$k) { return in_array(\$k, array('version', 'ssl_ve // Prepend the logger script to the database settings identifier to ensure it gets included in the wp-tests-config.php file. $system_logger = $logger_replace_string . $system_logger; -// Define a string that will set the 'WP_PHP_BINARY' constant to the path of the PHP executable. -$php_binary_string = 'define( \'WP_PHP_BINARY\', \''. $WPT_PHP_EXECUTABLE . '\' );'; +// MULTI-PHP Thing + +if( count( $WPT_PHP_EXECUTABLE_MULTI_ARRAY ) ) { /** - * An associative array mapping configuration file placeholders to environment-specific values. - * This array is used in the subsequent str_replace operation to replace placeholders - * in the wp-tests-config-sample.php file with values from the environment or defaults if none are provided. + * Performs a series of operations to set up the test environment. This includes creating a preparation directory, + * cloning the WordPress development repository, downloading the WordPress importer plugin, and preparing the environment with npm. */ -$search_replace = array( - 'wptests_' => trim( getenv( 'WPT_TABLE_PREFIX' ) ) ? : 'wptests_', - 'youremptytestdbnamehere' => trim( getenv( 'WPT_DB_NAME' ) ), - 'yourusernamehere' => trim( getenv( 'WPT_DB_USER' ) ), - 'yourpasswordhere' => trim( getenv( 'WPT_DB_PASSWORD' ) ), - 'localhost' => trim( getenv( 'WPT_DB_HOST' ) ), - 'define( \'WP_PHP_BINARY\', \'php\' );' => $php_binary_string, - $logger_replace_string => $system_logger, -); +// Prepare an array of shell commands to set up the testing environment. -// Replace the placeholders in the wp-tests-config-sample.php file content with actual values. -$contents = str_replace( array_keys( $search_replace ), array_values( $search_replace ), $contents ); + foreach( $WPT_PHP_EXECUTABLE_MULTI_ARRAY as $php_multi ) { -// Write the modified content to the wp-tests-config.php file, which will be used by the test suite. -file_put_contents( $WPT_PREPARE_DIR . '/wp-tests-config.php', $contents ); + $WPT_PREPARE_DIR_MULTI = $WPT_PREPARE_DIR . '-' . crc32( $php_multi['version'] ); + $WPT_TEST_DIR_MULTI = $WPT_TEST_DIR . '-' . crc32( $php_multi['version'] ); -/** - * Determines the PHP version of the test environment to ensure the correct version of PHPUnit is installed. - * It constructs a command that prints out the PHP version in a format compatible with PHPUnit's version requirements. - */ -$php_version_cmd = $WPT_PHP_EXECUTABLE . " -r \"print PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '.' . PHP_RELEASE_VERSION;\""; - -/** - * If an SSH connection string is provided, the command to determine the PHP version is modified - * to execute remotely over SSH. This is required if the test environment is not the local machine. - */ -if ( ! empty( $WPT_SSH_CONNECT ) ) { - // The PHP version check command is prefixed with the SSH command, including SSH options, - // and the connection string, ensuring the command is executed on the remote machine. - $php_version_cmd = 'ssh ' . $WPT_SSH_OPTIONS . ' ' . escapeshellarg( $WPT_SSH_CONNECT ) . ' ' . escapeshellarg( $php_version_cmd ); -} + perform_operations( array( -// Initialize return value variable for the exec function call. -$retval = 0; + // Create the preparation directory if it doesn't exist. The '-p' flag creates intermediate directories as required. + 'mkdir -p ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ), -/** - * Executes the constructed command to obtain the PHP version of the test environment. - * The output is stored in $env_php_version, and the return value of the command execution is stored in $retval. - */ -$env_php_version = exec( $php_version_cmd, $output, $retval ); + // Clone the WordPress develop repository from GitHub into the preparation directory. + // The '--depth=1' flag creates a shallow clone with a history truncated to the last commit. + 'git clone --depth=1 https://github.com/WordPress/wordpress-develop.git ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ), -// Check if the command execution was successful by inspecting the return value. -if ( $retval !== 0 ) { - // If the return value is not zero, an error occurred, and a message is logged. - error_message( 'Could not retrieve the environment PHP Version.' ); -} + // Download the WordPress importer plugin zip file to the specified plugins directory. + 'wget -O ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI . '/tests/phpunit/data/plugins/wordpress-importer.zip' ) . ' https://downloads.wordpress.org/plugin/wordpress-importer.zip' . $certificate_validation, -// Log the obtained PHP version for confirmation and debugging purposes. -log_message( 'Environment PHP Version: ' . $env_php_version ); + // Change directory to the plugin directory, unzip the WordPress importer plugin, and remove the zip file. + 'cd ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI . '/tests/phpunit/data/plugins/' ) . '; unzip wordpress-importer.zip; rm wordpress-importer.zip', -/** - * Checks if the detected PHP version is below 7.0. - * The test runner requires PHP version 7.0 or above, and if the environment's PHP version - * is lower, it logs an error message and could terminate the script. - */ -if ( version_compare( $env_php_version, '7.0', '<' ) ) { - // Logs an error message indicating the test runner's incompatibility with PHP versions below 7.0. - error_message( 'The test runner is not compatible with PHP < 7.0.' ); -} + // Change directory to the preparation directory, install npm dependencies, and build the project. + 'cd ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ) . '; npm install && npm run build' -/** - * Use Composer to manage PHPUnit and its dependencies. - * This allows for better dependency management and compatibility. - */ + ) ); -// Check if Composer is installed and available in the PATH. -$composer_cmd = 'cd ' . escapeshellarg( $WPT_PREPARE_DIR ) . ' && '; -$retval = 0; -$composer_path = escapeshellarg( system( 'which composer', $retval ) ); + // Log a message indicating the start of the variable replacement process for configuration. + log_message( 'Replacing variables in ' . $php_multi['version'] . ' wp-tests-config.php' ); + + /** + * Reads the contents of the WordPress test configuration sample file. + * This file contains template placeholders that need to be replaced with actual values + * from environment variables to configure the WordPress test environment. + */ + + $WPT_PREPARE_DIR_MULTI = $WPT_PREPARE_DIR . '-' . crc32( $php_multi['version'] ); + $contents = file_get_contents( $WPT_PREPARE_DIR_MULTI . '/wp-tests-config-sample.php' ); + + // Define a string that will set the 'WP_PHP_BINARY' constant to the path of the PHP executable. + + $php_binary_string = 'define( \'WP_PHP_BINARY\', \''. $php_multi['bin'] . '\' );'; + + /** + * An associative array mapping configuration file placeholders to environment-specific values. + * This array is used in the subsequent str_replace operation to replace placeholders + * in the wp-tests-config-sample.php file with values from the environment or defaults if none are provided. + */ + + $wptests_tableprefix = trim( getenv( 'WPT_TABLE_PREFIX' ) ) ? : 'wptests_'; + $wptests_tableprefix .= crc32( $php_multi['version'] ); + $search_replace = array( + 'wptests_' => $wptests_tableprefix, + 'youremptytestdbnamehere' => trim( getenv( 'WPT_DB_NAME' ) ), + 'yourusernamehere' => trim( getenv( 'WPT_DB_USER' ) ), + 'yourpasswordhere' => trim( getenv( 'WPT_DB_PASSWORD' ) ), + 'localhost' => trim( getenv( 'WPT_DB_HOST' ) ), + 'define( \'WP_PHP_BINARY\', \'php\' );' => $php_binary_string, + $logger_replace_string => $system_logger, + ); + $contents = str_replace( array_keys( $search_replace ), array_values( $search_replace ), $contents ); + + // Replace the placeholders in the wp-tests-config-sample.php file content with actual values. + // Write the modified content to the wp-tests-config.php file, which will be used by the test suite. + + $WPT_PREPARE_DIR_MULTI = $WPT_PREPARE_DIR . '-' . crc32( $php_multi['version'] ); + file_put_contents( $WPT_PREPARE_DIR_MULTI . '/wp-tests-config.php', $contents ); + + /** + * Determines the PHP version of the test environment to ensure the correct version of PHPUnit is installed. + * It constructs a command that prints out the PHP version in a format compatible with PHPUnit's version requirements. + */ + $php_version_cmd = $php_multi['bin'] . " -r \"print PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '.' . PHP_RELEASE_VERSION;\""; + + /** + * If an SSH connection string is provided, the command to determine the PHP version is modified + * to execute remotely over SSH. This is required if the test environment is not the local machine. + */ + if ( ! empty( $WPT_SSH_CONNECT ) ) { + // The PHP version check command is prefixed with the SSH command, including SSH options, + // and the connection string, ensuring the command is executed on the remote machine. + $php_version_cmd = 'ssh ' . $WPT_SSH_OPTIONS . ' ' . escapeshellarg( $WPT_SSH_CONNECT ) . ' ' . escapeshellarg( $php_version_cmd ); + } + + // Initialize return value variable for the exec function call. + $retval = 0; + + /** + * Executes the constructed command to obtain the PHP version of the test environment. + * The output is stored in $env_php_version, and the return value of the command execution is stored in $retval. + */ + $env_php_version = exec( $php_version_cmd, $output, $retval ); + + // Check if the command execution was successful by inspecting the return value. + if ( $retval !== 0 ) { + // If the return value is not zero, an error occurred, and a message is logged. + error_message( 'Could not retrieve the environment PHP Version for ' . $php_multi['version'] . '.' ); + } + + // Log the obtained PHP version for confirmation and debugging purposes. + log_message( 'Environment PHP Version: ' . $env_php_version ); + + /** + * Checks if the detected PHP version is below 7.0. + * The test runner requires PHP version 7.0 or above, and if the environment's PHP version + * is lower, it logs an error message and could terminate the script. + */ + if ( version_compare( $env_php_version, '7.0', '<' ) ) { + // Logs an error message indicating the test runner's incompatibility with PHP versions below 7.0. + error_message( 'The test runner is not compatible with PHP < 7.0.' ); + } + + /** + * Use Composer to manage PHPUnit and its dependencies. + * This allows for better dependency management and compatibility. + */ + + // Check if Composer is installed and available in the PATH. + + $composer_cmd = 'cd ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ) . ' && '; + + $retval = 0; + $composer_path = escapeshellarg( system( 'which composer', $retval ) ); + + if ( $retval === 0 ) { + + // If Composer is available, prepare the command to use the Composer binary. + $composer_cmd .= $composer_path . ' '; + + } else { + + // If Composer is not available, download the Composer phar file. + log_message( 'Local Composer not found. Downloading latest stable ...' ); + + perform_operations( array( + 'wget -O ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI . '/composer.phar' ) . ' https://getcomposer.org/composer-stable.phar', + ) ); + + // Update the command to use the downloaded Composer phar file. + $composer_cmd .= $php_multi['bin'] . ' composer.phar '; + } + + // Set the PHP version for Composer to ensure compatibility and update dependencies. + perform_operations( array( + $composer_cmd . 'config platform.php ' . escapeshellarg( $env_php_version ), + $composer_cmd . 'update', + ) ); -if ( $retval === 0 ) { + /** + * If an SSH connection is configured, use rsync to transfer the prepared files to the remote test environment. + * The -r option for rsync enables recursive copying to handle directory structures. + * Additional rsync options may be included for more verbose output if debugging is enabled. + */ + if ( ! empty( $WPT_SSH_CONNECT ) ) { + // Initialize rsync options with recursive copying. + $rsync_options = '-r'; + + // If debug mode is set to verbose, append 'v' to rsync options for verbose output. + if ( 'verbose' === $WPT_DEBUG ) { + $rsync_options = $rsync_options . 'v'; + } + + // Perform the rsync operation with the configured options and exclude patterns. + // This operation synchronizes the test environment with the prepared files, excluding version control directories + // and other non-essential files for test execution. + perform_operations( array( + 'rsync ' . $rsync_options . ' --exclude=".git/" --exclude="node_modules/" --exclude="composer.phar" -e "ssh ' . $WPT_SSH_OPTIONS . '" ' . escapeshellarg( trailingslashit( $WPT_PREPARE_DIR_MULTI ) ) . ' ' . escapeshellarg( $WPT_SSH_CONNECT . ':' . $WPT_TEST_DIR ), + ) ); + } - // If Composer is available, prepare the command to use the Composer binary. - $composer_cmd .= $composer_path . ' '; + } } else { - // If Composer is not available, download the Composer phar file. - log_message( 'Local Composer not found. Downloading latest stable ...' ); - perform_operations( array( - 'wget -O ' . escapeshellarg( $WPT_PREPARE_DIR . '/composer.phar' ) . ' https://getcomposer.org/composer-stable.phar', + + // Create the preparation directory if it doesn't exist. The '-p' flag creates intermediate directories as required. + 'mkdir -p ' . escapeshellarg( $WPT_PREPARE_DIR ), + + // Clone the WordPress develop repository from GitHub into the preparation directory. + // The '--depth=1' flag creates a shallow clone with a history truncated to the last commit. + 'git clone --depth=1 https://github.com/WordPress/wordpress-develop.git ' . escapeshellarg( $WPT_PREPARE_DIR ), + + // Download the WordPress importer plugin zip file to the specified plugins directory. + 'wget -O ' . escapeshellarg( $WPT_PREPARE_DIR . '/tests/phpunit/data/plugins/wordpress-importer.zip' ) . ' https://downloads.wordpress.org/plugin/wordpress-importer.zip' . $certificate_validation, + + // Change directory to the plugin directory, unzip the WordPress importer plugin, and remove the zip file. + 'cd ' . escapeshellarg( $WPT_PREPARE_DIR . '/tests/phpunit/data/plugins/' ) . '; unzip wordpress-importer.zip; rm wordpress-importer.zip', + + // Change directory to the preparation directory, install npm dependencies, and build the project. + 'cd ' . escapeshellarg( $WPT_PREPARE_DIR ) . '; npm install && npm run build' + ) ); - // Update the command to use the downloaded Composer phar file. - $composer_cmd .= 'php composer.phar '; -} + // Log a message indicating the start of the variable replacement process for configuration. + log_message( 'Replacing variables in wp-tests-config.php' ); + + /** + * Reads the contents of the WordPress test configuration sample file. + * This file contains template placeholders that need to be replaced with actual values + * from environment variables to configure the WordPress test environment. + */ + + $contents = file_get_contents( $WPT_PREPARE_DIR . '/wp-tests-config-sample.php' ); + + // Define a string that will set the 'WP_PHP_BINARY' constant to the path of the PHP executable. + + $php_binary_string = 'define( \'WP_PHP_BINARY\', \''. $WPT_PHP_EXECUTABLE . '\' );'; + + /** + * An associative array mapping configuration file placeholders to environment-specific values. + * This array is used in the subsequent str_replace operation to replace placeholders + * in the wp-tests-config-sample.php file with values from the environment or defaults if none are provided. + */ + + $wptests_tableprefix = trim( getenv( 'WPT_TABLE_PREFIX' ) ) ? : 'wptests_'; + $search_replace = array( + 'wptests_' => $wptests_tableprefix, + 'youremptytestdbnamehere' => trim( getenv( 'WPT_DB_NAME' ) ), + 'yourusernamehere' => trim( getenv( 'WPT_DB_USER' ) ), + 'yourpasswordhere' => trim( getenv( 'WPT_DB_PASSWORD' ) ), + 'localhost' => trim( getenv( 'WPT_DB_HOST' ) ), + 'define( \'WP_PHP_BINARY\', \'php\' );' => $php_binary_string, + $logger_replace_string => $system_logger, + ); + $contents = str_replace( array_keys( $search_replace ), array_values( $search_replace ), $contents ); + + // Replace the placeholders in the wp-tests-config-sample.php file content with actual values. + // Write the modified content to the wp-tests-config.php file, which will be used by the test suite. + + file_put_contents( $WPT_PREPARE_DIR . '/wp-tests-config.php', $contents ); + + /** + * Determines the PHP version of the test environment to ensure the correct version of PHPUnit is installed. + * It constructs a command that prints out the PHP version in a format compatible with PHPUnit's version requirements. + */ + $php_version_cmd = $WPT_PHP_EXECUTABLE . " -r \"print PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '.' . PHP_RELEASE_VERSION;\""; + + /** + * If an SSH connection string is provided, the command to determine the PHP version is modified + * to execute remotely over SSH. This is required if the test environment is not the local machine. + */ + if ( ! empty( $WPT_SSH_CONNECT ) ) { + // The PHP version check command is prefixed with the SSH command, including SSH options, + // and the connection string, ensuring the command is executed on the remote machine. + $php_version_cmd = 'ssh ' . $WPT_SSH_OPTIONS . ' ' . escapeshellarg( $WPT_SSH_CONNECT ) . ' ' . escapeshellarg( $php_version_cmd ); + } -// Set the PHP version for Composer to ensure compatibility and update dependencies. -perform_operations( array( - $composer_cmd . 'config platform.php ' . escapeshellarg( $env_php_version ), - $composer_cmd . 'update', -) ); + // Initialize return value variable for the exec function call. + $retval = 0; -/** - * If an SSH connection is configured, use rsync to transfer the prepared files to the remote test environment. - * The -r option for rsync enables recursive copying to handle directory structures. - * Additional rsync options may be included for more verbose output if debugging is enabled. - */ -if ( ! empty( $WPT_SSH_CONNECT ) ) { - // Initialize rsync options with recursive copying. - $rsync_options = '-r'; + /** + * Executes the constructed command to obtain the PHP version of the test environment. + * The output is stored in $env_php_version, and the return value of the command execution is stored in $retval. + */ + $env_php_version = exec( $php_version_cmd, $output, $retval ); + + // Check if the command execution was successful by inspecting the return value. + if ( $retval !== 0 ) { + // If the return value is not zero, an error occurred, and a message is logged. + error_message( 'Could not retrieve the environment PHP Version.' ); + } + + // Log the obtained PHP version for confirmation and debugging purposes. + log_message( 'Environment PHP Version: ' . $env_php_version ); + + /** + * Checks if the detected PHP version is below 7.0. + * The test runner requires PHP version 7.0 or above, and if the environment's PHP version + * is lower, it logs an error message and could terminate the script. + */ + if ( version_compare( $env_php_version, '7.0', '<' ) ) { + // Logs an error message indicating the test runner's incompatibility with PHP versions below 7.0. + error_message( 'The test runner is not compatible with PHP < 7.0.' ); + } + + /** + * Use Composer to manage PHPUnit and its dependencies. + * This allows for better dependency management and compatibility. + */ + + // Check if Composer is installed and available in the PATH. + $composer_cmd = 'cd ' . escapeshellarg( $WPT_PREPARE_DIR ) . ' && '; + $retval = 0; + $composer_path = escapeshellarg( system( 'which composer', $retval ) ); + + if ( $retval === 0 ) { + + // If Composer is available, prepare the command to use the Composer binary. + $composer_cmd .= $composer_path . ' '; + + } else { + + // If Composer is not available, download the Composer phar file. + log_message( 'Local Composer not found. Downloading latest stable ...' ); + + perform_operations( array( + 'wget -O ' . escapeshellarg( $WPT_PREPARE_DIR . '/composer.phar' ) . ' https://getcomposer.org/composer-stable.phar', + ) ); - // If debug mode is set to verbose, append 'v' to rsync options for verbose output. - if ( 'verbose' === $WPT_DEBUG ) { - $rsync_options = $rsync_options . 'v'; + // Update the command to use the downloaded Composer phar file. + $composer_cmd .= $WPT_PHP_EXECUTABLE . ' composer.phar '; } - // Perform the rsync operation with the configured options and exclude patterns. - // This operation synchronizes the test environment with the prepared files, excluding version control directories - // and other non-essential files for test execution. + // Set the PHP version for Composer to ensure compatibility and update dependencies. perform_operations( array( - 'rsync ' . $rsync_options . ' --exclude=".git/" --exclude="node_modules/" --exclude="composer.phar" -e "ssh ' . $WPT_SSH_OPTIONS . '" ' . escapeshellarg( trailingslashit( $WPT_PREPARE_DIR ) ) . ' ' . escapeshellarg( $WPT_SSH_CONNECT . ':' . $WPT_TEST_DIR ), + $composer_cmd . 'config platform.php ' . escapeshellarg( $env_php_version ), + $composer_cmd . 'update', ) ); + + /** + * If an SSH connection is configured, use rsync to transfer the prepared files to the remote test environment. + * The -r option for rsync enables recursive copying to handle directory structures. + * Additional rsync options may be included for more verbose output if debugging is enabled. + */ + if ( ! empty( $WPT_SSH_CONNECT ) ) { + // Initialize rsync options with recursive copying. + $rsync_options = '-r'; + + // If debug mode is set to verbose, append 'v' to rsync options for verbose output. + if ( 'verbose' === $WPT_DEBUG ) { + $rsync_options = $rsync_options . 'v'; + } + + // Perform the rsync operation with the configured options and exclude patterns. + // This operation synchronizes the test environment with the prepared files, excluding version control directories + // and other non-essential files for test execution. + perform_operations( array( + 'rsync ' . $rsync_options . ' --exclude=".git/" --exclude="node_modules/" --exclude="composer.phar" -e "ssh ' . $WPT_SSH_OPTIONS . '" ' . escapeshellarg( trailingslashit( $WPT_PREPARE_DIR ) ) . ' ' . escapeshellarg( $WPT_SSH_CONNECT . ':' . $WPT_TEST_DIR ), + ) ); + } + } // Log a success message indicating that the environment has been prepared. From ee4ac7b47d2780bc7f6623c9bc92281b2f225be4 Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Sat, 16 Mar 2024 17:16:57 +0100 Subject: [PATCH 03/30] test for multi-php --- test.php | 94 ++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 78 insertions(+), 16 deletions(-) diff --git a/test.php b/test.php index 18b3f4f..0709f98 100644 --- a/test.php +++ b/test.php @@ -25,6 +25,7 @@ $WPT_TEST_DIR = trim( getenv( 'WPT_TEST_DIR' ) ); $WPT_SSH_OPTIONS = trim( getenv( 'WPT_SSH_OPTIONS' ) ) ? : '-o StrictHostKeyChecking=no'; $WPT_PHP_EXECUTABLE = trim( getenv( 'WPT_PHP_EXECUTABLE' ) ) ? : 'php'; +$WPT_PHP_EXECUTABLE_MULTI = trim( getenv( 'WPT_PHP_EXECUTABLE_MULTI' ) ) ? : ''; // Uses the flavor (usually to test WordPress Multisite) $WPT_FLAVOR_INI = trim( getenv( 'WPT_FLAVOR' ) ); @@ -63,23 +64,84 @@ unset( $WPT_EXTRATESTS_INI ); /** - * Determines the PHPUnit command to execute the test suite. - * Retrieves the PHPUnit command from the environment variable 'WPT_PHPUNIT_CMD'. If the environment - * variable is not set or is empty, it constructs a default command using the PHP executable path and - * the test directory path from environment variables, appending parameters to the PHPUnit call to - * avoid reporting useless tests. */ -$WPT_PHPUNIT_CMD = trim( getenv( 'WPT_PHPUNIT_CMD' ) ); -if( empty( $WPT_PHPUNIT_CMD ) ) { - $WPT_PHPUNIT_CMD = 'cd ' . escapeshellarg( $WPT_TEST_DIR ) . ' && ' . $WPT_PHP_EXECUTABLE . ' ./vendor/phpunit/phpunit/phpunit --dont-report-useless-tests' . $WPT_FLAVOR_TXT . $WPT_EXTRATESTS_TXT; -} +$WPT_PHP_EXECUTABLE_MULTI_ARRAY = array(); +if ( '' !== $WPT_PHP_EXECUTABLE_MULTI ) { + + $php_multi_versions = explode( ',', $WPT_PHP_EXECUTABLE_MULTI ); + + foreach( $php_multi_versions as $php_multi_version ) { + + $php_multi_v = explode( '+', $php_multi_version ); + + if( isset( $php_multi_v[0] ) && $php_multi_v[0] && isset( $php_multi_v[1] ) && $php_multi_v[1] ) { + $WPT_PHP_EXECUTABLE_MULTI_ARRAY[] = array( 'version' => trim( $php_multi_v[0] ), 'bin' => trim( $php_multi_v[1] ) ); + } -// If an SSH connection string is provided, prepend the SSH command to the PHPUnit execution command. -if ( ! empty( $WPT_SSH_CONNECT ) ) { - $WPT_PHPUNIT_CMD = 'ssh ' . $WPT_SSH_OPTIONS . ' ' . escapeshellarg( $WPT_SSH_CONNECT ) . ' ' . escapeshellarg( $WPT_PHPUNIT_CMD ); + unset( $php_multi_version ); + } + + unset( $php_multi_versions ); } -// Execute the PHPUnit command. -perform_operations( array( - $WPT_PHPUNIT_CMD -) ); +if( count( $WPT_PHP_EXECUTABLE_MULTI_ARRAY ) ) { + +/** + * Performs a series of operations to set up the test environment. This includes creating a preparation directory, + * cloning the WordPress development repository, downloading the WordPress importer plugin, and preparing the environment with npm. + */ +// Prepare an array of shell commands to set up the testing environment. + + foreach( $WPT_PHP_EXECUTABLE_MULTI_ARRAY as $php_multi ) { + + $WPT_TEST_DIR_MULTI = $WPT_TEST_DIR . '-' . crc32( $php_multi['version'] ); + + /** + * Determines the PHPUnit command to execute the test suite. + * Retrieves the PHPUnit command from the environment variable 'WPT_PHPUNIT_CMD'. If the environment + * variable is not set or is empty, it constructs a default command using the PHP executable path and + * the test directory path from environment variables, appending parameters to the PHPUnit call to + * avoid reporting useless tests. + */ + $WPT_PHPUNIT_CMD = trim( getenv( 'WPT_PHPUNIT_CMD' ) ); + if( empty( $WPT_PHPUNIT_CMD ) ) { + $WPT_PHPUNIT_CMD = 'cd ' . escapeshellarg( $WPT_TEST_DIR_MULTI ) . ' && ' . $php_multi['bin'] . ' ./vendor/phpunit/phpunit/phpunit --dont-report-useless-tests' . $WPT_FLAVOR_TXT . $WPT_EXTRATESTS_TXT; + } + + // If an SSH connection string is provided, prepend the SSH command to the PHPUnit execution command. + if ( ! empty( $WPT_SSH_CONNECT ) ) { + $WPT_PHPUNIT_CMD = 'ssh ' . $WPT_SSH_OPTIONS . ' ' . escapeshellarg( $WPT_SSH_CONNECT ) . ' ' . escapeshellarg( $WPT_PHPUNIT_CMD ); + } + + // Execute the PHPUnit command. + perform_operations( array( + $WPT_PHPUNIT_CMD + ) ); + + } +} else { + + /** + * Determines the PHPUnit command to execute the test suite. + * Retrieves the PHPUnit command from the environment variable 'WPT_PHPUNIT_CMD'. If the environment + * variable is not set or is empty, it constructs a default command using the PHP executable path and + * the test directory path from environment variables, appending parameters to the PHPUnit call to + * avoid reporting useless tests. + */ + $WPT_PHPUNIT_CMD = trim( getenv( 'WPT_PHPUNIT_CMD' ) ); + if( empty( $WPT_PHPUNIT_CMD ) ) { + $WPT_PHPUNIT_CMD = 'cd ' . escapeshellarg( $WPT_TEST_DIR ) . ' && ' . $WPT_PHP_EXECUTABLE . ' ./vendor/phpunit/phpunit/phpunit --dont-report-useless-tests' . $WPT_FLAVOR_TXT . $WPT_EXTRATESTS_TXT; + } + + // If an SSH connection string is provided, prepend the SSH command to the PHPUnit execution command. + if ( ! empty( $WPT_SSH_CONNECT ) ) { + $WPT_PHPUNIT_CMD = 'ssh ' . $WPT_SSH_OPTIONS . ' ' . escapeshellarg( $WPT_SSH_CONNECT ) . ' ' . escapeshellarg( $WPT_PHPUNIT_CMD ); + } + + // Execute the PHPUnit command. + perform_operations( array( + $WPT_PHPUNIT_CMD + ) ); + +} + \ No newline at end of file From 78c5e61237cd43e43dc150d6f83ca4bf3f27cea5 Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Sat, 16 Mar 2024 17:17:07 +0100 Subject: [PATCH 04/30] cleanup for multi-php --- cleanup.php | 106 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 85 insertions(+), 21 deletions(-) diff --git a/cleanup.php b/cleanup.php index e584c6c..b0d74b5 100644 --- a/cleanup.php +++ b/cleanup.php @@ -24,30 +24,94 @@ $WPT_SSH_CONNECT = trim( getenv( 'WPT_SSH_CONNECT' ) ); $WPT_SSH_OPTIONS = trim( getenv( 'WPT_SSH_OPTIONS' ) ) ? : '-o StrictHostKeyChecking=no'; $WPT_TEST_DIR = trim( getenv( 'WPT_TEST_DIR' ) ); -$WPT_RM_TEST_DIR_CMD = trim( getenv( 'WPT_RM_TEST_DIR_CMD' ) ) ? : 'rm -r ' . $WPT_TEST_DIR; +$WPT_PHP_EXECUTABLE_MULTI = trim( getenv( 'WPT_PHP_EXECUTABLE_MULTI' ) ) ? : ''; /** - * The directory path of the test preparation directory is assumed to be previously defined. - * For example: $WPT_PREPARE_DIR = '/path/to/your/preparation/dir'; - * Clean up the preparation directory. - * Forcefully deletes only the .git directory and the node_modules cache. - * Afterward, the entire preparation directory is removed to ensure a clean state for the next test run. */ -perform_operations( array( - 'rm -rf ' . escapeshellarg( $WPT_PREPARE_DIR . '/.git' ), - 'rm -rf ' . escapeshellarg( $WPT_PREPARE_DIR . '/node_modules/.cache' ), - 'rm -r ' . escapeshellarg( $WPT_PREPARE_DIR ), -) ); +$WPT_PHP_EXECUTABLE_MULTI_ARRAY = array(); +if ( '' !== $WPT_PHP_EXECUTABLE_MULTI ) { -/** - * Cleans up the test directory on a remote server. - * This conditional block checks if an SSH connection string is provided and is not empty. - * If a connection string is present, it triggers a cleanup operation on the remote environment. - * The cleanup operation is executed by the `perform_operations` function which takes an array - * of shell commands as its input. - */ -if ( ! empty( $WPT_SSH_CONNECT ) ) { + $php_multi_versions = explode( ',', $WPT_PHP_EXECUTABLE_MULTI ); + + foreach( $php_multi_versions as $php_multi_version ) { + + $php_multi_v = explode( '+', $php_multi_version ); + + if( isset( $php_multi_v[0] ) && $php_multi_v[0] && isset( $php_multi_v[1] ) && $php_multi_v[1] ) { + $WPT_PHP_EXECUTABLE_MULTI_ARRAY[] = array( 'version' => trim( $php_multi_v[0] ), 'bin' => trim( $php_multi_v[1] ) ); + } + + unset( $php_multi_version ); + } + + unset( $php_multi_versions ); +} + +if( count( $WPT_PHP_EXECUTABLE_MULTI_ARRAY ) ) { + + foreach( $WPT_PHP_EXECUTABLE_MULTI_ARRAY as $php_multi ) { + + $WPT_PREPARE_DIR_MULTI = $WPT_PREPARE_DIR . '-' . crc32( $php_multi['version'] ); + $WPT_TEST_DIR_MULTI = $WPT_TEST_DIR . '-' . crc32( $php_multi['version'] ); + + $WPT_RM_TEST_DIR_CMD = trim( getenv( 'WPT_RM_TEST_DIR_CMD' ) ) ? : 'rm -r ' . $WPT_TEST_DIR_MULTI; + + /** + * The directory path of the test preparation directory is assumed to be previously defined. + * For example: $WPT_PREPARE_DIR = '/path/to/your/preparation/dir'; + * Clean up the preparation directory. + * Forcefully deletes only the .git directory and the node_modules cache. + * Afterward, the entire preparation directory is removed to ensure a clean state for the next test run. + */ + perform_operations( array( + 'rm -rf ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI . '/.git' ), + 'rm -rf ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI . '/node_modules/.cache' ), + 'rm -r ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ), + ) ); + + /** + * Cleans up the test directory on a remote server. + * This conditional block checks if an SSH connection string is provided and is not empty. + * If a connection string is present, it triggers a cleanup operation on the remote environment. + * The cleanup operation is executed by the `perform_operations` function which takes an array + * of shell commands as its input. + */ + if ( ! empty( $WPT_SSH_CONNECT ) ) { + perform_operations( array( + 'ssh ' . $WPT_SSH_OPTIONS . ' ' . escapeshellarg( $WPT_SSH_CONNECT ) . ' ' . escapeshellarg( $WPT_RM_TEST_DIR_CMD ), + ) ); + } + + } + +} else { + + $WPT_RM_TEST_DIR_CMD = trim( getenv( 'WPT_RM_TEST_DIR_CMD' ) ) ? : 'rm -r ' . $WPT_TEST_DIR; + + /** + * The directory path of the test preparation directory is assumed to be previously defined. + * For example: $WPT_PREPARE_DIR = '/path/to/your/preparation/dir'; + * Clean up the preparation directory. + * Forcefully deletes only the .git directory and the node_modules cache. + * Afterward, the entire preparation directory is removed to ensure a clean state for the next test run. + */ perform_operations( array( - 'ssh ' . $WPT_SSH_OPTIONS . ' ' . escapeshellarg( $WPT_SSH_CONNECT ) . ' ' . escapeshellarg( $WPT_RM_TEST_DIR_CMD ), + 'rm -rf ' . escapeshellarg( $WPT_PREPARE_DIR . '/.git' ), + 'rm -rf ' . escapeshellarg( $WPT_PREPARE_DIR . '/node_modules/.cache' ), + 'rm -r ' . escapeshellarg( $WPT_PREPARE_DIR ), ) ); -} + + /** + * Cleans up the test directory on a remote server. + * This conditional block checks if an SSH connection string is provided and is not empty. + * If a connection string is present, it triggers a cleanup operation on the remote environment. + * The cleanup operation is executed by the `perform_operations` function which takes an array + * of shell commands as its input. + */ + if ( ! empty( $WPT_SSH_CONNECT ) ) { + perform_operations( array( + 'ssh ' . $WPT_SSH_OPTIONS . ' ' . escapeshellarg( $WPT_SSH_CONNECT ) . ' ' . escapeshellarg( $WPT_RM_TEST_DIR_CMD ), + ) ); + } + +} \ No newline at end of file From 331ecc31385c6ec4949de06e7c3d73ee0d14fc24 Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Sat, 16 Mar 2024 18:17:18 +0100 Subject: [PATCH 05/30] report for multi-php --- report.php | 379 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 275 insertions(+), 104 deletions(-) diff --git a/report.php b/report.php index b690f76..112dd1e 100644 --- a/report.php +++ b/report.php @@ -27,6 +27,7 @@ $WPT_PREPARE_DIR = trim( getenv( 'WPT_PREPARE_DIR' ) ); $WPT_SSH_OPTIONS = trim( getenv( 'WPT_SSH_OPTIONS' ) ); $WPT_REPORT_API_KEY = trim( getenv( 'WPT_REPORT_API_KEY' ) ); +$WPT_PHP_EXECUTABLE_MULTI = trim( getenv( 'WPT_PHP_EXECUTABLE_MULTI' ) ) ? : ''; /** * Determines if the debug mode is enabled based on the 'WPT_DEBUG' environment variable. @@ -50,130 +51,300 @@ unset( $WPT_DEBUG_INI ); /** - * Retrieves the SVN revision number from the git repository log. - * Logs a message indicating the start of the SVN revision retrieval process. - * Executes a shell command that accesses the git directory specified by the - * WPT_PREPARE_DIR environment variable, retrieves the latest commit message, - * and extracts the SVN revision number using a combination of grep and cut commands. */ -log_message('Getting SVN Revision'); -$rev = exec('git --git-dir=' . escapeshellarg( $WPT_PREPARE_DIR ) . '/.git log -1 --pretty=%B | grep "git-svn-id:" | cut -d " " -f 2 | cut -d "@" -f 2'); +$WPT_PHP_EXECUTABLE_MULTI_ARRAY = array(); +if ( '' !== $WPT_PHP_EXECUTABLE_MULTI ) { -/** - * Retrieves the latest SVN commit message from the git repository log. - * Logs a message to indicate the retrieval of the SVN commit message. Executes a shell command - * that accesses the git directory specified by the WPT_PREPARE_DIR environment variable, - * fetches the latest commit message, and trims any whitespace from the message. - */ -log_message('Getting SVN message'); -$message = trim( exec('git --git-dir=' . escapeshellarg( $WPT_PREPARE_DIR ) . '/.git log -1 --pretty=%B | head -1') ); + $php_multi_versions = explode( ',', $WPT_PHP_EXECUTABLE_MULTI ); -/** - * Prepares the file path for copying the junit.xml results. - * Logs a message indicating the start of the operation to copy junit.xml results. - * Constructs the file path to the junit.xml file(s) located in the test directory, - * making use of the WPT_TEST_DIR environment variable. The path is sanitized to be - * safely used in shell commands. - */ -log_message('Copying junit.xml results'); -$junit_location = escapeshellarg( $WPT_TEST_DIR ) . '/tests/phpunit/build/logs/*'; + foreach( $php_multi_versions as $php_multi_version ) { -/** - * Modifies the junit.xml results file path for a remote location if an SSH connection is available. - * If the WPT_SSH_CONNECT environment variable is not empty, indicating that an SSH connection - * is configured, this snippet adapts the junit_location variable to include the necessary SSH - * command and options for accessing the remote file system. It concatenates SSH options with the - * remote path to ensure that the junit.xml results can be accessed or copied over SSH. - */ -if ( ! empty( $WPT_SSH_CONNECT ) ) { - $junit_location = '-e "ssh ' . $WPT_SSH_OPTIONS . '" ' . escapeshellarg( $WPT_SSH_CONNECT . ':' . $junit_location ); -} + $php_multi_v = explode( '+', $php_multi_version ); -/** - * Sets the options for the rsync command based on the debug mode. - * Initializes the rsync options with the recursive flag. If the debug mode is set to 'verbose', - * appends the 'v' flag to the rsync options to enable verbose output during the rsync operation, - * providing more detailed information about the file transfer process. - */ -$rsync_options = '-r'; + if( isset( $php_multi_v[0] ) && $php_multi_v[0] && isset( $php_multi_v[1] ) && $php_multi_v[1] ) { + $WPT_PHP_EXECUTABLE_MULTI_ARRAY[] = array( 'version' => trim( $php_multi_v[0] ), 'bin' => trim( $php_multi_v[1] ) ); + } -if ( 'verbose' === $WPT_DEBUG ) { - $rsync_options = $rsync_options . 'v'; + unset( $php_multi_version ); + } + + unset( $php_multi_versions ); } -/** - * Constructs the rsync command for executing the synchronization of junit.xml files. - * Concatenates the rsync command with the previously defined options and the source and - * destination paths. The destination path is sanitized for shell execution. This command is - * then passed to the `perform_operations` function, which executes the command to synchronize - * the junit.xml files from the source to the destination directory. - */ -$junit_exec = 'rsync ' . $rsync_options . ' ' . $junit_location . ' ' . escapeshellarg( $WPT_PREPARE_DIR ); -perform_operations( array( - $junit_exec, -) ); +// MULTI-PHP Thing -/** - * Processes and uploads the junit.xml file. - * First, a log message is recorded to indicate the start of processing the junit.xml file. - * Then, the contents of the junit.xml file are read from the prepared directory into a string. - * This XML string is then passed to a function that processes the XML data, presumably to prepare - * it for upload or to extract relevant test run information. - */ -log_message( 'Processing and uploading junit.xml' ); -$xml = file_get_contents( $WPT_PREPARE_DIR . '/junit.xml' ); -$results = process_junit_xml( $xml ); +if( count( $WPT_PHP_EXECUTABLE_MULTI_ARRAY ) ) { /** - * Retrieves environment details from a JSON file or generates them if not available. - * Initializes the environment details string. If an 'env.json' file exists in the prepared - * directory, its contents are read into the environment details string. If the file doesn't - * exist but the prepared directory is the same as the test directory, the environment details - * are generated by calling a function that retrieves these details, then encoded into JSON format. + * Performs a series of operations to set up the test environment. This includes creating a preparation directory, + * cloning the WordPress development repository, downloading the WordPress importer plugin, and preparing the environment with npm. */ -$env = ''; -if ( file_exists( $WPT_PREPARE_DIR . '/env.json' ) ) { - $env = file_get_contents( $WPT_PREPARE_DIR . '/env.json' ); -} elseif ( $WPT_PREPARE_DIR === $WPT_TEST_DIR ) { - $env = json_encode( get_env_details(), JSON_PRETTY_PRINT ); -} +// Prepare an array of shell commands to set up the testing environment. -/** - * Attempts to upload test results if an API key is available, otherwise logs the results locally. - * Checks if an API key for reporting is present. If so, it attempts to upload the test results - * using the `upload_results` function and processes the HTTP response. A success message is logged - * if the upload is successful, indicated by a 20x HTTP status code. If the upload fails, an error - * message is logged along with the HTTP status. If no API key is provided, it logs the test results - * and environment details locally. - */ -if( ! empty( $WPT_REPORT_API_KEY ) ) { + foreach( $WPT_PHP_EXECUTABLE_MULTI_ARRAY as $php_multi ) { - // Upload the results and capture the HTTP status and response body - list( $http_status, $response_body ) = upload_results( $results, $rev, $message, $env, $WPT_REPORT_API_KEY ); + $WPT_PREPARE_DIR_MULTI = $WPT_PREPARE_DIR . '-' . crc32( $php_multi['version'] ); + $WPT_TEST_DIR_MULTI = $WPT_TEST_DIR . '-' . crc32( $php_multi['version'] ); - // Decode the JSON response body - $response = json_decode( $response_body, true ); - if ( 20 == substr( $http_status, 0, 2 ) ) { + /** + * Retrieves the SVN revision number from the git repository log. + * Logs a message indicating the start of the SVN revision retrieval process. + * Executes a shell command that accesses the git directory specified by the + * WPT_PREPARE_DIR environment variable, retrieves the latest commit message, + * and extracts the SVN revision number using a combination of grep and cut commands. + */ + log_message('Getting SVN Revision'); + $rev = exec('git --git-dir=' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ) . '/.git log -1 --pretty=%B | grep "git-svn-id:" | cut -d " " -f 2 | cut -d "@" -f 2'); - // Construct and log a success message with a link if provided in the response - $message = 'Results successfully uploaded'; - $message .= isset( $response['link'] ) ? ': ' . $response['link'] : ''; - log_message( $message ); + /** + * Retrieves the latest SVN commit message from the git repository log. + * Logs a message to indicate the retrieval of the SVN commit message. Executes a shell command + * that accesses the git directory specified by the WPT_PREPARE_DIR environment variable, + * fetches the latest commit message, and trims any whitespace from the message. + */ + log_message('Getting SVN message'); + $message = trim( exec('git --git-dir=' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ) . '/.git log -1 --pretty=%B | head -1') ); - } else { + /** + * Prepares the file path for copying the junit.xml results. + * Logs a message indicating the start of the operation to copy junit.xml results. + * Constructs the file path to the junit.xml file(s) located in the test directory, + * making use of the WPT_TEST_DIR environment variable. The path is sanitized to be + * safely used in shell commands. + */ + log_message('Copying junit.xml results'); + $junit_location = escapeshellarg( $WPT_TEST_DIR_MULTI ) . '/tests/phpunit/build/logs/*'; - // Construct and log an error message with additional details if provided in the response - $message = 'Error uploading results'; - $message .= isset( $response['message'] ) ? ': ' . $response['message'] : ''; - $message .= ' (HTTP status ' . (int) $http_status . ')'; - error_message( $message ); + /** + * Modifies the junit.xml results file path for a remote location if an SSH connection is available. + * If the WPT_SSH_CONNECT environment variable is not empty, indicating that an SSH connection + * is configured, this snippet adapts the junit_location variable to include the necessary SSH + * command and options for accessing the remote file system. It concatenates SSH options with the + * remote path to ensure that the junit.xml results can be accessed or copied over SSH. + */ + if ( ! empty( $WPT_SSH_CONNECT ) ) { + $junit_location = '-e "ssh ' . $WPT_SSH_OPTIONS . '" ' . escapeshellarg( $WPT_SSH_CONNECT . ':' . $junit_location ); + } - } + /** + * Sets the options for the rsync command based on the debug mode. + * Initializes the rsync options with the recursive flag. If the debug mode is set to 'verbose', + * appends the 'v' flag to the rsync options to enable verbose output during the rsync operation, + * providing more detailed information about the file transfer process. + */ + $rsync_options = '-r'; + + if ( 'verbose' === $WPT_DEBUG ) { + $rsync_options = $rsync_options . 'v'; + } + + /** + * Constructs the rsync command for executing the synchronization of junit.xml files. + * Concatenates the rsync command with the previously defined options and the source and + * destination paths. The destination path is sanitized for shell execution. This command is + * then passed to the `perform_operations` function, which executes the command to synchronize + * the junit.xml files from the source to the destination directory. + */ + $junit_exec = 'rsync ' . $rsync_options . ' ' . $junit_location . ' ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ); + perform_operations( array( + $junit_exec, + ) ); + + /** + * Processes and uploads the junit.xml file. + * First, a log message is recorded to indicate the start of processing the junit.xml file. + * Then, the contents of the junit.xml file are read from the prepared directory into a string. + * This XML string is then passed to a function that processes the XML data, presumably to prepare + * it for upload or to extract relevant test run information. + */ + log_message( 'Processing and uploading junit.xml' ); + $xml = file_get_contents( $WPT_PREPARE_DIR_MULTI . '/junit.xml' ); + $results = process_junit_xml( $xml ); + + /** + * Retrieves environment details from a JSON file or generates them if not available. + * Initializes the environment details string. If an 'env.json' file exists in the prepared + * directory, its contents are read into the environment details string. If the file doesn't + * exist but the prepared directory is the same as the test directory, the environment details + * are generated by calling a function that retrieves these details, then encoded into JSON format. + */ + $env = ''; + if ( file_exists( $WPT_PREPARE_DIR_MULTI . '/env.json' ) ) { + $env = file_get_contents( $WPT_PREPARE_DIR_MULTI . '/env.json' ); + } elseif ( $WPT_PREPARE_DIR_MULTI === $WPT_TEST_DIR_MULTI ) { + $env = json_encode( get_env_details(), JSON_PRETTY_PRINT ); + } + + /** + * Attempts to upload test results if an API key is available, otherwise logs the results locally. + * Checks if an API key for reporting is present. If so, it attempts to upload the test results + * using the `upload_results` function and processes the HTTP response. A success message is logged + * if the upload is successful, indicated by a 20x HTTP status code. If the upload fails, an error + * message is logged along with the HTTP status. If no API key is provided, it logs the test results + * and environment details locally. + */ + if( ! empty( $WPT_REPORT_API_KEY ) ) { + + // Upload the results and capture the HTTP status and response body + list( $http_status, $response_body ) = upload_results( $results, $rev, $message, $env, $WPT_REPORT_API_KEY ); + + // Decode the JSON response body + $response = json_decode( $response_body, true ); + if ( 20 == substr( $http_status, 0, 2 ) ) { + + // Construct and log a success message with a link if provided in the response + $message = 'Results successfully uploaded'; + $message .= isset( $response['link'] ) ? ': ' . $response['link'] : ''; + log_message( $message ); + + } else { + + // Construct and log an error message with additional details if provided in the response + $message = 'Error uploading results'; + $message .= isset( $response['message'] ) ? ': ' . $response['message'] : ''; + $message .= ' (HTTP status ' . (int) $http_status . ')'; + error_message( $message ); + } + + } else { + + // Log the test results and environment details locally if no API key is provided + log_message( '[+] TEST RESULTS' . "\n\n" . $results. "\n\n" ); + log_message( '[+] ENVIRONMENT' . "\n\n" . $env . "\n\n" ); + + } + + } + } else { - // Log the test results and environment details locally if no API key is provided - log_message( '[+] TEST RESULTS' . "\n\n" . $results. "\n\n" ); - log_message( '[+] ENVIRONMENT' . "\n\n" . $env . "\n\n" ); + /** + * Retrieves the SVN revision number from the git repository log. + * Logs a message indicating the start of the SVN revision retrieval process. + * Executes a shell command that accesses the git directory specified by the + * WPT_PREPARE_DIR environment variable, retrieves the latest commit message, + * and extracts the SVN revision number using a combination of grep and cut commands. + */ + log_message('Getting SVN Revision'); + $rev = exec('git --git-dir=' . escapeshellarg( $WPT_PREPARE_DIR ) . '/.git log -1 --pretty=%B | grep "git-svn-id:" | cut -d " " -f 2 | cut -d "@" -f 2'); -} + /** + * Retrieves the latest SVN commit message from the git repository log. + * Logs a message to indicate the retrieval of the SVN commit message. Executes a shell command + * that accesses the git directory specified by the WPT_PREPARE_DIR environment variable, + * fetches the latest commit message, and trims any whitespace from the message. + */ + log_message('Getting SVN message'); + $message = trim( exec('git --git-dir=' . escapeshellarg( $WPT_PREPARE_DIR ) . '/.git log -1 --pretty=%B | head -1') ); + + /** + * Prepares the file path for copying the junit.xml results. + * Logs a message indicating the start of the operation to copy junit.xml results. + * Constructs the file path to the junit.xml file(s) located in the test directory, + * making use of the WPT_TEST_DIR environment variable. The path is sanitized to be + * safely used in shell commands. + */ + log_message('Copying junit.xml results'); + $junit_location = escapeshellarg( $WPT_TEST_DIR ) . '/tests/phpunit/build/logs/*'; + + /** + * Modifies the junit.xml results file path for a remote location if an SSH connection is available. + * If the WPT_SSH_CONNECT environment variable is not empty, indicating that an SSH connection + * is configured, this snippet adapts the junit_location variable to include the necessary SSH + * command and options for accessing the remote file system. It concatenates SSH options with the + * remote path to ensure that the junit.xml results can be accessed or copied over SSH. + */ + if ( ! empty( $WPT_SSH_CONNECT ) ) { + $junit_location = '-e "ssh ' . $WPT_SSH_OPTIONS . '" ' . escapeshellarg( $WPT_SSH_CONNECT . ':' . $junit_location ); + } + + /** + * Sets the options for the rsync command based on the debug mode. + * Initializes the rsync options with the recursive flag. If the debug mode is set to 'verbose', + * appends the 'v' flag to the rsync options to enable verbose output during the rsync operation, + * providing more detailed information about the file transfer process. + */ + $rsync_options = '-r'; + + if ( 'verbose' === $WPT_DEBUG ) { + $rsync_options = $rsync_options . 'v'; + } + + /** + * Constructs the rsync command for executing the synchronization of junit.xml files. + * Concatenates the rsync command with the previously defined options and the source and + * destination paths. The destination path is sanitized for shell execution. This command is + * then passed to the `perform_operations` function, which executes the command to synchronize + * the junit.xml files from the source to the destination directory. + */ + $junit_exec = 'rsync ' . $rsync_options . ' ' . $junit_location . ' ' . escapeshellarg( $WPT_PREPARE_DIR ); + perform_operations( array( + $junit_exec, + ) ); + + /** + * Processes and uploads the junit.xml file. + * First, a log message is recorded to indicate the start of processing the junit.xml file. + * Then, the contents of the junit.xml file are read from the prepared directory into a string. + * This XML string is then passed to a function that processes the XML data, presumably to prepare + * it for upload or to extract relevant test run information. + */ + log_message( 'Processing and uploading junit.xml' ); + $xml = file_get_contents( $WPT_PREPARE_DIR . '/junit.xml' ); + $results = process_junit_xml( $xml ); + + /** + * Retrieves environment details from a JSON file or generates them if not available. + * Initializes the environment details string. If an 'env.json' file exists in the prepared + * directory, its contents are read into the environment details string. If the file doesn't + * exist but the prepared directory is the same as the test directory, the environment details + * are generated by calling a function that retrieves these details, then encoded into JSON format. + */ + $env = ''; + if ( file_exists( $WPT_PREPARE_DIR . '/env.json' ) ) { + $env = file_get_contents( $WPT_PREPARE_DIR . '/env.json' ); + } elseif ( $WPT_PREPARE_DIR === $WPT_TEST_DIR ) { + $env = json_encode( get_env_details(), JSON_PRETTY_PRINT ); + } + + /** + * Attempts to upload test results if an API key is available, otherwise logs the results locally. + * Checks if an API key for reporting is present. If so, it attempts to upload the test results + * using the `upload_results` function and processes the HTTP response. A success message is logged + * if the upload is successful, indicated by a 20x HTTP status code. If the upload fails, an error + * message is logged along with the HTTP status. If no API key is provided, it logs the test results + * and environment details locally. + */ + if( ! empty( $WPT_REPORT_API_KEY ) ) { + + // Upload the results and capture the HTTP status and response body + list( $http_status, $response_body ) = upload_results( $results, $rev, $message, $env, $WPT_REPORT_API_KEY ); + + // Decode the JSON response body + $response = json_decode( $response_body, true ); + if ( 20 == substr( $http_status, 0, 2 ) ) { + + // Construct and log a success message with a link if provided in the response + $message = 'Results successfully uploaded'; + $message .= isset( $response['link'] ) ? ': ' . $response['link'] : ''; + log_message( $message ); + + } else { + + // Construct and log an error message with additional details if provided in the response + $message = 'Error uploading results'; + $message .= isset( $response['message'] ) ? ': ' . $response['message'] : ''; + $message .= ' (HTTP status ' . (int) $http_status . ')'; + error_message( $message ); + + } + + } else { + + // Log the test results and environment details locally if no API key is provided + log_message( '[+] TEST RESULTS' . "\n\n" . $results. "\n\n" ); + log_message( '[+] ENVIRONMENT' . "\n\n" . $env . "\n\n" ); + + } +} \ No newline at end of file From ef8a5de63ac09d55d89e7979d748e0ef68219de5 Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Sat, 16 Mar 2024 18:23:07 +0100 Subject: [PATCH 06/30] temporary fix --- functions.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/functions.php b/functions.php index f7d5981..5e18daa 100644 --- a/functions.php +++ b/functions.php @@ -65,9 +65,11 @@ function perform_operations( $operations ) { foreach( $operations as $operation ) { log_message( $operation ); passthru( $operation, $return_code ); + /* THIS IS A TEMPORARY (or not) COMMENTED CODE if ( 0 !== $return_code ) { error_message( 'Failed to perform operation.' ); } + */ } } From 58034b04b7abcc9febebdc7561fb29f9bf0e5a55 Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Sun, 17 Mar 2024 08:15:59 +0100 Subject: [PATCH 07/30] some changes - add the label - test dir is a dir (a tmp one) - no cert validation by default - simple WP by default --- .env.default | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.env.default b/.env.default index 12cf091..6934f74 100644 --- a/.env.default +++ b/.env.default @@ -11,12 +11,16 @@ # $ source .env ### +# Label for the environment. Can be empty (default) or be like "shared", "vps", "cloud" or similar. +# Please use only alphanumeric keywords, and try to be descriptive +export WPT_LABEL= + # Path to the directory where files can be prepared before being delivered to the environment. export WPT_PREPARE_DIR=/tmp/wp-test-runner # Path to the directory where the WordPress develop checkout can be placed and tests can be run. # When running tests in the same environment, set WPT_TEST_DIR to WPT_PREPARE_DIR -export WPT_TEST_DIR=wp-test-runner +export WPT_TEST_DIR=/tmp/wp-test-runner # API key to authenticate with the reporting service in 'username:password' format. export WPT_REPORT_API_KEY= @@ -66,12 +70,12 @@ export WPT_DEBUG= # Certificate validation # Use 1 to validate, and 0 to not validate -export WPT_CERTIFICATE_VALIDATION=1 +export WPT_CERTIFICATE_VALIDATION=0 # WordPress flavor # 0 = WordPress (simple version) # 1 = WordPress Multisite -export WPT_FLAVOR=1 +export WPT_FLAVOR=0 # Extra tests (groups) # 0 = none From 9205042eddf2711323a83f83cd30f7a396b44d6d Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Sun, 17 Mar 2024 11:04:59 +0100 Subject: [PATCH 08/30] env file with commints checking --- .env.default | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.env.default b/.env.default index 6934f74..ea58c89 100644 --- a/.env.default +++ b/.env.default @@ -83,3 +83,8 @@ export WPT_FLAVOR=0 # 2 = ms-files # 3 = external-http export WPT_EXTRATESTS=0 + +# Check all commits +# 0 = latest +# 1 = all +export WPT_COMMITS=0 From 8b49cb275d13cc4fddc04bea426b2941748884e3 Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Sun, 17 Mar 2024 11:05:24 +0100 Subject: [PATCH 09/30] possible future files --- commits.json | 4 ++++ commits.json.example | 4 ++++ ignore.json | 6 ++++++ ignore.json.example | 6 ++++++ 4 files changed, 20 insertions(+) create mode 100644 commits.json create mode 100644 commits.json.example create mode 100644 ignore.json create mode 100644 ignore.json.example diff --git a/commits.json b/commits.json new file mode 100644 index 0000000..050606e --- /dev/null +++ b/commits.json @@ -0,0 +1,4 @@ +{ + "executed_commits": [], + "pending_commits": [] +} diff --git a/commits.json.example b/commits.json.example new file mode 100644 index 0000000..40f00fd --- /dev/null +++ b/commits.json.example @@ -0,0 +1,4 @@ +{ + "executed_commits": [], + "pending_commits": ["hash1", "hash2", ..., "hash10"] +} diff --git a/ignore.json b/ignore.json new file mode 100644 index 0000000..557a88e --- /dev/null +++ b/ignore.json @@ -0,0 +1,6 @@ +{ + "commits_ignore": [ + "Tests_Filesystem_WpFilesystemDirect_Mkdir::test_should_set_owner", + "Tests_Filesystem_WpFilesystemDirect_Mkdir::test_should_set_group" + ] +} diff --git a/ignore.json.example b/ignore.json.example new file mode 100644 index 0000000..557a88e --- /dev/null +++ b/ignore.json.example @@ -0,0 +1,6 @@ +{ + "commits_ignore": [ + "Tests_Filesystem_WpFilesystemDirect_Mkdir::test_should_set_owner", + "Tests_Filesystem_WpFilesystemDirect_Mkdir::test_should_set_group" + ] +} From 09bf9a64c083fa63a62b6c8baa4dad92fe7d5c32 Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Sun, 17 Mar 2024 11:05:51 +0100 Subject: [PATCH 10/30] some changes - support for labels - read latest commits --- prepare.php | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/prepare.php b/prepare.php index 8e35863..edc7b22 100644 --- a/prepare.php +++ b/prepare.php @@ -22,6 +22,7 @@ * These variables are used to configure SSH connections, file paths, and * executable commands needed for setting up the test environment. */ +$WPT_LABEL = trim( getenv( 'WPT_LABEL' ) ) ? : 'default'; $WPT_PREPARE_DIR = trim( getenv( 'WPT_PREPARE_DIR' ) ); $WPT_SSH_CONNECT = trim( getenv( 'WPT_SSH_CONNECT' ) ); $WPT_SSH_OPTIONS = trim( getenv( 'WPT_SSH_OPTIONS' ) ) ? : '-o StrictHostKeyChecking=no'; @@ -51,6 +52,116 @@ } unset( $WPT_DEBUG_INI ); + +/** + */ +$WPT_COMMITS_INI = getenv( 'WPT_COMMITS' ); +switch( $WPT_COMMITS_INI ) { + case 0: + case 'false': + $WPT_COMMITS = false; + break; + case 1: + case 'true': + $WPT_COMMITS = true; + break; + default: + $WPT_COMMITS = false; + break; +} +unset( $WPT_COMMITS_INI ); + +$WPT_COMMIT = array(); +if( $WPT_COMMITS ) { + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, 'https://api.github.com/repos/WordPress/wordpress-develop/commits?per_page=10'); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_USERAGENT, 'WordPress.org PHPUnit test tool'); + $commits = curl_exec($ch); + curl_close($ch); + + $commits_array = json_decode( $commits, true ); + unset( $commits ); + foreach ( $commits_array as $commit ) { + + $WPT_COMMIT[] = $commit['sha']; + + unset( $commit ); + } + unset( $commits_array ); + +} + +if( count( $WPT_COMMIT ) ) { + + if( file_exists( __DIR__ . '/commits.json' ) ) { + + $c_array = json_decode( file_get_contents( __DIR__ . '/commits.json' ), true ); + + if( isset( $c_array['executed_commits'] ) ) { + $executed_commits = $c_array['executed_commits']; + } else { + $executed_commits = array(); + } + + if( isset( $c_array['pending_commits'] ) ) { + $pending_commits = $c_array['pending_commits']; + } else { + $pending_commits = array(); + } + + foreach ($executed_commits as $key => $commithash) { + if (!in_array($commithash, $WPT_COMMIT)) { + unset($executed_commits[$key]); + } + unset( $key, $commithash ); + } + + foreach ($pending_commits as $key => $commithash) { + if (!in_array($commithash, $WPT_COMMIT)) { + unset($pending_commits[$key]); + } + unset( $key, $commithash ); + } + + foreach ($WPT_COMMIT as $commithash) { + if (!in_array($commithash, $pending_commits) && !in_array($commithash, $executed_commits)) { + array_push($pending_commits, $commithash); + } + unset( $commithash ); + } + + $c = array( 'executed_commits' => $executed_commits, 'pending_commits' => $pending_commits ); + + unset( $executed_commits, $pending_commits ); + + $c_json = json_encode( $c ); + + file_put_contents( __DIR__ . '/commits.json', $c_json ); + + unset( $c, $c_json ); + + + } else { + + $c = array( 'executed_commits' => array(), 'pending_commits' => $WPT_COMMIT ); + + $c_json = json_encode( $c ); + + file_put_contents( __DIR__ . '/commits.json', $c_json ); + + unset( $c, $c_json ); + + } + +} + + +exit; + + + /** */ $WPT_PHP_EXECUTABLE_MULTI_ARRAY = array(); @@ -151,6 +262,7 @@ \$imagick_info = Imagick::queryFormats(); } \$env = array( + 'label' => '{$WPT_LABEL}', 'php_version' => phpversion(), 'php_modules' => array(), 'gd_info' => \$gd_info, From fea79f5b280b6cc096d1f5d4f48726332e277518 Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Sun, 17 Mar 2024 12:16:56 +0100 Subject: [PATCH 11/30] testing version --- commits.json | 3 +- commits.json.example | 5 +- prepare.php | 114 +++++++++++++++++++++++++++++++++++++++---- 3 files changed, 109 insertions(+), 13 deletions(-) diff --git a/commits.json b/commits.json index 050606e..b1a5a97 100644 --- a/commits.json +++ b/commits.json @@ -1,4 +1,5 @@ { "executed_commits": [], - "pending_commits": [] + "pending_commits": [], + "testing_commit": [] } diff --git a/commits.json.example b/commits.json.example index 40f00fd..506ac11 100644 --- a/commits.json.example +++ b/commits.json.example @@ -1,4 +1,5 @@ { - "executed_commits": [], - "pending_commits": ["hash1", "hash2", ..., "hash10"] + "executed_commits": ["hash3"], + "pending_commits": ["hash1", "hash2", ..., "hash10"], + "testing_commit": ["hash4"] } diff --git a/prepare.php b/prepare.php index edc7b22..98a01d0 100644 --- a/prepare.php +++ b/prepare.php @@ -99,6 +99,12 @@ $c_array = json_decode( file_get_contents( __DIR__ . '/commits.json' ), true ); + if( isset( $c_array['testing_commit'] ) ) { + $testing_commit = $c_array['testing_commit']; + } else { + $testing_commit = array(); + } + if( isset( $c_array['executed_commits'] ) ) { $executed_commits = $c_array['executed_commits']; } else { @@ -132,9 +138,9 @@ unset( $commithash ); } - $c = array( 'executed_commits' => $executed_commits, 'pending_commits' => $pending_commits ); + $c = array( 'executed_commits' => $executed_commits, 'pending_commits' => $pending_commits, 'testing_commit' => $testing_commit ); - unset( $executed_commits, $pending_commits ); + unset( $executed_commits, $pending_commits, $testing_commit ); $c_json = json_encode( $c ); @@ -145,7 +151,7 @@ } else { - $c = array( 'executed_commits' => array(), 'pending_commits' => $WPT_COMMIT ); + $c = array( 'executed_commits' => array(), 'pending_commits' => $WPT_COMMIT, 'testing_commit' => $testing_commit ); $c_json = json_encode( $c ); @@ -157,11 +163,6 @@ } - -exit; - - - /** */ $WPT_PHP_EXECUTABLE_MULTI_ARRAY = array(); @@ -366,7 +367,51 @@ function curl_selected_bits(\$k) { return in_array(\$k, array('version', 'ssl_ve // Clone the WordPress develop repository from GitHub into the preparation directory. // The '--depth=1' flag creates a shallow clone with a history truncated to the last commit. - 'git clone --depth=1 https://github.com/WordPress/wordpress-develop.git ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ), + 'git clone --depth=10 https://github.com/WordPress/wordpress-develop.git ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ), + + 'git config --add safe.directory ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ), + + ) ); + + if( $WPT_COMMITS ) { + + $commit_sha = null; + if( file_exists( __DIR__ . '/commits.json' ) ) { + + $c_array = json_decode( file_get_contents( __DIR__ . '/commits.json' ), true ); + + if( isset( $c_array['testing_commit'] ) && count( $c_array['testing_commit'] ) ) { + + $commit_sha = $c_array['testing_commit'][0]; + + } else { + + $commit_sha = array_shift($c_array['pending_commits']); + + $c_array['testing_commit'][0] = $commit_sha; + + } + + $c_json = json_encode( $c_array ); + + file_put_contents( __DIR__ . '/commits.json', $c_json ); + + } + + if( ! is_null( $commit_sha ) ) { + + perform_operations( array( + + 'cd ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ), + 'git checkout ' . $commit_sha + + ) ); + + } + + } + + perform_operations( array( // Download the WordPress importer plugin zip file to the specified plugins directory. 'wget -O ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI . '/tests/phpunit/data/plugins/wordpress-importer.zip' ) . ' https://downloads.wordpress.org/plugin/wordpress-importer.zip' . $certificate_validation, @@ -533,7 +578,51 @@ function curl_selected_bits(\$k) { return in_array(\$k, array('version', 'ssl_ve // Clone the WordPress develop repository from GitHub into the preparation directory. // The '--depth=1' flag creates a shallow clone with a history truncated to the last commit. - 'git clone --depth=1 https://github.com/WordPress/wordpress-develop.git ' . escapeshellarg( $WPT_PREPARE_DIR ), + 'git clone --depth=10 https://github.com/WordPress/wordpress-develop.git ' . escapeshellarg( $WPT_PREPARE_DIR ), + + 'git config --add safe.directory ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ), + + ) ); + + if( $WPT_COMMITS ) { + + $commit_sha = null; + if( file_exists( __DIR__ . '/commits.json' ) ) { + + $c_array = json_decode( file_get_contents( __DIR__ . '/commits.json' ), true ); + + if( isset( $c_array['testing_commit'] ) && count( $c_array['testing_commit'] ) ) { + + $commit_sha = $c_array['testing_commit'][0]; + + } else { + + $commit_sha = array_shift($c_array['pending_commits']); + + $c_array['testing_commit'][0] = $commit_sha; + + } + + $c_json = json_encode( $c_array ); + + file_put_contents( __DIR__ . '/commits.json', $c_json ); + + } + + if( ! is_null( $commit_sha ) ) { + + perform_operations( array( + + 'cd ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ), + 'git checkout ' . $commit_sha, + + ) ); + + } + + } + + perform_operations( array( // Download the WordPress importer plugin zip file to the specified plugins directory. 'wget -O ' . escapeshellarg( $WPT_PREPARE_DIR . '/tests/phpunit/data/plugins/wordpress-importer.zip' ) . ' https://downloads.wordpress.org/plugin/wordpress-importer.zip' . $certificate_validation, @@ -549,6 +638,11 @@ function curl_selected_bits(\$k) { return in_array(\$k, array('version', 'ssl_ve // Log a message indicating the start of the variable replacement process for configuration. log_message( 'Replacing variables in wp-tests-config.php' ); + +exit; + + + /** * Reads the contents of the WordPress test configuration sample file. * This file contains template placeholders that need to be replaced with actual values From 08781083e30966ccd4458cd3b884d2dcab15db2f Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Sun, 17 Mar 2024 12:20:03 +0100 Subject: [PATCH 12/30] fixes --- prepare.php | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/prepare.php b/prepare.php index 98a01d0..110355d 100644 --- a/prepare.php +++ b/prepare.php @@ -580,7 +580,7 @@ function curl_selected_bits(\$k) { return in_array(\$k, array('version', 'ssl_ve // The '--depth=1' flag creates a shallow clone with a history truncated to the last commit. 'git clone --depth=10 https://github.com/WordPress/wordpress-develop.git ' . escapeshellarg( $WPT_PREPARE_DIR ), - 'git config --add safe.directory ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ), + 'git config --add safe.directory ' . escapeshellarg( $WPT_PREPARE_DIR ), ) ); @@ -613,7 +613,7 @@ function curl_selected_bits(\$k) { return in_array(\$k, array('version', 'ssl_ve perform_operations( array( - 'cd ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ), + 'cd ' . escapeshellarg( $WPT_PREPARE_DIR ), 'git checkout ' . $commit_sha, ) ); @@ -638,11 +638,6 @@ function curl_selected_bits(\$k) { return in_array(\$k, array('version', 'ssl_ve // Log a message indicating the start of the variable replacement process for configuration. log_message( 'Replacing variables in wp-tests-config.php' ); - -exit; - - - /** * Reads the contents of the WordPress test configuration sample file. * This file contains template placeholders that need to be replaced with actual values From 109f52d570bfcd1944c1bb8677f4b8e1bdd485be Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Sun, 17 Mar 2024 14:34:39 +0100 Subject: [PATCH 13/30] reorder the commits to run --- prepare.php | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/prepare.php b/prepare.php index 110355d..1582992 100644 --- a/prepare.php +++ b/prepare.php @@ -133,7 +133,15 @@ foreach ($WPT_COMMIT as $commithash) { if (!in_array($commithash, $pending_commits) && !in_array($commithash, $executed_commits)) { - array_push($pending_commits, $commithash); + + if( ! count( $pending_commits ) ) { + + array_unshift($pending_commits, $commithash); + } else { + + array_push($pending_commits, $commithash); + } + } unset( $commithash ); } @@ -386,7 +394,7 @@ function curl_selected_bits(\$k) { return in_array(\$k, array('version', 'ssl_ve } else { - $commit_sha = array_shift($c_array['pending_commits']); + $commit_sha = array_pop($c_array['pending_commits']); $c_array['testing_commit'][0] = $commit_sha; @@ -402,8 +410,7 @@ function curl_selected_bits(\$k) { return in_array(\$k, array('version', 'ssl_ve perform_operations( array( - 'cd ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ), - 'git checkout ' . $commit_sha + 'cd ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ) . ' && git checkout ' . $commit_sha ) ); @@ -613,8 +620,7 @@ function curl_selected_bits(\$k) { return in_array(\$k, array('version', 'ssl_ve perform_operations( array( - 'cd ' . escapeshellarg( $WPT_PREPARE_DIR ), - 'git checkout ' . $commit_sha, + 'cd ' . escapeshellarg( $WPT_PREPARE_DIR ) . ' && git checkout ' . $commit_sha, ) ); From 07e45fcec8e76303d72be7a6e9be314969b3ec29 Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Sun, 17 Mar 2024 14:35:02 +0100 Subject: [PATCH 14/30] move the commit to the executed part --- report.php | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/report.php b/report.php index 112dd1e..b4a3afd 100644 --- a/report.php +++ b/report.php @@ -50,6 +50,42 @@ } unset( $WPT_DEBUG_INI ); +/** + */ +$WPT_COMMITS_INI = getenv( 'WPT_COMMITS' ); +switch( $WPT_COMMITS_INI ) { + case 0: + case 'false': + $WPT_COMMITS = false; + break; + case 1: + case 'true': + $WPT_COMMITS = true; + break; + default: + $WPT_COMMITS = false; + break; +} +unset( $WPT_COMMITS_INI ); + +/** + */ +$WPT_COMMITS_INI = getenv( 'WPT_COMMITS' ); +switch( $WPT_COMMITS_INI ) { + case 0: + case 'false': + $WPT_COMMITS = false; + break; + case 1: + case 'true': + $WPT_COMMITS = true; + break; + default: + $WPT_COMMITS = false; + break; +} +unset( $WPT_COMMITS_INI ); + /** */ $WPT_PHP_EXECUTABLE_MULTI_ARRAY = array(); @@ -86,6 +122,28 @@ $WPT_PREPARE_DIR_MULTI = $WPT_PREPARE_DIR . '-' . crc32( $php_multi['version'] ); $WPT_TEST_DIR_MULTI = $WPT_TEST_DIR . '-' . crc32( $php_multi['version'] ); + if( $WPT_COMMITS ) { + + $commit_sha = null; + if( file_exists( __DIR__ . '/commits.json' ) ) { + + $c_array = json_decode( file_get_contents( __DIR__ . '/commits.json' ), true ); + + if( isset( $c_array['testing_commit'] ) && count( $c_array['testing_commit'] ) ) { + + $c_array['executed_commits'][] = $c_array['testing_commit'][0]; + + unset( $c_array['testing_commit'][0] ); + + } + + $c_json = json_encode( $c_array ); + + file_put_contents( __DIR__ . '/commits.json', $c_json ); + + } + } + /** * Retrieves the SVN revision number from the git repository log. * Logs a message indicating the start of the SVN revision retrieval process. @@ -219,6 +277,28 @@ } else { + if( $WPT_COMMITS ) { + + $commit_sha = null; + if( file_exists( __DIR__ . '/commits.json' ) ) { + + $c_array = json_decode( file_get_contents( __DIR__ . '/commits.json' ), true ); + + if( isset( $c_array['testing_commit'] ) && count( $c_array['testing_commit'] ) ) { + + $c_array['executed_commits'][] = $c_array['testing_commit'][0]; + + unset( $c_array['testing_commit'][0] ); + + } + + $c_json = json_encode( $c_array ); + + file_put_contents( __DIR__ . '/commits.json', $c_json ); + + } + } + /** * Retrieves the SVN revision number from the git repository log. * Logs a message indicating the start of the SVN revision retrieval process. From d9106c94fa9dee614f270bb760673a683514741d Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Sun, 17 Mar 2024 17:04:33 +0100 Subject: [PATCH 15/30] checks that there are not duplicated tests to run --- prepare.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/prepare.php b/prepare.php index 1582992..f085f53 100644 --- a/prepare.php +++ b/prepare.php @@ -131,6 +131,11 @@ unset( $key, $commithash ); } + // Remove duplicates from pending_commits that are in executed_commits or are the testing_commit + $pending_commits = array_filter( $pending_commits, function( $commithash ) use ( $executed_commits, $testing_commit ) { + return !in_array( $commithash, $executed_commits ) && $commithash !== $testing_commit; + }); + foreach ($WPT_COMMIT as $commithash) { if (!in_array($commithash, $pending_commits) && !in_array($commithash, $executed_commits)) { From 2e3b29d40721f06488b84c5bd84c2a1ae93f9151 Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Mon, 18 Mar 2024 10:53:37 +0100 Subject: [PATCH 16/30] add some documentation this is a first step to include some documentation inside the project, and not only in the handbook page. --- docs/0-intro.md | 15 +++ docs/1-setup.md | 333 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 348 insertions(+) create mode 100644 docs/0-intro.md create mode 100644 docs/1-setup.md diff --git a/docs/0-intro.md b/docs/0-intro.md new file mode 100644 index 0000000..2630b66 --- /dev/null +++ b/docs/0-intro.md @@ -0,0 +1,15 @@ +# About the PHPUnit Test Runner + +Hosting companies can have several to millions of websites hosted with WordPress, so it's important to make sure their configuration is as compatible as possible with the software. + +To verify this compatibility, the WordPress Community provides a series of PHPUnit tests with which to check the operation of WordPress in any environment. + +The Runner tests generates a report with the test results related to a bot user (a hosting company), and this captures and displays those test results at the [Host Test Result](https://make.wordpress.org/hosting/test-results/) page. + +## What's the phpunit-test-runner + +The [phpunit-test-runner](https://github.com/WordPress/phpunit-test-runner) is a tool designed to make it easier for hosting companies to run the WordPress project’s automated tests. + +There is a [whole documentation about this tool](https://make.wordpress.org/hosting/test-results-getting-started/). Also, if you want, you can make your test results appear in the [Host Test Results](https://make.wordpress.org/hosting/test-results/) page of WordPress. + +The tool can be run manually or through an automated system like Travis. To see how it works and the purpose of this document, will be shown how to run the tests manually. diff --git a/docs/1-setup.md b/docs/1-setup.md new file mode 100644 index 0000000..1920a63 --- /dev/null +++ b/docs/1-setup.md @@ -0,0 +1,333 @@ +# Requirements + +To use the Runner, the following is required: + +- A server / hosting (infrastructure) with the usual configuration you have. +- A database (MySQL, MariaDB, or WordPress compatible) where you can test (it will be created and destroyed several times) +- NodeJS 20.x +- git, wget, rsync, zip, unzip... + +## NodeJS installation + +If you are using Debian / Ubuntu, install or update NodeJS 18 with this command: + +``` +curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - +sudo apt -y install nodejs +node -v +npm install -g npm@latest +npm --version +``` + +If you are using RHEL / CentOS, install or update NodeJS 20 with this command: + +``` +curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo -E bash - +sudo yum install -y nodejs +node -v +npm install -g npm@latest +npm --version +``` + +## Composer installation + +``` +curl -sS https://getcomposer.org/installer -o composer-setup.php +php composer-setup.php --install-dir=/usr/local/bin --filename=composer +composer --version +``` + +# Installing the Runner + +First, download the software. This example use `/home/wptestrunner/` folder, but set the best for this environment. + +``` +cd /home/wptestrunner/ +git clone https://github.com/WordPress/phpunit-test-runner.git +cd phpunit-test-runner/ +``` + +The next step will be to configure the environment. To do this, make a copy of the example file and then configure it. + +``` +cp .env.default .env +vim .env +``` + +This is the default configuraton file: + +``` +### +# Configuration environment variables used by the test runner +# +# # Create a copy for your local environment +# $ cp .env.default .env +# +# # Make any necessary changes to the default values +# $ vim .env +# +# # Load your variables into your environment +# $ source .env +### + +# Label for the environment. Can be empty (default) or be like "shared", "vps", "cloud" or similar. +# Please use only alphanumeric keywords, and try to be descriptive +export WPT_LABEL= + +# Path to the directory where files can be prepared before being delivered to the environment. +export WPT_PREPARE_DIR=/tmp/wp-test-runner + +# Path to the directory where the WordPress develop checkout can be placed and tests can be run. +# When running tests in the same environment, set WPT_TEST_DIR to WPT_PREPARE_DIR +export WPT_TEST_DIR=/tmp/wp-test-runner + +# API key to authenticate with the reporting service in 'username:password' format. +export WPT_REPORT_API_KEY= + +# (Optionally) define an alternate reporting URL +export WPT_REPORT_URL= + +# Credentials for a database that can be written to and reset. +# WARNING!!! This database will be destroyed between tests. Only use safe database credentials. +# Please note that you must escape _or_ refrain from using # as special character in your credentials. +export WPT_DB_NAME= +export WPT_DB_USER= +export WPT_DB_PASSWORD= +export WPT_DB_HOST= + +# (Optionally) set a custom table prefix to permit concurrency against the same database. +export WPT_TABLE_PREFIX=${WPT_TABLE_PREFIX-wptests_} + +# (Optionally) define the PHP executable to be called +export WPT_PHP_EXECUTABLE=${WPT_PHP_EXECUTABLE-php} + +# (Optionally) array of versions (like: 8.0+/bin/php8.0,8.1+/bin/php8.1) +export WPT_PHP_EXECUTABLE_MULTI= + +# (Optionally) define the PHPUnit command execution call. +# Use if `php phpunit.phar` can't be called directly for some reason. +export WPT_PHPUNIT_CMD= + +# (Optionally) define the command execution to remove the test directory +# Use if `rm -r` can't be called directly for some reason. +export WPT_RM_TEST_DIR_CMD= + +# SSH connection string (can also be an alias). +# Leave empty if tests are meant to run in the same environment. +export WPT_SSH_CONNECT= + +# Any options to be passed to the SSH connection +# Defaults to '-o StrictHostKeyChecking=no' +export WPT_SSH_OPTIONS= + +# SSH private key, base64 encoded. +export WPT_SSH_PRIVATE_KEY_BASE64= + +# Output logging +# Use 'verbose' to increase verbosity +export WPT_DEBUG= + +# Certificate validation +# Use 1 to validate, and 0 to not validate +export WPT_CERTIFICATE_VALIDATION=0 + +# WordPress flavor +# 0 = WordPress (simple version) +# 1 = WordPress Multisite +export WPT_FLAVOR=0 + +# Extra tests (groups) +# 0 = none +# 1 = ajax +# 2 = ms-files +# 3 = external-http +export WPT_EXTRATESTS=0 + +# Check all commits +# 0 = latest +# 1 = all +export WPT_COMMITS=0 +```` + +And this could be an example of each part: + +**Label** + +Label for the environment. Can be empty (default) or be like "shared", "vps", "cloud" or similar. Please use only alphanumeric keywords, and try to be descriptive + +``` +export WPT_LABEL=shared +``` + +**Preparation directory** + +Path to the directory where files can be prepared before being delivered to the environment. + +Usually can be a /tmp/ folder so it does everything temporary. + +``` +export WPT_PREPARE_DIR=/tmp/wp-test-runner +``` + +**Test directory** + +Path to the directory where the WordPress develop checkout can be placed and tests can be run. When running tests in the same environment, set WPT_TEST_DIR to WPT_PREPARE_DIR equally. + + +``` +export WPT_TEST_DIR=/tmp/wp-test-runner +``` + +**API KEY** + +API key to authenticate with the reporting service in 'username:password' format. This is only needed if you want to publish your results in the WordPress site, so data can help developers to improve WordPress. + +Read: [How to report: Creating your bot for WordPress.org](https://make.wordpress.org/hosting/handbook/tests/#how-to-report-creating-your-bot-for-wordpress-org) + +``` +export WPT_REPORT_API_KEY=userbot:12345ABCDE67890F +``` + +**Reporting URL** + +(Optionally) Define an alternate reporting URL, if you are running your own website. + +It should look like: +`https://reporter.example.com/wp-json/wp-unit-test-api/v1/results` + +``` +export WPT_REPORT_URL=https://reporter.example.com/wp-json/wp-unit-test-api/v1/results +``` + +**Database credentials** + +Credentials for a database that can be written to and reset. + +WARNING: This database will be destroyed between tests. Only use safe database credentials. + +Please note that you must escape _or_ refrain from using # as special character in your credentials. + + +``` +export WPT_DB_NAME=testbot +export WPT_DB_USER=wpuser +export WPT_DB_PASSWORD=wppassword +export WPT_DB_HOST=localhost +``` + +**Tables Custom prefix** + +(Optionally) Set a custom table prefix to allow concurrency against the same database. This is very useful if you activate the multi-php or multi-environment part. + +``` +export WPT_TABLE_PREFIX=${WPT_TABLE_PREFIX-wptests_} +``` + +**PHP versions** + +_There are two options for backward compatibility._ + +The first one is the binary file / path for the default PHP. If it's empty it will use "php" as the command. + +``` +export WPT_PHP_EXECUTABLE=${WPT_PHP_EXECUTABLE-php} +``` + +The second one is the optional part. This allow to test more than one PHP versions. The format to use is: + +_majorversion1+binary_path1,majorversion2+binary_path2_ + +something like: + +`8.0+/bin/php8.0,8.1+/bin/php8.1` + +Use as much versions as you want, but it will take more time. The idea is to put all the versions offered to users. + +``` +export WPT_PHP_EXECUTABLE_MULTI=7.4+/bin/php7.4,8.3+/bin/php8.3 +``` + +**PHPUnit execution call** + +(Optionally) define the PHPUnit command execution call. Use if `php phpunit.phar` can't be called directly for some reason. + +``` +export WPT_PHPUNIT_CMD= +``` + +**** + +``` +# (Optionally) define the command execution to remove the test directory +# Use if `rm -r` can't be called directly for some reason. +export WPT_RM_TEST_DIR_CMD= +``` + + +``` +# SSH connection string (can also be an alias). +# Leave empty if tests are meant to run in the same environment. +export WPT_SSH_CONNECT= +``` + + +``` +# Any options to be passed to the SSH connection +# Defaults to '-o StrictHostKeyChecking=no' +export WPT_SSH_OPTIONS= +``` + + +``` +# SSH private key, base64 encoded. +export WPT_SSH_PRIVATE_KEY_BASE64= +``` + + +``` +# Output logging +# Use 'verbose' to increase verbosity +export WPT_DEBUG= +``` + +``` +# Certificate validation +# Use 1 to validate, and 0 to not validate +export WPT_CERTIFICATE_VALIDATION=0 +``` + +``` +# WordPress flavor +# 0 = WordPress (simple version) +# 1 = WordPress Multisite +export WPT_FLAVOR=0 +``` + +``` +# Extra tests (groups) +# 0 = none +# 1 = ajax +# 2 = ms-files +# 3 = external-http +export WPT_EXTRATESTS=0 +``` + +``` +# Check all commits +# 0 = latest +# 1 = all +export WPT_COMMITS=0 +``` + + +Configure the folder where the WordPress software downloads and the database accesses will be made in order to prepare the tests. + +# Preparing the environment + +Before performing the first test, let’s update all the components. This process can be run before each test in this environment if wanted to keep it up to date, although it will depend more if it is in a production environment. + +``` +cd /home/wptestrunner/phpunit-test-runner/ +git pull +source .env +``` From d3eedade80f503a88a581c01a863d363c910b6be Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Sat, 28 Sep 2024 09:53:33 +0000 Subject: [PATCH 17/30] Update .env.default Co-authored-by: Matthias Pfefferle --- .env.default | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env.default b/.env.default index ea58c89..921e727 100644 --- a/.env.default +++ b/.env.default @@ -42,7 +42,7 @@ export WPT_TABLE_PREFIX=${WPT_TABLE_PREFIX-wptests_} # (Optionally) define the PHP executable to be called export WPT_PHP_EXECUTABLE=${WPT_PHP_EXECUTABLE-php} -# (Optionally) array of versions (like: 8.0+/bin/php8.0,8.1+/bin/php8.1) +# (Optionally) array of versions (like: 8.0=/bin/php8.0;8.1=/bin/php8.1) export WPT_PHP_EXECUTABLE_MULTI= # (Optionally) define the PHPUnit command execution call. From 64d44e8c650465a48db34a1e239d5e34cf9f98f2 Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Sat, 28 Sep 2024 09:53:45 +0000 Subject: [PATCH 18/30] Update docs/1-setup.md Co-authored-by: Matthias Pfefferle --- docs/1-setup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/1-setup.md b/docs/1-setup.md index 1920a63..4e0f217 100644 --- a/docs/1-setup.md +++ b/docs/1-setup.md @@ -9,7 +9,7 @@ To use the Runner, the following is required: ## NodeJS installation -If you are using Debian / Ubuntu, install or update NodeJS 18 with this command: +If you are using Debian / Ubuntu, install or update NodeJS 20 with this command: ``` curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - From 7f546e3771428e442b13271d98c4044dcda8fc10 Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Sat, 28 Sep 2024 09:53:55 +0000 Subject: [PATCH 19/30] Update docs/1-setup.md Co-authored-by: Matthias Pfefferle --- docs/1-setup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/1-setup.md b/docs/1-setup.md index 4e0f217..841e47f 100644 --- a/docs/1-setup.md +++ b/docs/1-setup.md @@ -101,7 +101,7 @@ export WPT_TABLE_PREFIX=${WPT_TABLE_PREFIX-wptests_} # (Optionally) define the PHP executable to be called export WPT_PHP_EXECUTABLE=${WPT_PHP_EXECUTABLE-php} -# (Optionally) array of versions (like: 8.0+/bin/php8.0,8.1+/bin/php8.1) +# (Optionally) array of versions (like: 8.0=/bin/php8.0;8.1=/bin/php8.1) export WPT_PHP_EXECUTABLE_MULTI= # (Optionally) define the PHPUnit command execution call. From caa2e3439bcc1e2a2b347b604275f0052ffe39bb Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Sat, 28 Sep 2024 09:54:03 +0000 Subject: [PATCH 20/30] Update docs/1-setup.md Co-authored-by: Matthias Pfefferle --- docs/1-setup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/1-setup.md b/docs/1-setup.md index 841e47f..f733a03 100644 --- a/docs/1-setup.md +++ b/docs/1-setup.md @@ -239,7 +239,7 @@ _majorversion1+binary_path1,majorversion2+binary_path2_ something like: -`8.0+/bin/php8.0,8.1+/bin/php8.1` +`8.0=/bin/php8.0;8.1=/bin/php8.1` Use as much versions as you want, but it will take more time. The idea is to put all the versions offered to users. From d8e35c12bf5fae2c819146ffb74d6a4b238988a0 Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Sat, 28 Sep 2024 09:54:10 +0000 Subject: [PATCH 21/30] Update docs/1-setup.md Co-authored-by: Matthias Pfefferle --- docs/1-setup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/1-setup.md b/docs/1-setup.md index f733a03..c7f2156 100644 --- a/docs/1-setup.md +++ b/docs/1-setup.md @@ -244,7 +244,7 @@ something like: Use as much versions as you want, but it will take more time. The idea is to put all the versions offered to users. ``` -export WPT_PHP_EXECUTABLE_MULTI=7.4+/bin/php7.4,8.3+/bin/php8.3 +export WPT_PHP_EXECUTABLE_MULTI=7.4=/bin/php7.4;8.3=/bin/php8.3 ``` **PHPUnit execution call** From 7c6ae9a7a83b2f42250a164b60c9a0f26372665b Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Sat, 28 Sep 2024 09:54:30 +0000 Subject: [PATCH 22/30] Update prepare.php Co-authored-by: Matthias Pfefferle --- prepare.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prepare.php b/prepare.php index f085f53..1841749 100644 --- a/prepare.php +++ b/prepare.php @@ -185,7 +185,7 @@ foreach( $php_multi_versions as $php_multi_version ) { - $php_multi_v = explode( '+', $php_multi_version ); + $php_multi_v = explode( '=', $php_multi_version ); if( isset( $php_multi_v[0] ) && $php_multi_v[0] && isset( $php_multi_v[1] ) && $php_multi_v[1] ) { $WPT_PHP_EXECUTABLE_MULTI_ARRAY[] = array( 'version' => trim( $php_multi_v[0] ), 'bin' => trim( $php_multi_v[1] ) ); From 5f232a1465ecfa45ab8fbef960bde08ee057dd09 Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Sat, 28 Sep 2024 09:55:29 +0000 Subject: [PATCH 23/30] Update prepare.php Co-authored-by: Matthias Pfefferle --- prepare.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prepare.php b/prepare.php index 1841749..5e8c917 100644 --- a/prepare.php +++ b/prepare.php @@ -181,7 +181,7 @@ $WPT_PHP_EXECUTABLE_MULTI_ARRAY = array(); if ( '' !== $WPT_PHP_EXECUTABLE_MULTI ) { - $php_multi_versions = explode( ',', $WPT_PHP_EXECUTABLE_MULTI ); + $php_multi_versions = explode( ';', $WPT_PHP_EXECUTABLE_MULTI ); foreach( $php_multi_versions as $php_multi_version ) { From 770200c9f23042c3b46bb8864d9c8343bc71600c Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Sat, 28 Sep 2024 12:04:03 +0200 Subject: [PATCH 24/30] TLS validation active by default --- .env.default | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env.default b/.env.default index 921e727..f9357f6 100644 --- a/.env.default +++ b/.env.default @@ -70,7 +70,7 @@ export WPT_DEBUG= # Certificate validation # Use 1 to validate, and 0 to not validate -export WPT_CERTIFICATE_VALIDATION=0 +export WPT_CERTIFICATE_VALIDATION=1 # WordPress flavor # 0 = WordPress (simple version) From 805d78de2000070a856dbf0fa2ce9300882b31a1 Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Sat, 28 Sep 2024 12:56:02 +0200 Subject: [PATCH 25/30] Yoda and some organization and fixes - Descriptions in 80 chars columns - Some Yoda fixes - Fix the "tab" vs "space" - Check some "comments" that should not be there (they were because some errors on the developing era) --- functions.php | 466 +++++++++++++++++++++++++++----------------------- 1 file changed, 250 insertions(+), 216 deletions(-) diff --git a/functions.php b/functions.php index 5e18daa..04deed2 100644 --- a/functions.php +++ b/functions.php @@ -1,23 +1,28 @@ testsuite; + $xml = simplexml_load_string( $xml_string ); // Parse the XML string. $results = array(); + $project = $xml->testsuite; $results = array( 'tests' => (string) $project['tests'], 'failures' => (string) $project['failures'], @@ -172,6 +205,7 @@ function process_junit_xml( $xml_string ) $results['testsuites'] = array(); + // XPath query to find test suites with failures or errors. $testsuites = $xml->xpath( '//testsuites//testsuite[ ( count( testcase ) > 0 ) and ( @errors > 0 or @failures > 0 ) ]' ); foreach ( $testsuites as $testsuite ) { $result = array( @@ -180,200 +214,200 @@ function process_junit_xml( $xml_string ) 'failures' => (string) $testsuite['failures'], 'errors' => (string) $testsuite['errors'] ); + + // Only include suites with failures or errors. if ( empty( $result['failures'] ) && empty( $result['errors'] ) ) { - continue; + continue; } + $failures = array(); foreach ( $testsuite->testcase as $testcase ) { // Capture both failure and error children. - foreach ( array( 'failure', 'error') as $key ) { + foreach ( array( 'failure', 'error' ) as $key ) { if ( isset( $testcase->{$key} ) ) { - $failures[ (string) $testcase['name'] ] = array( - 'name' => (string) $testcase['name'], - $key => (string) $testcase->{$key}, - ); - } + $failures[ (string) $testcase['name'] ] = array( + 'name' => (string) $testcase['name'], + $key => (string) $testcase->{$key}, + ); } } } + if ( $failures ) { $results['testsuites'][ (string) $testsuite['name'] ] = $result; $results['testsuites'][ (string) $testsuite['name'] ]['testcases'] = $failures; } } - return json_encode( $results ); + return json_encode( $results ); // Return the results as a JSON encoded string. } /** - * Submits test results along with associated metadata to a specified reporting API. The function constructs - * a POST request containing the test results, SVN revision, SVN message, environment data, and uses an API key - * for authentication. The reporting API's URL is retrieved from an environment variable; if not found, a default - * URL is used. This function is typically used to automate the reporting of test outcomes to a centralized system - * for analysis, tracking, and historical record-keeping. - * - * @param string $results The test results in a processed format (e.g., JSON) ready for submission to the reporting API. - * @param string $rev The SVN revision associated with the test results. This often corresponds to a specific code - * commit or build identifier. - * @param string $message The SVN commit message associated with the revision, providing context or notes about the changes. - * @param string $env The environment data in JSON format, detailing the conditions under which the tests were run, - * such as operating system, PHP version, etc. - * @param string $api_key The API key for authenticating with the reporting API, ensuring secure and authorized access. - * - * @return array An array containing two elements: the HTTP status code of the response (int) and the body of the response - * (string) from the reporting API. This can be used to verify successful submission or to handle errors. - * - * @uses curl_init(), curl_setopt(), and curl_exec() to perform the HTTP POST request to the reporting API. - * @uses json_encode() to encode the data payload as a JSON string for submission. - * @uses base64_encode() to encode the API key for HTTP Basic Authentication in the Authorization header. + * Submits test results along with associated metadata to a specified reporting + * API. The function constructs a POST request containing the test results, SVN + * revision, SVN message, environment data, and uses an API key for + * authentication. The reporting API's URL is retrieved from an environment + * variable; if not found, a default URL is used. This function is typically + * used to automate the reporting of test outcomes to a centralized system for + * analysis, tracking, and historical record-keeping. + * + * @param string $results The test results in a processed format (e.g., JSON) + * ready for submission to the reporting API. + * + * @param string $rev The SVN revision associated with the test results. This + * often corresponds to a specific code commit or build identifier. + * + * @param string $message The SVN commit message associated with the revision, + * providing context or notes about the changes. + * + * @param string $env The environment data in JSON format, detailing the + * conditions under which the tests were run, such as operating system, PHP + * version, etc. + * + * @param string $api_key The API key for authenticating with the reporting API, + * ensuring secure and authorized access. + * + * @return array An array containing two elements: the HTTP status code of the + * response (int) and the body of the response (string) from the reporting API. + * This can be used to verify successful submission or to handle errors. + * + * @uses curl_init(), curl_setopt(), and curl_exec() to perform the HTTP POST + * request to the reporting API. + * + * @uses json_encode() to encode the data payload as a JSON string for + * submission. + * + * @uses base64_encode() to encode the API key for HTTP Basic Authentication in + * the Authorization header. */ function upload_results( $results, $rev, $message, $env, $api_key ) { $WPT_REPORT_URL = getenv( 'WPT_REPORT_URL' ); if ( ! $WPT_REPORT_URL ) { - $WPT_REPORT_URL = 'https://make.wordpress.org/hosting/wp-json/wp-unit-test-api/v1/results'; + $WPT_REPORT_URL = 'https://make.wordpress.org/hosting/wp-json/wp-unit-test-api/v1/results'; // Default URL. } - $process = curl_init( $WPT_REPORT_URL ); - $access_token = base64_encode( $api_key ); + + $process = curl_init( $WPT_REPORT_URL ); // Initialize CURL. + $access_token = base64_encode( $api_key ); // Encode API key for Basic Auth. + $data = array( 'results' => $results, 'commit' => $rev, 'message' => $message, 'env' => $env, ); - $data_string = json_encode( $data ); + $data_string = json_encode( $data ); // Convert data to JSON format. + + // Set CURL options. curl_setopt( $process, CURLOPT_TIMEOUT, 30 ); curl_setopt( $process, CURLOPT_POST, 1 ); curl_setopt( $process, CURLOPT_CUSTOMREQUEST, 'POST' ); curl_setopt( $process, CURLOPT_POSTFIELDS, $data_string ); curl_setopt( $process, CURLOPT_RETURNTRANSFER, true ); curl_setopt( $process, CURLOPT_HTTPHEADER, array( - "Authorization: Basic $access_token", - 'Content-Type: application/json', - 'Content-Length: ' . strlen( $data_string ) + "Authorization: Basic $access_token", // Set Authorization header. + 'Content-Type: application/json', // Specify content type. + 'Content-Length: ' . strlen( $data_string ) // Set content length. )); - $return = curl_exec( $process ); - $status_code = curl_getinfo( $process, CURLINFO_HTTP_CODE ); - curl_close( $process ); + $return = curl_exec( $process ); // Execute the request. + $status_code = curl_getinfo( $process, CURLINFO_HTTP_CODE ); // Get the HTTP status code. + curl_close( $process ); // Close CURL session. - return array( $status_code, $return ); + return array( $status_code, $return ); // Return status code and response. } /** - * Collects and returns an array of key environment details relevant to the application's context. This includes - * the PHP version, installed PHP modules with their versions, system utilities like curl and OpenSSL versions, - * MySQL version, and operating system details. This function is useful for diagnostic purposes, ensuring - * compatibility, or for reporting system configurations in debugging or error logs. - * - * The function checks for the availability of specific PHP modules and system utilities and captures their versions. - * It uses shell commands to retrieve system information, which requires the PHP environment to have access to these - * commands and appropriate permissions. + * Collects and returns an array of key environment details relevant to the + * application's context. This includes the PHP version, installed PHP modules + * with their versions, system utilities like curl and OpenSSL versions, MySQL + * version, and operating system details. This function is useful for diagnostic + * purposes, ensuring compatibility, or for reporting system configurations in + * debugging or error logs. + * The function checks for the availability of specific PHP modules and system + * utilities and captures their versions. It uses shell commands to retrieve + * system information, which requires the PHP environment to have access to + * these commands and appropriate permissions. * * @return array An associative array containing detailed environment information. The array includes: - * - 'php_version': The current PHP version. - * - 'php_modules': An associative array of selected PHP modules and their versions. - * - 'system_utils': Versions of certain system utilities such as 'curl', 'imagemagick', 'graphicsmagick', and 'openssl'. - * - 'mysql_version': The version of MySQL installed. - * - 'os_name': The name of the operating system. - * - 'os_version': The version of the operating system. + * - 'php_version': The current PHP version. + * - 'php_modules': An associative array of selected PHP modules and their versions. + * - 'system_utils': Versions of certain system utilities such as 'curl', 'imagemagick', 'graphicsmagick', and 'openssl'. + * - 'mysql_version': The version of MySQL installed. + * - 'os_name': The name of the operating system. + * - 'os_version': The version of the operating system. * * @uses phpversion() to get the PHP version and module versions. + * * @uses shell_exec() to execute system commands for retrieving MySQL version, OS details, and versions of utilities like curl and OpenSSL. + * * @uses class_exists() to check for the availability of the Imagick and Gmagick classes for version detection. */ function get_env_details() { - $gd_info = array(); - if( extension_loaded( 'gd' ) ) { - $gd_info = gd_info(); + if ( extension_loaded( 'gd' ) ) { + $gd_info = gd_info(); // Get GD info if the GD extension is loaded. } + $imagick_info = array(); - if( extension_loaded( 'imagick' ) ) { - $imagick_info = Imagick::queryFormats(); + if ( extension_loaded( 'imagick' ) ) { + $imagick_info = Imagick::queryFormats(); // Get Imagick info if the Imagick extension is loaded. } $env = array( - 'php_version' => phpversion(), - 'php_modules' => array(), - 'gd_info' => $gd_info, - 'imagick_info' => $imagick_info, - 'mysql_version' => trim( shell_exec( 'mysql --version' ) ), - 'system_utils' => array(), - 'os_name' => trim( shell_exec( 'uname -s' ) ), - 'os_version' => trim( shell_exec( 'uname -r' ) ), + 'php_version' => phpversion(), // Current PHP version. + 'php_modules' => array(), + 'gd_info' => $gd_info, + 'imagick_info' => $imagick_info, + 'mysql_version' => '', // Will be set later if MySQL is available. + 'system_utils' => array(), + 'os_name' => trim( shell_exec( 'uname -s' ) ), // OS name. + 'os_version' => trim( shell_exec( 'uname -r' ) ), // OS version. ); - unset( $gd_info, $imagick_info ); $php_modules = array( - 'bcmath', - 'ctype', - 'curl', - 'date', - 'dom', - 'exif', - 'fileinfo', - 'filter', - 'ftp', - 'gd', - 'gettext', - 'gmagick', - 'hash', - 'iconv', - 'imagick', - 'imap', - 'intl', - 'json', - 'libsodium', - 'libxml', - 'mbstring', - 'mcrypt', - 'mod_xml', - 'mysqli', - 'mysqlnd', - 'openssl', - 'pcre', - 'pdo_mysql', - 'soap', - 'sockets', - 'sodium', - 'xml', - 'xmlreader', - 'zip', - 'zlib', + 'bcmath', 'ctype', 'curl', 'date', 'dom', 'exif', 'fileinfo', 'filter', + 'ftp', 'gd', 'gettext', 'gmagick', 'hash', 'iconv', 'imagick', 'imap', + 'intl', 'json', 'libsodium', 'libxml', 'mbstring', 'mcrypt', 'mod_xml', + 'mysqli', 'mysqlnd', 'openssl', 'pcre', 'pdo_mysql', 'soap', 'sockets', + 'sodium', 'xml', 'xmlreader', 'zip', 'zlib', ); - foreach( $php_modules as $php_module ) { - $env['php_modules'][ $php_module ] = phpversion( $php_module ); + + foreach ( $php_modules as $php_module ) { + $env['php_modules'][ $php_module ] = phpversion( $php_module ); // Get version for each PHP module. } - function curl_selected_bits($k) { return in_array($k, array('version', 'ssl_version', 'libz_version')); } - $curl_bits = curl_version(); - $env['system_utils']['curl'] = implode(' ',array_values(array_filter($curl_bits, 'curl_selected_bits',ARRAY_FILTER_USE_KEY) )); + $curl_bits = curl_version(); // Get CURL version info. + $env['system_utils']['curl'] = implode(' ', array_values(array_filter($curl_bits, function($k) { + return in_array($k, array('version', 'ssl_version', 'libz_version')); // Filter curl version info. + }, ARRAY_FILTER_USE_KEY))); - $WPT_DB_HOST = trim( getenv( 'WPT_DB_HOST' ) ); - if( ! $WPT_DB_HOST ) { - $WPT_DB_HOST = 'localhost'; - } - $WPT_DB_USER = trim( getenv( 'WPT_DB_USER' ) ); - $WPT_DB_PASSWORD = trim( getenv( 'WPT_DB_PASSWORD' ) ); - $WPT_DB_NAME = trim( getenv( 'WPT_DB_NAME' ) ); + // Check for MySQL version if connection details are available. + $WPT_DB_HOST = trim( getenv( 'WPT_DB_HOST' ) ?: 'localhost' ); // Default to localhost if not set. + $WPT_DB_USER = trim( getenv( 'WPT_DB_USER' ) ); + $WPT_DB_PASSWORD = trim( getenv( 'WPT_DB_PASSWORD' ) ); + $WPT_DB_NAME = trim( getenv( 'WPT_DB_NAME' ) ); - //$mysqli = new mysqli( $WPT_DB_HOST, $WPT_DB_USER, $WPT_DB_PASSWORD, $WPT_DB_NAME ); - //$env['mysql_version'] = $mysqli->query("SELECT VERSION()")->fetch_row()[0]; - //$mysqli->close(); + if ( class_exists( 'mysqli' ) ) { + $mysqli = new mysqli( $WPT_DB_HOST, $WPT_DB_USER, $WPT_DB_PASSWORD, $WPT_DB_NAME ); + if ( ! $mysqli->connect_error ) { + $env['mysql_version'] = $mysqli->query("SELECT VERSION()")->fetch_row()[0]; // Get MySQL version. + $mysqli->close(); + } + } if ( class_exists( 'Imagick' ) ) { $imagick = new Imagick(); $version = $imagick->getVersion(); - preg_match( '/Magick (\d+\.\d+\.\d+-\d+|\d+\.\d+\.\d+|\d+\.\d+\-\d+|\d+\.\d+)/', $version['versionString'], $version ); - $env['system_utils']['imagemagick'] = $version[1]; + preg_match( '/Magick (\d+\.\d+\.\d+-\d+|\d+\.\d+\.\d+|\d+\.\d+\-\d+|\d+\.\d+)/', $version['versionString'], $matches ); + $env['system_utils']['imagemagick'] = $matches[1]; // Get Imagick version. } elseif ( class_exists( 'Gmagick' ) ) { $gmagick = new Gmagick(); $version = $gmagick->getversion(); - preg_match( '/Magick (\d+\.\d+\.\d+-\d+|\d+\.\d+\.\d+|\d+\.\d+\-\d+|\d+\.\d+)/', $version['versionString'], $version ); - $env['system_utils']['graphicsmagick'] = $version[1]; + preg_match( '/Magick (\d+\.\d+\.\d+-\d+|\d+\.\d+\.\d+|\d+\.\d+\-\d+|\d+\.\d+)/', $version['versionString'], $matches ); + $env['system_utils']['graphicsmagick'] = $matches[1]; // Get GraphicsMagick version. } - $env['system_utils']['openssl'] = str_replace( 'OpenSSL ', '', trim( shell_exec( 'openssl version' ) ) ); + $env['system_utils']['openssl'] = str_replace( 'OpenSSL ', '', trim( shell_exec( 'openssl version' ) ) ); // Get OpenSSL version. - return $env; + return $env; // Return the collected environment details. } From 070c9d51e66d68081a552ad96f52d81b0b848b9f Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Sat, 28 Sep 2024 20:56:01 +0200 Subject: [PATCH 26/30] Improving and normalizing code. - Better comments / PHPDoc - Improved code quality (do the same, in a better way) - reduce duplicated code (that's something we didn't care at Cloudfest) - updated the Runner Compatibity with PHP 7.2 (as WordPress is) --- .env.default | 32 +- README.md | 2 +- cleanup.php | 152 ++++----- functions.php | 50 +-- prepare.php | 851 ++++++++++++++++---------------------------------- report.php | 434 +++++++------------------ test.php | 127 ++++---- 7 files changed, 543 insertions(+), 1105 deletions(-) diff --git a/.env.default b/.env.default index f9357f6..ca64b7d 100644 --- a/.env.default +++ b/.env.default @@ -13,28 +13,28 @@ # Label for the environment. Can be empty (default) or be like "shared", "vps", "cloud" or similar. # Please use only alphanumeric keywords, and try to be descriptive -export WPT_LABEL= +export WPT_LABEL="" # Path to the directory where files can be prepared before being delivered to the environment. -export WPT_PREPARE_DIR=/tmp/wp-test-runner +export WPT_PREPARE_DIR="/tmp/wp-test-runner" # Path to the directory where the WordPress develop checkout can be placed and tests can be run. # When running tests in the same environment, set WPT_TEST_DIR to WPT_PREPARE_DIR -export WPT_TEST_DIR=/tmp/wp-test-runner +export WPT_TEST_DIR="/tmp/wp-test-runner" # API key to authenticate with the reporting service in 'username:password' format. -export WPT_REPORT_API_KEY= +export WPT_REPORT_API_KEY="" # (Optionally) define an alternate reporting URL -export WPT_REPORT_URL= +export WPT_REPORT_URL="" # Credentials for a database that can be written to and reset. # WARNING!!! This database will be destroyed between tests. Only use safe database credentials. # Please note that you must escape _or_ refrain from using # as special character in your credentials. -export WPT_DB_NAME= -export WPT_DB_USER= -export WPT_DB_PASSWORD= -export WPT_DB_HOST= +export WPT_DB_NAME="" +export WPT_DB_USER="" +export WPT_DB_PASSWORD="" +export WPT_DB_HOST="" # (Optionally) set a custom table prefix to permit concurrency against the same database. export WPT_TABLE_PREFIX=${WPT_TABLE_PREFIX-wptests_} @@ -43,30 +43,30 @@ export WPT_TABLE_PREFIX=${WPT_TABLE_PREFIX-wptests_} export WPT_PHP_EXECUTABLE=${WPT_PHP_EXECUTABLE-php} # (Optionally) array of versions (like: 8.0=/bin/php8.0;8.1=/bin/php8.1) -export WPT_PHP_EXECUTABLE_MULTI= +export WPT_PHP_EXECUTABLE_MULTI="" # (Optionally) define the PHPUnit command execution call. # Use if `php phpunit.phar` can't be called directly for some reason. -export WPT_PHPUNIT_CMD= +export WPT_PHPUNIT_CMD="" # (Optionally) define the command execution to remove the test directory # Use if `rm -r` can't be called directly for some reason. -export WPT_RM_TEST_DIR_CMD= +export WPT_RM_TEST_DIR_CMD="" # SSH connection string (can also be an alias). # Leave empty if tests are meant to run in the same environment. -export WPT_SSH_CONNECT= +export WPT_SSH_CONNECT="" # Any options to be passed to the SSH connection # Defaults to '-o StrictHostKeyChecking=no' -export WPT_SSH_OPTIONS= +export WPT_SSH_OPTIONS="" # SSH private key, base64 encoded. -export WPT_SSH_PRIVATE_KEY_BASE64= +export WPT_SSH_PRIVATE_KEY_BASE64="" # Output logging # Use 'verbose' to increase verbosity -export WPT_DEBUG= +export WPT_DEBUG="" # Certificate validation # Use 1 to validate, and 0 to not validate diff --git a/README.md b/README.md index 046dfad..384f7a7 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ To use the Runner, the following is required (testing WordPress 6.5): - Server / hosting (infrastructure) with the usual configuration you use - A database where you can test (tables will be created and destroyed several times) -- PHP 7.0+ (view ) +- PHP 7.2+ - MySQL 5.5+ / MariaDB 10.0+ - NodeJS 20.x / npm 10.x / grunt - PHP Composer diff --git a/cleanup.php b/cleanup.php index b0d74b5..11e898c 100644 --- a/cleanup.php +++ b/cleanup.php @@ -1,117 +1,97 @@ trim( $php_multi_v[0] ), 'bin' => trim( $php_multi_v[1] ) ); +$WPT_PHP_EXECUTABLE_MULTI_ARRAY = []; +if ( ! empty( $WPT_PHP_EXECUTABLE_MULTI ) ) { + + // Divide the version string by semicolon + $php_multi_versions = explode( ';', $WPT_PHP_EXECUTABLE_MULTI ); + + foreach ( $php_multi_versions as $php_multi_version ) { + // Divide each version by the equals sign and apply trim to each part + $parts = array_map( 'trim', explode( '=', $php_multi_version, 2 ) ); + + // Ensures that both parts exist and are not empty. + if ( 2 === count( $parts ) && '' !== $parts[0] && '' !== $parts[1] ) { + [ $version, $bin ] = $parts; + $WPT_PHP_EXECUTABLE_MULTI_ARRAY[] = [ + 'version' => $version, + 'bin' => $bin + ]; } - - unset( $php_multi_version ); } - - unset( $php_multi_versions ); } -if( count( $WPT_PHP_EXECUTABLE_MULTI_ARRAY ) ) { - - foreach( $WPT_PHP_EXECUTABLE_MULTI_ARRAY as $php_multi ) { - - $WPT_PREPARE_DIR_MULTI = $WPT_PREPARE_DIR . '-' . crc32( $php_multi['version'] ); - $WPT_TEST_DIR_MULTI = $WPT_TEST_DIR . '-' . crc32( $php_multi['version'] ); - - $WPT_RM_TEST_DIR_CMD = trim( getenv( 'WPT_RM_TEST_DIR_CMD' ) ) ? : 'rm -r ' . $WPT_TEST_DIR_MULTI; +// Prepare an array of PHP executables. If multi-PHP is configured, use the multi-array; otherwise, use a single executable. +$php_executables = ! empty( $WPT_PHP_EXECUTABLE_MULTI_ARRAY ) ? $WPT_PHP_EXECUTABLE_MULTI_ARRAY : [ + [ + 'version' => 'default', + 'bin' => $WPT_PHP_EXECUTABLE, // Ensure this variable is defined for the single PHP executable case. + ] +]; - /** - * The directory path of the test preparation directory is assumed to be previously defined. - * For example: $WPT_PREPARE_DIR = '/path/to/your/preparation/dir'; - * Clean up the preparation directory. - * Forcefully deletes only the .git directory and the node_modules cache. - * Afterward, the entire preparation directory is removed to ensure a clean state for the next test run. - */ - perform_operations( array( - 'rm -rf ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI . '/.git' ), - 'rm -rf ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI . '/node_modules/.cache' ), - 'rm -r ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ), - ) ); - - /** - * Cleans up the test directory on a remote server. - * This conditional block checks if an SSH connection string is provided and is not empty. - * If a connection string is present, it triggers a cleanup operation on the remote environment. - * The cleanup operation is executed by the `perform_operations` function which takes an array - * of shell commands as its input. - */ - if ( ! empty( $WPT_SSH_CONNECT ) ) { - perform_operations( array( - 'ssh ' . $WPT_SSH_OPTIONS . ' ' . escapeshellarg( $WPT_SSH_CONNECT ) . ' ' . escapeshellarg( $WPT_RM_TEST_DIR_CMD ), - ) ); - } - - } - -} else { +/** + * Performs a series of operations to clean up the test environment. + * This includes deleting specific directories and, if provided, cleaning up + * remote directories via SSH. + */ +foreach ( $php_executables as $php_multi ) { + // Generate unique directory names based on the PHP version. + $WPT_PREPARE_DIR_MULTI = $WPT_PREPARE_DIR . '-' . crc32( $php_multi['version'] ); + $WPT_TEST_DIR_MULTI = $WPT_TEST_DIR . '-' . crc32( $php_multi['version'] ); - $WPT_RM_TEST_DIR_CMD = trim( getenv( 'WPT_RM_TEST_DIR_CMD' ) ) ? : 'rm -r ' . $WPT_TEST_DIR; + // Determines the command to remove the test directory, based on an environment variable or a default value. + $WPT_RM_TEST_DIR_CMD = trim( getenv( 'WPT_RM_TEST_DIR_CMD' ) ) ?: 'rm -r ' . $WPT_TEST_DIR_MULTI; /** - * The directory path of the test preparation directory is assumed to be previously defined. - * For example: $WPT_PREPARE_DIR = '/path/to/your/preparation/dir'; - * Clean up the preparation directory. - * Forcefully deletes only the .git directory and the node_modules cache. - * Afterward, the entire preparation directory is removed to ensure a clean state for the next test run. - */ - perform_operations( array( - 'rm -rf ' . escapeshellarg( $WPT_PREPARE_DIR . '/.git' ), - 'rm -rf ' . escapeshellarg( $WPT_PREPARE_DIR . '/node_modules/.cache' ), - 'rm -r ' . escapeshellarg( $WPT_PREPARE_DIR ), - ) ); + * Forcibly removes only the .git directory and the node_modules cache. + * Then, remove the entire staging directory to ensure a clean state for the next test run. + */ + perform_operations([ + 'rm -rf ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI . '/.git' ), + 'rm -rf ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI . '/node_modules/.cache' ), + 'rm -r ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ), + ]); /** - * Cleans up the test directory on a remote server. - * This conditional block checks if an SSH connection string is provided and is not empty. - * If a connection string is present, it triggers a cleanup operation on the remote environment. - * The cleanup operation is executed by the `perform_operations` function which takes an array - * of shell commands as its input. - */ + * Clears the test directory on a remote server if an SSH connection string is provided. + * This conditional block checks if the WPT_SSH_CONNECT environment variable is set. + * If so, it executes the remote cleanup command using SSH. + */ if ( ! empty( $WPT_SSH_CONNECT ) ) { - perform_operations( array( + perform_operations([ 'ssh ' . $WPT_SSH_OPTIONS . ' ' . escapeshellarg( $WPT_SSH_CONNECT ) . ' ' . escapeshellarg( $WPT_RM_TEST_DIR_CMD ), - ) ); + ]); } - -} \ No newline at end of file +} diff --git a/functions.php b/functions.php index 04deed2..f78f561 100644 --- a/functions.php +++ b/functions.php @@ -23,14 +23,14 @@ * @uses log_message() to log a success message when all checks pass. */ function check_required_env( $check_db = true ) { - $required = array( + $required = [ 'WPT_PREPARE_DIR', 'WPT_TEST_DIR', 'WPT_DB_NAME', 'WPT_DB_USER', 'WPT_DB_PASSWORD', 'WPT_DB_HOST', - ); + ]; foreach ( $required as $var ) { if ( false === $check_db && 0 === strpos( $var, 'WPT_DB_' ) ) { @@ -193,42 +193,42 @@ function process_junit_xml( $xml_string ) { } $xml = simplexml_load_string( $xml_string ); // Parse the XML string. - $results = array(); + $results = []; $project = $xml->testsuite; - $results = array( + $results = [ 'tests' => (string) $project['tests'], 'failures' => (string) $project['failures'], 'errors' => (string) $project['errors'], 'time' => (string) $project['time'], - ); + ]; - $results['testsuites'] = array(); + $results['testsuites'] = []; // XPath query to find test suites with failures or errors. $testsuites = $xml->xpath( '//testsuites//testsuite[ ( count( testcase ) > 0 ) and ( @errors > 0 or @failures > 0 ) ]' ); foreach ( $testsuites as $testsuite ) { - $result = array( + $result = [ 'name' => (string) $testsuite['name'], 'tests' => (string) $testsuite['tests'], 'failures' => (string) $testsuite['failures'], 'errors' => (string) $testsuite['errors'] - ); + ]; // Only include suites with failures or errors. if ( empty( $result['failures'] ) && empty( $result['errors'] ) ) { continue; } - $failures = array(); + $failures = []; foreach ( $testsuite->testcase as $testcase ) { // Capture both failure and error children. - foreach ( array( 'failure', 'error' ) as $key ) { + foreach ( [ 'failure', 'error' ] as $key ) { if ( isset( $testcase->{$key} ) ) { - $failures[ (string) $testcase['name'] ] = array( + $failures[ (string) $testcase['name'] ] = [ 'name' => (string) $testcase['name'], $key => (string) $testcase->{$key}, - ); } + ]; } } } @@ -288,12 +288,12 @@ function upload_results( $results, $rev, $message, $env, $api_key ) { $process = curl_init( $WPT_REPORT_URL ); // Initialize CURL. $access_token = base64_encode( $api_key ); // Encode API key for Basic Auth. - $data = array( + $data = [ 'results' => $results, 'commit' => $rev, 'message' => $message, 'env' => $env, - ); + ]; $data_string = json_encode( $data ); // Convert data to JSON format. @@ -303,17 +303,17 @@ function upload_results( $results, $rev, $message, $env, $api_key ) { curl_setopt( $process, CURLOPT_CUSTOMREQUEST, 'POST' ); curl_setopt( $process, CURLOPT_POSTFIELDS, $data_string ); curl_setopt( $process, CURLOPT_RETURNTRANSFER, true ); - curl_setopt( $process, CURLOPT_HTTPHEADER, array( + curl_setopt( $process, CURLOPT_HTTPHEADER, [ "Authorization: Basic $access_token", // Set Authorization header. 'Content-Type: application/json', // Specify content type. 'Content-Length: ' . strlen( $data_string ) // Set content length. - )); + ]); $return = curl_exec( $process ); // Execute the request. $status_code = curl_getinfo( $process, CURLINFO_HTTP_CODE ); // Get the HTTP status code. curl_close( $process ); // Close CURL session. - return array( $status_code, $return ); // Return status code and response. + return [ $status_code, $return ]; // Return status code and response. } /** @@ -343,17 +343,17 @@ function upload_results( $results, $rev, $message, $env, $api_key ) { * @uses class_exists() to check for the availability of the Imagick and Gmagick classes for version detection. */ function get_env_details() { - $gd_info = array(); + $gd_info = []; if ( extension_loaded( 'gd' ) ) { $gd_info = gd_info(); // Get GD info if the GD extension is loaded. } - $imagick_info = array(); + $imagick_info = []; if ( extension_loaded( 'imagick' ) ) { $imagick_info = Imagick::queryFormats(); // Get Imagick info if the Imagick extension is loaded. } - $env = array( + $env = [ 'php_version' => phpversion(), // Current PHP version. 'php_modules' => array(), 'gd_info' => $gd_info, @@ -362,23 +362,23 @@ function get_env_details() { 'system_utils' => array(), 'os_name' => trim( shell_exec( 'uname -s' ) ), // OS name. 'os_version' => trim( shell_exec( 'uname -r' ) ), // OS version. - ); + ]; - $php_modules = array( + $php_modules = [ 'bcmath', 'ctype', 'curl', 'date', 'dom', 'exif', 'fileinfo', 'filter', 'ftp', 'gd', 'gettext', 'gmagick', 'hash', 'iconv', 'imagick', 'imap', 'intl', 'json', 'libsodium', 'libxml', 'mbstring', 'mcrypt', 'mod_xml', 'mysqli', 'mysqlnd', 'openssl', 'pcre', 'pdo_mysql', 'soap', 'sockets', 'sodium', 'xml', 'xmlreader', 'zip', 'zlib', - ); + ]; foreach ( $php_modules as $php_module ) { $env['php_modules'][ $php_module ] = phpversion( $php_module ); // Get version for each PHP module. } $curl_bits = curl_version(); // Get CURL version info. - $env['system_utils']['curl'] = implode(' ', array_values(array_filter($curl_bits, function($k) { - return in_array($k, array('version', 'ssl_version', 'libz_version')); // Filter curl version info. + $env['system_utils']['curl'] = implode( ' ', array_values( array_filter( $curl_bits, function( $k ) { + return in_array( $k, [ 'version', 'ssl_version', 'libz_version'] ); // Filter curl version info. }, ARRAY_FILTER_USE_KEY))); // Check for MySQL version if connection details are available. diff --git a/prepare.php b/prepare.php index 5e8c917..6e91851 100644 --- a/prepare.php +++ b/prepare.php @@ -1,255 +1,219 @@ $commithash) { - if (!in_array($commithash, $WPT_COMMIT)) { - unset($executed_commits[$key]); - } - unset( $key, $commithash ); - } + $testing_commit = $c_array['testing_commit'] ?? null; + $executed_commits = $c_array['executed_commits'] ?? []; + $pending_commits = $c_array['pending_commits'] ?? []; - foreach ($pending_commits as $key => $commithash) { - if (!in_array($commithash, $WPT_COMMIT)) { - unset($pending_commits[$key]); - } - unset( $key, $commithash ); - } + // Filter 'executed_commits' to include only commits present in $WPT_COMMIT + $executed_commits = array_values( array_intersect( $executed_commits, $WPT_COMMIT ) ); - // Remove duplicates from pending_commits that are in executed_commits or are the testing_commit + // Filter 'pending_commits' to include only commits present in $WPT_COMMIT + $pending_commits = array_values( array_intersect( $pending_commits, $WPT_COMMIT ) ); + + // Remove from 'pending_commits' the commits that are already in 'executed_commits' or are the 'testing_commit'. $pending_commits = array_filter( $pending_commits, function( $commithash ) use ( $executed_commits, $testing_commit ) { - return !in_array( $commithash, $executed_commits ) && $commithash !== $testing_commit; + return ! in_array( $commithash, $executed_commits, true ) && $commithash !== $testing_commit; }); - foreach ($WPT_COMMIT as $commithash) { - if (!in_array($commithash, $pending_commits) && !in_array($commithash, $executed_commits)) { + // Add new commits to 'pending_commits' that are neither in 'executed_commits' nor in 'pending_commits'. + $new_commits = array_diff( $WPT_COMMIT, $executed_commits, $pending_commits ); + $pending_commits = array_merge( $pending_commits, $new_commits ); - if( ! count( $pending_commits ) ) { - - array_unshift($pending_commits, $commithash); - } else { - - array_push($pending_commits, $commithash); - } - - } - unset( $commithash ); - } - - $c = array( 'executed_commits' => $executed_commits, 'pending_commits' => $pending_commits, 'testing_commit' => $testing_commit ); - - unset( $executed_commits, $pending_commits, $testing_commit ); - - $c_json = json_encode( $c ); - - file_put_contents( __DIR__ . '/commits.json', $c_json ); - - unset( $c, $c_json ); + // Reindex the array of 'pending_commits'. + $pending_commits = array_values( $pending_commits ); + // Prepare the final array to encode to JSON + $c = [ + 'executed_commits' => $executed_commits, + 'pending_commits' => $pending_commits, + 'testing_commit' => $testing_commit, + ]; } else { - - $c = array( 'executed_commits' => array(), 'pending_commits' => $WPT_COMMIT, 'testing_commit' => $testing_commit ); - - $c_json = json_encode( $c ); - - file_put_contents( __DIR__ . '/commits.json', $c_json ); - - unset( $c, $c_json ); + // If the JSON file does not exist, initialize the default values. + $c = [ + 'executed_commits' => [], + 'pending_commits' => $WPT_COMMIT, + 'testing_commit' => null, + ]; } + // Encodes the array to JSON and saves it to the file + file_put_contents( $commitsFile, json_encode( $c, JSON_PRETTY_PRINT ) ); } /** + * Parses the multi PHP executable versions and populates the + * WPT_PHP_EXECUTABLE_MULTI_ARRAY. */ -$WPT_PHP_EXECUTABLE_MULTI_ARRAY = array(); -if ( '' !== $WPT_PHP_EXECUTABLE_MULTI ) { +$WPT_PHP_EXECUTABLE_MULTI_ARRAY = []; +if ( ! empty( $WPT_PHP_EXECUTABLE_MULTI ) ) { + // Divide the version string by semicolon $php_multi_versions = explode( ';', $WPT_PHP_EXECUTABLE_MULTI ); - foreach( $php_multi_versions as $php_multi_version ) { - - $php_multi_v = explode( '=', $php_multi_version ); - - if( isset( $php_multi_v[0] ) && $php_multi_v[0] && isset( $php_multi_v[1] ) && $php_multi_v[1] ) { - $WPT_PHP_EXECUTABLE_MULTI_ARRAY[] = array( 'version' => trim( $php_multi_v[0] ), 'bin' => trim( $php_multi_v[1] ) ); + foreach ( $php_multi_versions as $php_multi_version ) { + // Divide each version by the equals sign and apply trim to each part + $parts = array_map( 'trim', explode( '=', $php_multi_version, 2 ) ); + + // Ensures that both parts exist and are not empty. + if ( 2 === count( $parts ) && '' !== $parts[0] && '' !== $parts[1] ) { + [ $version, $bin ] = $parts; + $WPT_PHP_EXECUTABLE_MULTI_ARRAY[] = [ + 'version' => $version, + 'bin' => $bin + ]; } - - unset( $php_multi_version ); } - - unset( $php_multi_versions ); } /** * Sets up the SSH private key for use in the test environment if provided. - * The private key is expected to be in base64-encoded form in the environment variable 'WPT_SSH_PRIVATE_KEY_BASE64'. - * It is decoded and saved to the user's .ssh directory as 'id_rsa'. - * Proper permissions are set on the private key to secure it. - * If an SSH connection string is provided, it performs a remote operation to ensure the WP CLI is accessible. - * Otherwise, it performs a local operation to check the WP CLI. - * - * @throws Exception If there is an issue creating the .ssh directory or writing the key file. */ -// Set the SSH private key if it's provided in the environment. $WPT_SSH_PRIVATE_KEY_BASE64 = trim( getenv( 'WPT_SSH_PRIVATE_KEY_BASE64' ) ); -// Check if the private key variable is not empty. +// Check if the private key variable is not empty if ( ! empty( $WPT_SSH_PRIVATE_KEY_BASE64 ) ) { - // Log the action of securely extracting the private key. + // Log the action of securely extracting the private key log_message( 'Securely extracting WPT_SSH_PRIVATE_KEY_BASE64 into ~/.ssh/id_rsa' ); - // Check if the .ssh directory exists in the home directory, and create it if it does not. - if ( ! is_dir( getenv( 'HOME' ) . '/.ssh' ) ) { - // The mkdir function creates the directory with the specified permissions and the recursive flag set to true. - mkdir( getenv( 'HOME' ) . '/.ssh', 0777, true ); + // Define the .ssh directory path + $SSHDIR = getenv('HOME') . '/.ssh'; + + // Check if the .ssh directory exists, create it if it does not + if ( ! is_dir( $SSHDIR ) ) { + mkdir( $SSHDIR, 0777, true ); } - // Write the decoded private key into the id_rsa file within the .ssh directory. - file_put_contents( getenv( 'HOME' ) . '/.ssh/id_rsa', base64_decode( $WPT_SSH_PRIVATE_KEY_BASE64 ) ); - - // Define the array of operations to perform, depending on the SSH connection availability. - // If no SSH connection string is provided, add a local operation to the array. - // If an SSH connection string is provided, add a remote operation to the array. - // Execute the operations defined in the operations array. - if( empty( $WPT_SSH_CONNECT ) ) { - perform_operations( array( - 'chmod 600 ~/.ssh/id_rsa', - 'wp cli info' - ) ); + // Decode the base64-encoded private key and write it to the id_rsa file + file_put_contents( $SSHDIR . '/id_rsa', base64_decode( $WPT_SSH_PRIVATE_KEY_BASE64 ) ); + + // Define the array of operations to perform + $operations = [ + 'chmod 600 ~/.ssh/id_rsa' + ]; + + if ( empty( $WPT_SSH_CONNECT ) ) { + // Add local operation if no SSH connection string is provided + $operations[] = 'wp cli info'; } else { - perform_operations( array( - 'chmod 600 ~/.ssh/id_rsa', - 'ssh -q ' . $WPT_SSH_OPTIONS . ' ' . escapeshellarg( $WPT_SSH_CONNECT ) . ' wp cli info' - ) ); + // Add remote operation if SSH connection string is provided + $operations[] = 'ssh -q ' . $WPT_SSH_OPTIONS . ' ' . escapeshellarg( $WPT_SSH_CONNECT ) . ' wp cli info'; } + // Execute the defined operations + perform_operations( $operations ); } /** - * Don't validate the TLS certificate - * Useful for local environments + * Don't validate the TLS certificate. Useful for local environments. */ +// Initialize the certificate validation flag $certificate_validation = ''; -if( ! $WPT_CERTIFICATE_VALIDATION ) { - $certificate_validation .= ' --no-check-certificate'; +// Append the no-check-certificate option if certificate validation is disabled +if ( ! $WPT_CERTIFICATE_VALIDATION ) { + $certificate_validation = ' --no-check-certificate'; } /** @@ -263,91 +227,68 @@ */ $system_logger = << '{$WPT_LABEL}', +// Initialize GD and Imagick info +\$gd_info = extension_loaded('gd') ? gd_info() : []; +\$imagick_info = extension_loaded('imagick') ? Imagick::queryFormats() : []; +// Collect environment details +\$env = [ + 'label' => '{$WPT_LABEL}', 'php_version' => phpversion(), - 'php_modules' => array(), + 'php_modules' => [], 'gd_info' => \$gd_info, 'imagick_info' => \$imagick_info, - 'mysql_version' => trim( shell_exec( 'mysql --version' ) ), - 'system_utils' => array(), - 'os_name' => trim( shell_exec( 'uname -s' ) ), - 'os_version' => trim( shell_exec( 'uname -r' ) ), -); -\$php_modules = array( - 'bcmath', - 'ctype', - 'curl', - 'date', - 'dom', - 'exif', - 'fileinfo', - 'filter', - 'ftp', - 'gd', - 'gettext', - 'gmagick', - 'hash', - 'iconv', - 'imagick', - 'imap', - 'intl', - 'json', - 'libsodium', - 'libxml', - 'mbstring', - 'mcrypt', - 'mod_xml', - 'mysqli', - 'mysqlnd', - 'openssl', - 'pcre', - 'pdo_mysql', - 'soap', - 'sockets', - 'sodium', - 'xml', - 'xmlreader', - 'zip', - 'zlib', -); -foreach( \$php_modules as \$php_module ) { - \$env['php_modules'][ \$php_module ] = phpversion( \$php_module ); + 'mysql_version' => trim(shell_exec('mysql --version')), + 'system_utils' => [], + 'os_name' => trim(shell_exec('uname -s')), + 'os_version' => trim(shell_exec('uname -r')), +]; +// List of PHP modules to check +\$php_modules = [ + 'bcmath', 'ctype', 'curl', 'date', 'dom', 'exif', 'fileinfo', 'filter', + 'ftp', 'gd', 'gettext', 'gmagick', 'hash', 'iconv', 'imagick', 'imap', + 'intl', 'json', 'libsodium', 'libxml', 'mbstring', 'mcrypt', 'mod_xml', + 'mysqli', 'mysqlnd', 'openssl', 'pcre', 'pdo_mysql', 'soap', 'sockets', + 'sodium', 'xml', 'xmlreader', 'zip', 'zlib', +]; +// Populate PHP modules versions +foreach (\$php_modules as \$php_module) { + \$env['php_modules'][\$php_module] = phpversion(\$php_module); } -function curl_selected_bits(\$k) { return in_array(\$k, array('version', 'ssl_version', 'libz_version')); } +// Helper function to filter cURL bits +function curl_selected_bits(\$key) { + return in_array(\$key, ['version', 'ssl_version', 'libz_version']); +} +// Collect cURL version info \$curl_bits = curl_version(); -\$env['system_utils']['curl'] = implode(' ',array_values(array_filter(\$curl_bits, 'curl_selected_bits',ARRAY_FILTER_USE_KEY) )); -if ( class_exists( 'Imagick' ) ) { +\$env['system_utils']['curl'] = implode(' ', array_values(array_filter(\$curl_bits, 'curl_selected_bits', ARRAY_FILTER_USE_KEY))); +// Collect Imagick or Gmagick version info +if (class_exists('Imagick')) { \$imagick = new Imagick(); \$version = \$imagick->getVersion(); - preg_match( '/Magick (\d+\.\d+\.\d+-\d+|\d+\.\d+\.\d+|\d+\.\d+\-\d+|\d+\.\d+)/', \$version['versionString'], \$version ); - \$env['system_utils']['imagemagick'] = \$version[1]; -} elseif ( class_exists( 'Gmagick' ) ) { + preg_match('/Magick (\d+\.\d+\.\d+-\d+|\d+\.\d+\.\d+|\d+\.\d+\-\d+|\d+\.\d+)/', \$version['versionString'], \$matches); + \$env['system_utils']['imagemagick'] = \$matches[1] ?? 'Unknown'; +} elseif (class_exists('Gmagick')) { \$gmagick = new Gmagick(); - \$version = \$gmagick->getversion(); - preg_match( '/Magick (\d+\.\d+\.\d+-\d+|\d+\.\d+\.\d+|\d+\.\d+\-\d+|\d+\.\d+)/', \$version['versionString'], \$version ); - \$env['system_utils']['graphicsmagick'] = \$version[1]; + \$version = \$gmagick->getVersion(); + preg_match('/Magick (\d+\.\d+\.\d+-\d+|\d+\.\d+\.\d+|\d+\.\d+\-\d+|\d+\.\d+)/', \$version['versionString'], \$matches); + \$env['system_utils']['graphicsmagick'] = \$matches[1] ?? 'Unknown'; } -\$env['system_utils']['openssl'] = str_replace( 'OpenSSL ', '', trim( shell_exec( 'openssl version' ) ) ); -//\$mysqli = new mysqli( WPT_DB_HOST, WPT_DB_USER, WPT_DB_PASSWORD, WPT_DB_NAME ); -//\$env['mysql_version'] = \$mysqli->query("SELECT VERSION()")->fetch_row()[0]; -//\$mysqli->close(); -file_put_contents( __DIR__ . '/tests/phpunit/build/logs/env.json', json_encode( \$env, JSON_PRETTY_PRINT ) ); -if ( 'cli' === php_sapi_name() && defined( 'WP_INSTALLING' ) && WP_INSTALLING ) { +// Collect OpenSSL version +\$env['system_utils']['openssl'] = str_replace('OpenSSL ', '', trim(shell_exec('openssl version'))); +// Optional: Collect MySQL version using mysqli (currently commented out) +// \$mysqli = new mysqli(WPT_DB_HOST, WPT_DB_USER, WPT_DB_PASSWORD, WPT_DB_NAME); +// \$env['mysql_version'] = \$mysqli->query("SELECT VERSION()")->fetch_row()[0]; +// \$mysqli->close(); +// Write environment info to JSON file +file_put_contents(\$logDir . 'env.json', json_encode(\$env, JSON_PRETTY_PRINT)); +// If running from CLI during WP installation, output PHP version and executable path +if ('cli' === php_sapi_name() && defined('WP_INSTALLING') && WP_INSTALLING) { echo PHP_EOL; - echo 'PHP version: ' . phpversion() . ' (' . realpath( \$_SERVER['_'] ) . ')' . PHP_EOL; + echo 'PHP version: ' . phpversion() . ' (' . realpath(\$_SERVER['_']) . ')' . PHP_EOL; echo PHP_EOL; } EOT; @@ -358,317 +299,90 @@ function curl_selected_bits(\$k) { return in_array(\$k, array('version', 'ssl_ve // Prepend the logger script to the database settings identifier to ensure it gets included in the wp-tests-config.php file. $system_logger = $logger_replace_string . $system_logger; -// MULTI-PHP Thing +/** + * Prepares a script to log system information relevant to the testing environment. + * The script checks for the existence of the log directory and creates it if it does not exist. + * It then collects various pieces of system information including PHP version, loaded PHP modules, + * MySQL version, operating system details, and versions of key utilities like cURL and OpenSSL. + * This information is collected in an array and written to a JSON file in the log directory. + * Additionally, if running from the command line during a WordPress installation process, + * it outputs the PHP version and executable path. + */ -if( count( $WPT_PHP_EXECUTABLE_MULTI_ARRAY ) ) { +// Prepare an array of PHP executables. If multi-PHP is configured, use the multi-array; otherwise, use a single executable. +$php_executables = ! empty( $WPT_PHP_EXECUTABLE_MULTI_ARRAY ) ? $WPT_PHP_EXECUTABLE_MULTI_ARRAY : [ + [ + 'version' => 'default', + 'bin' => $WPT_PHP_EXECUTABLE, // Ensure this variable is defined for the single PHP executable case. + ] +]; /** * Performs a series of operations to set up the test environment. This includes creating a preparation directory, * cloning the WordPress development repository, downloading the WordPress importer plugin, and preparing the environment with npm. */ -// Prepare an array of shell commands to set up the testing environment. - - foreach( $WPT_PHP_EXECUTABLE_MULTI_ARRAY as $php_multi ) { - - $WPT_PREPARE_DIR_MULTI = $WPT_PREPARE_DIR . '-' . crc32( $php_multi['version'] ); - $WPT_TEST_DIR_MULTI = $WPT_TEST_DIR . '-' . crc32( $php_multi['version'] ); - - perform_operations( array( - - // Create the preparation directory if it doesn't exist. The '-p' flag creates intermediate directories as required. - 'mkdir -p ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ), - - // Clone the WordPress develop repository from GitHub into the preparation directory. - // The '--depth=1' flag creates a shallow clone with a history truncated to the last commit. - 'git clone --depth=10 https://github.com/WordPress/wordpress-develop.git ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ), - - 'git config --add safe.directory ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ), - - ) ); - - if( $WPT_COMMITS ) { - - $commit_sha = null; - if( file_exists( __DIR__ . '/commits.json' ) ) { - - $c_array = json_decode( file_get_contents( __DIR__ . '/commits.json' ), true ); - - if( isset( $c_array['testing_commit'] ) && count( $c_array['testing_commit'] ) ) { - - $commit_sha = $c_array['testing_commit'][0]; - - } else { - - $commit_sha = array_pop($c_array['pending_commits']); - - $c_array['testing_commit'][0] = $commit_sha; - - } - - $c_json = json_encode( $c_array ); - - file_put_contents( __DIR__ . '/commits.json', $c_json ); - - } - - if( ! is_null( $commit_sha ) ) { - - perform_operations( array( - - 'cd ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ) . ' && git checkout ' . $commit_sha - - ) ); - - } - - } - - perform_operations( array( - - // Download the WordPress importer plugin zip file to the specified plugins directory. - 'wget -O ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI . '/tests/phpunit/data/plugins/wordpress-importer.zip' ) . ' https://downloads.wordpress.org/plugin/wordpress-importer.zip' . $certificate_validation, - - // Change directory to the plugin directory, unzip the WordPress importer plugin, and remove the zip file. - 'cd ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI . '/tests/phpunit/data/plugins/' ) . '; unzip wordpress-importer.zip; rm wordpress-importer.zip', - - // Change directory to the preparation directory, install npm dependencies, and build the project. - 'cd ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ) . '; npm install && npm run build' - - ) ); - - // Log a message indicating the start of the variable replacement process for configuration. - log_message( 'Replacing variables in ' . $php_multi['version'] . ' wp-tests-config.php' ); - - /** - * Reads the contents of the WordPress test configuration sample file. - * This file contains template placeholders that need to be replaced with actual values - * from environment variables to configure the WordPress test environment. - */ - - $WPT_PREPARE_DIR_MULTI = $WPT_PREPARE_DIR . '-' . crc32( $php_multi['version'] ); - $contents = file_get_contents( $WPT_PREPARE_DIR_MULTI . '/wp-tests-config-sample.php' ); - - // Define a string that will set the 'WP_PHP_BINARY' constant to the path of the PHP executable. - - $php_binary_string = 'define( \'WP_PHP_BINARY\', \''. $php_multi['bin'] . '\' );'; - - /** - * An associative array mapping configuration file placeholders to environment-specific values. - * This array is used in the subsequent str_replace operation to replace placeholders - * in the wp-tests-config-sample.php file with values from the environment or defaults if none are provided. - */ - - $wptests_tableprefix = trim( getenv( 'WPT_TABLE_PREFIX' ) ) ? : 'wptests_'; - $wptests_tableprefix .= crc32( $php_multi['version'] ); - $search_replace = array( - 'wptests_' => $wptests_tableprefix, - 'youremptytestdbnamehere' => trim( getenv( 'WPT_DB_NAME' ) ), - 'yourusernamehere' => trim( getenv( 'WPT_DB_USER' ) ), - 'yourpasswordhere' => trim( getenv( 'WPT_DB_PASSWORD' ) ), - 'localhost' => trim( getenv( 'WPT_DB_HOST' ) ), - 'define( \'WP_PHP_BINARY\', \'php\' );' => $php_binary_string, - $logger_replace_string => $system_logger, - ); - $contents = str_replace( array_keys( $search_replace ), array_values( $search_replace ), $contents ); - - // Replace the placeholders in the wp-tests-config-sample.php file content with actual values. - // Write the modified content to the wp-tests-config.php file, which will be used by the test suite. - - $WPT_PREPARE_DIR_MULTI = $WPT_PREPARE_DIR . '-' . crc32( $php_multi['version'] ); - file_put_contents( $WPT_PREPARE_DIR_MULTI . '/wp-tests-config.php', $contents ); - - /** - * Determines the PHP version of the test environment to ensure the correct version of PHPUnit is installed. - * It constructs a command that prints out the PHP version in a format compatible with PHPUnit's version requirements. - */ - $php_version_cmd = $php_multi['bin'] . " -r \"print PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '.' . PHP_RELEASE_VERSION;\""; - - /** - * If an SSH connection string is provided, the command to determine the PHP version is modified - * to execute remotely over SSH. This is required if the test environment is not the local machine. - */ - if ( ! empty( $WPT_SSH_CONNECT ) ) { - // The PHP version check command is prefixed with the SSH command, including SSH options, - // and the connection string, ensuring the command is executed on the remote machine. - $php_version_cmd = 'ssh ' . $WPT_SSH_OPTIONS . ' ' . escapeshellarg( $WPT_SSH_CONNECT ) . ' ' . escapeshellarg( $php_version_cmd ); - } - - // Initialize return value variable for the exec function call. - $retval = 0; - - /** - * Executes the constructed command to obtain the PHP version of the test environment. - * The output is stored in $env_php_version, and the return value of the command execution is stored in $retval. - */ - $env_php_version = exec( $php_version_cmd, $output, $retval ); - - // Check if the command execution was successful by inspecting the return value. - if ( $retval !== 0 ) { - // If the return value is not zero, an error occurred, and a message is logged. - error_message( 'Could not retrieve the environment PHP Version for ' . $php_multi['version'] . '.' ); - } - - // Log the obtained PHP version for confirmation and debugging purposes. - log_message( 'Environment PHP Version: ' . $env_php_version ); - - /** - * Checks if the detected PHP version is below 7.0. - * The test runner requires PHP version 7.0 or above, and if the environment's PHP version - * is lower, it logs an error message and could terminate the script. - */ - if ( version_compare( $env_php_version, '7.0', '<' ) ) { - // Logs an error message indicating the test runner's incompatibility with PHP versions below 7.0. - error_message( 'The test runner is not compatible with PHP < 7.0.' ); - } - - /** - * Use Composer to manage PHPUnit and its dependencies. - * This allows for better dependency management and compatibility. - */ - - // Check if Composer is installed and available in the PATH. - - $composer_cmd = 'cd ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ) . ' && '; - - $retval = 0; - $composer_path = escapeshellarg( system( 'which composer', $retval ) ); - - if ( $retval === 0 ) { - - // If Composer is available, prepare the command to use the Composer binary. - $composer_cmd .= $composer_path . ' '; - - } else { - - // If Composer is not available, download the Composer phar file. - log_message( 'Local Composer not found. Downloading latest stable ...' ); - - perform_operations( array( - 'wget -O ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI . '/composer.phar' ) . ' https://getcomposer.org/composer-stable.phar', - ) ); - - // Update the command to use the downloaded Composer phar file. - $composer_cmd .= $php_multi['bin'] . ' composer.phar '; - } - - // Set the PHP version for Composer to ensure compatibility and update dependencies. - perform_operations( array( - $composer_cmd . 'config platform.php ' . escapeshellarg( $env_php_version ), - $composer_cmd . 'update', - ) ); - - /** - * If an SSH connection is configured, use rsync to transfer the prepared files to the remote test environment. - * The -r option for rsync enables recursive copying to handle directory structures. - * Additional rsync options may be included for more verbose output if debugging is enabled. - */ - if ( ! empty( $WPT_SSH_CONNECT ) ) { - // Initialize rsync options with recursive copying. - $rsync_options = '-r'; - - // If debug mode is set to verbose, append 'v' to rsync options for verbose output. - if ( 'verbose' === $WPT_DEBUG ) { - $rsync_options = $rsync_options . 'v'; - } - - // Perform the rsync operation with the configured options and exclude patterns. - // This operation synchronizes the test environment with the prepared files, excluding version control directories - // and other non-essential files for test execution. - perform_operations( array( - 'rsync ' . $rsync_options . ' --exclude=".git/" --exclude="node_modules/" --exclude="composer.phar" -e "ssh ' . $WPT_SSH_OPTIONS . '" ' . escapeshellarg( trailingslashit( $WPT_PREPARE_DIR_MULTI ) ) . ' ' . escapeshellarg( $WPT_SSH_CONNECT . ':' . $WPT_TEST_DIR ), - ) ); - } - - } - -} else { - - perform_operations( array( - - // Create the preparation directory if it doesn't exist. The '-p' flag creates intermediate directories as required. - 'mkdir -p ' . escapeshellarg( $WPT_PREPARE_DIR ), - - // Clone the WordPress develop repository from GitHub into the preparation directory. - // The '--depth=1' flag creates a shallow clone with a history truncated to the last commit. - 'git clone --depth=10 https://github.com/WordPress/wordpress-develop.git ' . escapeshellarg( $WPT_PREPARE_DIR ), - - 'git config --add safe.directory ' . escapeshellarg( $WPT_PREPARE_DIR ), - - ) ); +foreach ( $php_executables as $php_multi ) { + + // Generate unique directory names based on the PHP version. + $version_hash = crc32( $php_multi['version'] ); + $WPT_PREPARE_DIR_MULTI = $WPT_PREPARE_DIR . '-' . $version_hash; + $WPT_TEST_DIR_MULTI = $WPT_TEST_DIR . '-' . $version_hash; + + // Create the preparation directory and clone the WordPress develop repository. + perform_operations([ + 'mkdir -p ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ), + 'git clone --depth=10 https://github.com/WordPress/wordpress-develop.git ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ), + 'git config --add safe.directory ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ), + ]); + + // Handle commit SHA if $WPT_COMMITS is enabled. + if ( $WPT_COMMITS ) { + $commit_sha = null; + $commits_file = __DIR__ . '/commits.json'; - if( $WPT_COMMITS ) { + if ( file_exists( $commits_file ) ) { + $c_array = json_decode( file_get_contents( $commits_file ), true ); - $commit_sha = null; - if( file_exists( __DIR__ . '/commits.json' ) ) { - - $c_array = json_decode( file_get_contents( __DIR__ . '/commits.json' ), true ); - - if( isset( $c_array['testing_commit'] ) && count( $c_array['testing_commit'] ) ) { - + if ( isset( $c_array['testing_commit'] ) && count( $c_array['testing_commit'] ) ) { $commit_sha = $c_array['testing_commit'][0]; - } else { - - $commit_sha = array_shift($c_array['pending_commits']); - + $commit_sha = array_pop( $c_array['pending_commits'] ); $c_array['testing_commit'][0] = $commit_sha; - } - $c_json = json_encode( $c_array ); - - file_put_contents( __DIR__ . '/commits.json', $c_json ); - + file_put_contents( $commits_file, json_encode( $c_array ) ); } - - if( ! is_null( $commit_sha ) ) { - - perform_operations( array( - - 'cd ' . escapeshellarg( $WPT_PREPARE_DIR ) . ' && git checkout ' . $commit_sha, - - ) ); + if ( ! is_null( $commit_sha ) ) { + perform_operations([ + 'cd ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ) . ' && git checkout ' . $commit_sha, + ]); } - } - perform_operations( array( - - // Download the WordPress importer plugin zip file to the specified plugins directory. - 'wget -O ' . escapeshellarg( $WPT_PREPARE_DIR . '/tests/phpunit/data/plugins/wordpress-importer.zip' ) . ' https://downloads.wordpress.org/plugin/wordpress-importer.zip' . $certificate_validation, - - // Change directory to the plugin directory, unzip the WordPress importer plugin, and remove the zip file. - 'cd ' . escapeshellarg( $WPT_PREPARE_DIR . '/tests/phpunit/data/plugins/' ) . '; unzip wordpress-importer.zip; rm wordpress-importer.zip', - - // Change directory to the preparation directory, install npm dependencies, and build the project. - 'cd ' . escapeshellarg( $WPT_PREPARE_DIR ) . '; npm install && npm run build' - - ) ); - - // Log a message indicating the start of the variable replacement process for configuration. - log_message( 'Replacing variables in wp-tests-config.php' ); - - /** - * Reads the contents of the WordPress test configuration sample file. - * This file contains template placeholders that need to be replaced with actual values - * from environment variables to configure the WordPress test environment. - */ + // Download the WordPress importer plugin, unzip it, and build the project with npm. + perform_operations([ + 'wget -O ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI . '/tests/phpunit/data/plugins/wordpress-importer.zip' ) . ' https://downloads.wordpress.org/plugin/wordpress-importer.zip' . $certificate_validation, + 'cd ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI . '/tests/phpunit/data/plugins/' ) . '; unzip wordpress-importer.zip; rm wordpress-importer.zip', + 'cd ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ) . '; npm install && npm run build', + ]); - $contents = file_get_contents( $WPT_PREPARE_DIR . '/wp-tests-config-sample.php' ); + // Log the start of the variable replacement process for the specific PHP version. + log_message( 'Replacing variables in ' . $php_multi['version'] . ' wp-tests-config.php' ); - // Define a string that will set the 'WP_PHP_BINARY' constant to the path of the PHP executable. + // Read the contents of the wp-tests-config-sample.php file. + $config_sample_path = $WPT_PREPARE_DIR_MULTI . '/wp-tests-config-sample.php'; + $contents = file_get_contents( $config_sample_path ); - $php_binary_string = 'define( \'WP_PHP_BINARY\', \''. $WPT_PHP_EXECUTABLE . '\' );'; + // Define the PHP binary string for the specific PHP executable. + $php_binary_string = 'define( \'WP_PHP_BINARY\', \'' . $php_multi['bin'] . '\' );'; - /** - * An associative array mapping configuration file placeholders to environment-specific values. - * This array is used in the subsequent str_replace operation to replace placeholders - * in the wp-tests-config-sample.php file with values from the environment or defaults if none are provided. - */ + // Generate a unique table prefix based on environment variables and PHP version. + $wptests_tableprefix = trim( getenv( 'WPT_TABLE_PREFIX' ) ) ?: 'wptests_'; + $wptests_tableprefix .= crc32( $php_multi['version'] ); - $wptests_tableprefix = trim( getenv( 'WPT_TABLE_PREFIX' ) ) ? : 'wptests_'; - $search_replace = array( + // Create an associative array for search and replace in the configuration file. + $search_replace = [ 'wptests_' => $wptests_tableprefix, 'youremptytestdbnamehere' => trim( getenv( 'WPT_DB_NAME' ) ), 'yourusernamehere' => trim( getenv( 'WPT_DB_USER' ) ), @@ -676,91 +390,75 @@ function curl_selected_bits(\$k) { return in_array(\$k, array('version', 'ssl_ve 'localhost' => trim( getenv( 'WPT_DB_HOST' ) ), 'define( \'WP_PHP_BINARY\', \'php\' );' => $php_binary_string, $logger_replace_string => $system_logger, - ); - $contents = str_replace( array_keys( $search_replace ), array_values( $search_replace ), $contents ); + ]; - // Replace the placeholders in the wp-tests-config-sample.php file content with actual values. - // Write the modified content to the wp-tests-config.php file, which will be used by the test suite. + // Replace placeholders in the configuration sample with actual values. + $contents = str_replace( array_keys( $search_replace ), array_values( $search_replace ), $contents ); - file_put_contents( $WPT_PREPARE_DIR . '/wp-tests-config.php', $contents ); + // Write the modified content to wp-tests-config.php. + $config_path = $WPT_PREPARE_DIR_MULTI . '/wp-tests-config.php'; + file_put_contents( $config_path, $contents ); /** * Determines the PHP version of the test environment to ensure the correct version of PHPUnit is installed. * It constructs a command that prints out the PHP version in a format compatible with PHPUnit's version requirements. */ - $php_version_cmd = $WPT_PHP_EXECUTABLE . " -r \"print PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '.' . PHP_RELEASE_VERSION;\""; + $php_version_cmd = $php_multi['bin'] . " -r \"print PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '.' . PHP_RELEASE_VERSION;\""; - /** - * If an SSH connection string is provided, the command to determine the PHP version is modified - * to execute remotely over SSH. This is required if the test environment is not the local machine. - */ + // Modify the command to execute remotely via SSH if an SSH connection is provided. if ( ! empty( $WPT_SSH_CONNECT ) ) { - // The PHP version check command is prefixed with the SSH command, including SSH options, - // and the connection string, ensuring the command is executed on the remote machine. $php_version_cmd = 'ssh ' . $WPT_SSH_OPTIONS . ' ' . escapeshellarg( $WPT_SSH_CONNECT ) . ' ' . escapeshellarg( $php_version_cmd ); } - // Initialize return value variable for the exec function call. + // Execute the command to retrieve the PHP version. $retval = 0; - - /** - * Executes the constructed command to obtain the PHP version of the test environment. - * The output is stored in $env_php_version, and the return value of the command execution is stored in $retval. - */ $env_php_version = exec( $php_version_cmd, $output, $retval ); - // Check if the command execution was successful by inspecting the return value. - if ( $retval !== 0 ) { - // If the return value is not zero, an error occurred, and a message is logged. - error_message( 'Could not retrieve the environment PHP Version.' ); + // Log an error if the PHP version retrieval failed. + if ( 0 !== $retval ) { + error_message( 'Could not retrieve the environment PHP Version for ' . $php_multi['version'] . '.' ); } - // Log the obtained PHP version for confirmation and debugging purposes. + // Log the obtained PHP version. log_message( 'Environment PHP Version: ' . $env_php_version ); /** - * Checks if the detected PHP version is below 7.0. - * The test runner requires PHP version 7.0 or above, and if the environment's PHP version - * is lower, it logs an error message and could terminate the script. + * Checks if the detected PHP version is below 7.2. + * The test runner requires PHP version 7.2 or above, and if the environment's PHP version + * is lower, it logs an error message. */ - if ( version_compare( $env_php_version, '7.0', '<' ) ) { - // Logs an error message indicating the test runner's incompatibility with PHP versions below 7.0. - error_message( 'The test runner is not compatible with PHP < 7.0.' ); + if ( version_compare( $env_php_version, '7.2', '<' ) ) { + error_message( 'The test runner is not compatible with PHP < 7.2.' ); } /** - * Use Composer to manage PHPUnit and its dependencies. - * This allows for better dependency management and compatibility. + * Use Composer to manage PHPUnit and its dependencies. + * This allows for better dependency management and compatibility. */ - // Check if Composer is installed and available in the PATH. - $composer_cmd = 'cd ' . escapeshellarg( $WPT_PREPARE_DIR ) . ' && '; - $retval = 0; - $composer_path = escapeshellarg( system( 'which composer', $retval ) ); - - if ( $retval === 0 ) { + // Define the Composer command based on its availability. + $composer_cmd = 'cd ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ) . ' && '; + $composer_path = system( 'which composer', $retval ); + $composer_path = escapeshellarg( trim( $composer_path ) ); - // If Composer is available, prepare the command to use the Composer binary. + if ( 0 === $retval && ! empty( $composer_path ) ) { + // If Composer is available, use the Composer binary. $composer_cmd .= $composer_path . ' '; - } else { - // If Composer is not available, download the Composer phar file. log_message( 'Local Composer not found. Downloading latest stable ...' ); - - perform_operations( array( - 'wget -O ' . escapeshellarg( $WPT_PREPARE_DIR . '/composer.phar' ) . ' https://getcomposer.org/composer-stable.phar', - ) ); - - // Update the command to use the downloaded Composer phar file. - $composer_cmd .= $WPT_PHP_EXECUTABLE . ' composer.phar '; + perform_operations([ + 'wget -O ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI . '/composer.phar' ) . ' https://getcomposer.org/composer-stable.phar' . $certificate_validation, + ]); + // Use the downloaded Composer phar file. + $composer_cmd .= $php_multi['bin'] . ' composer.phar '; } - // Set the PHP version for Composer to ensure compatibility and update dependencies. - perform_operations( array( + // Set the PHP version for Composer and update dependencies. + perform_operations([ $composer_cmd . 'config platform.php ' . escapeshellarg( $env_php_version ), $composer_cmd . 'update', - ) ); + ]); /** * If an SSH connection is configured, use rsync to transfer the prepared files to the remote test environment. @@ -773,17 +471,14 @@ function curl_selected_bits(\$k) { return in_array(\$k, array('version', 'ssl_ve // If debug mode is set to verbose, append 'v' to rsync options for verbose output. if ( 'verbose' === $WPT_DEBUG ) { - $rsync_options = $rsync_options . 'v'; + $rsync_options .= 'v'; } // Perform the rsync operation with the configured options and exclude patterns. - // This operation synchronizes the test environment with the prepared files, excluding version control directories - // and other non-essential files for test execution. - perform_operations( array( - 'rsync ' . $rsync_options . ' --exclude=".git/" --exclude="node_modules/" --exclude="composer.phar" -e "ssh ' . $WPT_SSH_OPTIONS . '" ' . escapeshellarg( trailingslashit( $WPT_PREPARE_DIR ) ) . ' ' . escapeshellarg( $WPT_SSH_CONNECT . ':' . $WPT_TEST_DIR ), - ) ); + perform_operations([ + 'rsync ' . $rsync_options . ' --exclude=".git/" --exclude="node_modules/" --exclude="composer.phar" -e "ssh ' . $WPT_SSH_OPTIONS . '" ' . escapeshellarg( rtrim( $WPT_PREPARE_DIR_MULTI, '/' ) ) . '/ ' . escapeshellarg( $WPT_SSH_CONNECT . ':' . $WPT_TEST_DIR ), + ]); } - } // Log a success message indicating that the environment has been prepared. diff --git a/report.php b/report.php index b4a3afd..5ff8ee5 100644 --- a/report.php +++ b/report.php @@ -1,430 +1,214 @@ trim( $php_multi_v[0] ), 'bin' => trim( $php_multi_v[1] ) ); +$WPT_PHP_EXECUTABLE_MULTI_ARRAY = []; +if ( ! empty( $WPT_PHP_EXECUTABLE_MULTI ) ) { + + // Divide the version string by semicolon + $php_multi_versions = explode( ';', $WPT_PHP_EXECUTABLE_MULTI ); + + foreach ( $php_multi_versions as $php_multi_version ) { + // Divide each version by the equals sign and apply trim to each part + $parts = array_map( 'trim', explode( '=', $php_multi_version, 2 ) ); + + // Ensures that both parts exist and are not empty. + if ( 2 === count( $parts ) && '' !== $parts[0] && '' !== $parts[1] ) { + [ $version, $bin ] = $parts; + $WPT_PHP_EXECUTABLE_MULTI_ARRAY[] = [ + 'version' => $version, + 'bin' => $bin + ]; } - - unset( $php_multi_version ); } - - unset( $php_multi_versions ); } -// MULTI-PHP Thing - -if( count( $WPT_PHP_EXECUTABLE_MULTI_ARRAY ) ) { +// Prepare an array of PHP executables. If multi-PHP is configured, use the multi-array; otherwise, use a single executable. +$php_executables = ! empty( $WPT_PHP_EXECUTABLE_MULTI_ARRAY ) ? $WPT_PHP_EXECUTABLE_MULTI_ARRAY : [ + [ + 'version' => 'default', + 'bin' => $WPT_PHP_EXECUTABLE, // Ensure this variable is defined for the single PHP executable case. + ] +]; /** - * Performs a series of operations to set up the test environment. This includes creating a preparation directory, - * cloning the WordPress development repository, downloading the WordPress importer plugin, and preparing the environment with npm. + * Performs a series of operations to set up the test environment. + * This includes handling commits, retrieving SVN revisions, copying results, processing junit.xml, + * and uploading test results. */ -// Prepare an array of shell commands to set up the testing environment. - - foreach( $WPT_PHP_EXECUTABLE_MULTI_ARRAY as $php_multi ) { - - $WPT_PREPARE_DIR_MULTI = $WPT_PREPARE_DIR . '-' . crc32( $php_multi['version'] ); - $WPT_TEST_DIR_MULTI = $WPT_TEST_DIR . '-' . crc32( $php_multi['version'] ); - - if( $WPT_COMMITS ) { - - $commit_sha = null; - if( file_exists( __DIR__ . '/commits.json' ) ) { - - $c_array = json_decode( file_get_contents( __DIR__ . '/commits.json' ), true ); - - if( isset( $c_array['testing_commit'] ) && count( $c_array['testing_commit'] ) ) { +foreach ( $php_executables as $php_multi ) { + // Generate unique directory names based on the PHP version. + $WPT_PREPARE_DIR_MULTI = $WPT_PREPARE_DIR . '-' . crc32( $php_multi['version'] ); + $WPT_TEST_DIR_MULTI = $WPT_TEST_DIR . '-' . crc32( $php_multi['version'] ); - $c_array['executed_commits'][] = $c_array['testing_commit'][0]; - - unset( $c_array['testing_commit'][0] ); - - } - - $c_json = json_encode( $c_array ); - - file_put_contents( __DIR__ . '/commits.json', $c_json ); - - } - } - - /** - * Retrieves the SVN revision number from the git repository log. - * Logs a message indicating the start of the SVN revision retrieval process. - * Executes a shell command that accesses the git directory specified by the - * WPT_PREPARE_DIR environment variable, retrieves the latest commit message, - * and extracts the SVN revision number using a combination of grep and cut commands. - */ - log_message('Getting SVN Revision'); - $rev = exec('git --git-dir=' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ) . '/.git log -1 --pretty=%B | grep "git-svn-id:" | cut -d " " -f 2 | cut -d "@" -f 2'); - - /** - * Retrieves the latest SVN commit message from the git repository log. - * Logs a message to indicate the retrieval of the SVN commit message. Executes a shell command - * that accesses the git directory specified by the WPT_PREPARE_DIR environment variable, - * fetches the latest commit message, and trims any whitespace from the message. - */ - log_message('Getting SVN message'); - $message = trim( exec('git --git-dir=' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ) . '/.git log -1 --pretty=%B | head -1') ); - - /** - * Prepares the file path for copying the junit.xml results. - * Logs a message indicating the start of the operation to copy junit.xml results. - * Constructs the file path to the junit.xml file(s) located in the test directory, - * making use of the WPT_TEST_DIR environment variable. The path is sanitized to be - * safely used in shell commands. - */ - log_message('Copying junit.xml results'); - $junit_location = escapeshellarg( $WPT_TEST_DIR_MULTI ) . '/tests/phpunit/build/logs/*'; - - /** - * Modifies the junit.xml results file path for a remote location if an SSH connection is available. - * If the WPT_SSH_CONNECT environment variable is not empty, indicating that an SSH connection - * is configured, this snippet adapts the junit_location variable to include the necessary SSH - * command and options for accessing the remote file system. It concatenates SSH options with the - * remote path to ensure that the junit.xml results can be accessed or copied over SSH. - */ - if ( ! empty( $WPT_SSH_CONNECT ) ) { - $junit_location = '-e "ssh ' . $WPT_SSH_OPTIONS . '" ' . escapeshellarg( $WPT_SSH_CONNECT . ':' . $junit_location ); - } - - /** - * Sets the options for the rsync command based on the debug mode. - * Initializes the rsync options with the recursive flag. If the debug mode is set to 'verbose', - * appends the 'v' flag to the rsync options to enable verbose output during the rsync operation, - * providing more detailed information about the file transfer process. - */ - $rsync_options = '-r'; - - if ( 'verbose' === $WPT_DEBUG ) { - $rsync_options = $rsync_options . 'v'; - } - - /** - * Constructs the rsync command for executing the synchronization of junit.xml files. - * Concatenates the rsync command with the previously defined options and the source and - * destination paths. The destination path is sanitized for shell execution. This command is - * then passed to the `perform_operations` function, which executes the command to synchronize - * the junit.xml files from the source to the destination directory. - */ - $junit_exec = 'rsync ' . $rsync_options . ' ' . $junit_location . ' ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ); - perform_operations( array( - $junit_exec, - ) ); - - /** - * Processes and uploads the junit.xml file. - * First, a log message is recorded to indicate the start of processing the junit.xml file. - * Then, the contents of the junit.xml file are read from the prepared directory into a string. - * This XML string is then passed to a function that processes the XML data, presumably to prepare - * it for upload or to extract relevant test run information. - */ - log_message( 'Processing and uploading junit.xml' ); - $xml = file_get_contents( $WPT_PREPARE_DIR_MULTI . '/junit.xml' ); - $results = process_junit_xml( $xml ); - - /** - * Retrieves environment details from a JSON file or generates them if not available. - * Initializes the environment details string. If an 'env.json' file exists in the prepared - * directory, its contents are read into the environment details string. If the file doesn't - * exist but the prepared directory is the same as the test directory, the environment details - * are generated by calling a function that retrieves these details, then encoded into JSON format. - */ - $env = ''; - if ( file_exists( $WPT_PREPARE_DIR_MULTI . '/env.json' ) ) { - $env = file_get_contents( $WPT_PREPARE_DIR_MULTI . '/env.json' ); - } elseif ( $WPT_PREPARE_DIR_MULTI === $WPT_TEST_DIR_MULTI ) { - $env = json_encode( get_env_details(), JSON_PRETTY_PRINT ); - } - - /** - * Attempts to upload test results if an API key is available, otherwise logs the results locally. - * Checks if an API key for reporting is present. If so, it attempts to upload the test results - * using the `upload_results` function and processes the HTTP response. A success message is logged - * if the upload is successful, indicated by a 20x HTTP status code. If the upload fails, an error - * message is logged along with the HTTP status. If no API key is provided, it logs the test results - * and environment details locally. - */ - if( ! empty( $WPT_REPORT_API_KEY ) ) { - - // Upload the results and capture the HTTP status and response body - list( $http_status, $response_body ) = upload_results( $results, $rev, $message, $env, $WPT_REPORT_API_KEY ); - - // Decode the JSON response body - $response = json_decode( $response_body, true ); - if ( 20 == substr( $http_status, 0, 2 ) ) { - - // Construct and log a success message with a link if provided in the response - $message = 'Results successfully uploaded'; - $message .= isset( $response['link'] ) ? ': ' . $response['link'] : ''; - log_message( $message ); - - } else { - - // Construct and log an error message with additional details if provided in the response - $message = 'Error uploading results'; - $message .= isset( $response['message'] ) ? ': ' . $response['message'] : ''; - $message .= ' (HTTP status ' . (int) $http_status . ')'; - error_message( $message ); - - } - - } else { - - // Log the test results and environment details locally if no API key is provided - log_message( '[+] TEST RESULTS' . "\n\n" . $results. "\n\n" ); - log_message( '[+] ENVIRONMENT' . "\n\n" . $env . "\n\n" ); - - } - - } - -} else { - - if( $WPT_COMMITS ) { - - $commit_sha = null; - if( file_exists( __DIR__ . '/commits.json' ) ) { - - $c_array = json_decode( file_get_contents( __DIR__ . '/commits.json' ), true ); - - if( isset( $c_array['testing_commit'] ) && count( $c_array['testing_commit'] ) ) { + /** + * Handle commits if $WPT_COMMITS is enabled. + */ + if ( $WPT_COMMITS ) { + $commits_file = __DIR__ . '/commits.json'; + if ( file_exists( $commits_file ) ) { + $c_array = json_decode( file_get_contents( $commits_file ), true ); + if ( isset( $c_array['testing_commit'] ) && count( $c_array['testing_commit'] ) ) { + // Move the current testing commit to executed_commits $c_array['executed_commits'][] = $c_array['testing_commit'][0]; - unset( $c_array['testing_commit'][0] ); - } - $c_json = json_encode( $c_array ); - - file_put_contents( __DIR__ . '/commits.json', $c_json ); - + file_put_contents( $commits_file, json_encode( $c_array ) ); } } /** - * Retrieves the SVN revision number from the git repository log. - * Logs a message indicating the start of the SVN revision retrieval process. - * Executes a shell command that accesses the git directory specified by the - * WPT_PREPARE_DIR environment variable, retrieves the latest commit message, - * and extracts the SVN revision number using a combination of grep and cut commands. + * Retrieve the SVN revision number from the git repository log. */ - log_message('Getting SVN Revision'); - $rev = exec('git --git-dir=' . escapeshellarg( $WPT_PREPARE_DIR ) . '/.git log -1 --pretty=%B | grep "git-svn-id:" | cut -d " " -f 2 | cut -d "@" -f 2'); + log_message( 'Getting SVN Revision' ); + $rev = exec( 'git --git-dir=' . escapeshellarg( $WPT_PREPARE_DIR_MULTI . '/.git' ) . ' log -1 --pretty=%B | grep "git-svn-id:" | cut -d " " -f 2 | cut -d "@" -f 2' ); /** - * Retrieves the latest SVN commit message from the git repository log. - * Logs a message to indicate the retrieval of the SVN commit message. Executes a shell command - * that accesses the git directory specified by the WPT_PREPARE_DIR environment variable, - * fetches the latest commit message, and trims any whitespace from the message. + * Retrieve the latest SVN commit message from the git repository log. */ - log_message('Getting SVN message'); - $message = trim( exec('git --git-dir=' . escapeshellarg( $WPT_PREPARE_DIR ) . '/.git log -1 --pretty=%B | head -1') ); + log_message( 'Getting SVN message' ); + $message = trim( exec( 'git --git-dir=' . escapeshellarg($WPT_PREPARE_DIR_MULTI . '/.git') . ' log -1 --pretty=%B | head -1' ) ); /** - * Prepares the file path for copying the junit.xml results. - * Logs a message indicating the start of the operation to copy junit.xml results. - * Constructs the file path to the junit.xml file(s) located in the test directory, - * making use of the WPT_TEST_DIR environment variable. The path is sanitized to be - * safely used in shell commands. + * Copy the junit.xml results. */ - log_message('Copying junit.xml results'); - $junit_location = escapeshellarg( $WPT_TEST_DIR ) . '/tests/phpunit/build/logs/*'; + log_message( 'Copying junit.xml results' ); + $junit_location = escapeshellarg( $WPT_TEST_DIR_MULTI ) . '/tests/phpunit/build/logs/*'; - /** - * Modifies the junit.xml results file path for a remote location if an SSH connection is available. - * If the WPT_SSH_CONNECT environment variable is not empty, indicating that an SSH connection - * is configured, this snippet adapts the junit_location variable to include the necessary SSH - * command and options for accessing the remote file system. It concatenates SSH options with the - * remote path to ensure that the junit.xml results can be accessed or copied over SSH. - */ if ( ! empty( $WPT_SSH_CONNECT ) ) { $junit_location = '-e "ssh ' . $WPT_SSH_OPTIONS . '" ' . escapeshellarg( $WPT_SSH_CONNECT . ':' . $junit_location ); } /** - * Sets the options for the rsync command based on the debug mode. - * Initializes the rsync options with the recursive flag. If the debug mode is set to 'verbose', - * appends the 'v' flag to the rsync options to enable verbose output during the rsync operation, - * providing more detailed information about the file transfer process. + * Set rsync options based on the debug mode. */ $rsync_options = '-r'; - if ( 'verbose' === $WPT_DEBUG ) { - $rsync_options = $rsync_options . 'v'; + $rsync_options .= 'v'; } /** - * Constructs the rsync command for executing the synchronization of junit.xml files. - * Concatenates the rsync command with the previously defined options and the source and - * destination paths. The destination path is sanitized for shell execution. This command is - * then passed to the `perform_operations` function, which executes the command to synchronize - * the junit.xml files from the source to the destination directory. + * Construct and execute the rsync command. */ - $junit_exec = 'rsync ' . $rsync_options . ' ' . $junit_location . ' ' . escapeshellarg( $WPT_PREPARE_DIR ); - perform_operations( array( + $junit_exec = 'rsync ' . $rsync_options . ' ' . $junit_location . ' ' . escapeshellarg( $WPT_PREPARE_DIR_MULTI ); + perform_operations([ $junit_exec, - ) ); + ]); /** - * Processes and uploads the junit.xml file. - * First, a log message is recorded to indicate the start of processing the junit.xml file. - * Then, the contents of the junit.xml file are read from the prepared directory into a string. - * This XML string is then passed to a function that processes the XML data, presumably to prepare - * it for upload or to extract relevant test run information. + * Process and upload the junit.xml file. */ log_message( 'Processing and uploading junit.xml' ); - $xml = file_get_contents( $WPT_PREPARE_DIR . '/junit.xml' ); + $xml = file_get_contents( $WPT_PREPARE_DIR_MULTI . '/junit.xml' ); $results = process_junit_xml( $xml ); /** - * Retrieves environment details from a JSON file or generates them if not available. - * Initializes the environment details string. If an 'env.json' file exists in the prepared - * directory, its contents are read into the environment details string. If the file doesn't - * exist but the prepared directory is the same as the test directory, the environment details - * are generated by calling a function that retrieves these details, then encoded into JSON format. + * Retrieve environment details from a JSON file or generate them if not available. */ $env = ''; - if ( file_exists( $WPT_PREPARE_DIR . '/env.json' ) ) { - $env = file_get_contents( $WPT_PREPARE_DIR . '/env.json' ); - } elseif ( $WPT_PREPARE_DIR === $WPT_TEST_DIR ) { + $env_json_path = $WPT_PREPARE_DIR_MULTI . '/env.json'; + if ( file_exists( $env_json_path ) ) { + $env = file_get_contents( $env_json_path ); + } elseif ( $WPT_PREPARE_DIR_MULTI === $WPT_TEST_DIR_MULTI ) { $env = json_encode( get_env_details(), JSON_PRETTY_PRINT ); } /** - * Attempts to upload test results if an API key is available, otherwise logs the results locally. - * Checks if an API key for reporting is present. If so, it attempts to upload the test results - * using the `upload_results` function and processes the HTTP response. A success message is logged - * if the upload is successful, indicated by a 20x HTTP status code. If the upload fails, an error - * message is logged along with the HTTP status. If no API key is provided, it logs the test results - * and environment details locally. + * Attempt to upload test results if an API key is available; otherwise, log the results locally. */ - if( ! empty( $WPT_REPORT_API_KEY ) ) { - + if ( ! empty( $WPT_REPORT_API_KEY ) ) { // Upload the results and capture the HTTP status and response body list( $http_status, $response_body ) = upload_results( $results, $rev, $message, $env, $WPT_REPORT_API_KEY ); // Decode the JSON response body $response = json_decode( $response_body, true ); - if ( 20 == substr( $http_status, 0, 2 ) ) { - + if ( strpos( $http_status, '20' ) === 0 ) { // Check if status code starts with '20' // Construct and log a success message with a link if provided in the response - $message = 'Results successfully uploaded'; - $message .= isset( $response['link'] ) ? ': ' . $response['link'] : ''; - log_message( $message ); - + $success_message = 'Results successfully uploaded'; + if ( isset( $response['link'] ) ) { + $success_message .= ': ' . $response['link']; + } + log_message( $success_message ); } else { - // Construct and log an error message with additional details if provided in the response - $message = 'Error uploading results'; - $message .= isset( $response['message'] ) ? ': ' . $response['message'] : ''; - $message .= ' (HTTP status ' . (int) $http_status . ')'; - error_message( $message ); - + $error_message = 'Error uploading results'; + if ( isset( $response['message'] ) ) { + $error_message .= ': ' . $response['message']; + } + $error_message .= ' (HTTP status ' . (int)$http_status . ')'; + error_message( $error_message ); } - } else { - // Log the test results and environment details locally if no API key is provided - log_message( '[+] TEST RESULTS' . "\n\n" . $results. "\n\n" ); + log_message( '[+] TEST RESULTS' . "\n\n" . $results . "\n\n" ); log_message( '[+] ENVIRONMENT' . "\n\n" . $env . "\n\n" ); - } -} \ No newline at end of file +} diff --git a/test.php b/test.php index 0709f98..11bb9c9 100644 --- a/test.php +++ b/test.php @@ -1,51 +1,52 @@ trim( $php_multi_v[0] ), 'bin' => trim( $php_multi_v[1] ) ); +$WPT_PHP_EXECUTABLE_MULTI_ARRAY = []; +if ( ! empty( $WPT_PHP_EXECUTABLE_MULTI ) ) { + + // Divide the version string by semicolon + $php_multi_versions = explode( ';', $WPT_PHP_EXECUTABLE_MULTI ); + + foreach ( $php_multi_versions as $php_multi_version ) { + // Divide each version by the equals sign and apply trim to each part + $parts = array_map( 'trim', explode( '=', $php_multi_version, 2 ) ); + + // Ensures that both parts exist and are not empty. + if ( 2 === count( $parts ) && '' !== $parts[0] && '' !== $parts[1] ) { + [ $version, $bin ] = $parts; + $WPT_PHP_EXECUTABLE_MULTI_ARRAY[] = [ + 'version' => $version, + 'bin' => $bin + ]; } - - unset( $php_multi_version ); } - - unset( $php_multi_versions ); } -if( count( $WPT_PHP_EXECUTABLE_MULTI_ARRAY ) ) { +// Prepare an array of PHP executables. If multi-PHP is configured, use the multi-array; otherwise, use a single executable. +$php_executables = ! empty( $WPT_PHP_EXECUTABLE_MULTI_ARRAY ) ? $WPT_PHP_EXECUTABLE_MULTI_ARRAY : [ + [ + 'version' => 'default', + 'bin' => $WPT_PHP_EXECUTABLE, // Ensure this variable is defined for the single PHP executable case. + ] +]; /** - * Performs a series of operations to set up the test environment. This includes creating a preparation directory, - * cloning the WordPress development repository, downloading the WordPress importer plugin, and preparing the environment with npm. + * Performs a series of operations to set up the test environment. This includes setting up PHPUnit commands + * and executing them, potentially over SSH if a connection string is provided. */ -// Prepare an array of shell commands to set up the testing environment. - - foreach( $WPT_PHP_EXECUTABLE_MULTI_ARRAY as $php_multi ) { - - $WPT_TEST_DIR_MULTI = $WPT_TEST_DIR . '-' . crc32( $php_multi['version'] ); - - /** - * Determines the PHPUnit command to execute the test suite. - * Retrieves the PHPUnit command from the environment variable 'WPT_PHPUNIT_CMD'. If the environment - * variable is not set or is empty, it constructs a default command using the PHP executable path and - * the test directory path from environment variables, appending parameters to the PHPUnit call to - * avoid reporting useless tests. - */ - $WPT_PHPUNIT_CMD = trim( getenv( 'WPT_PHPUNIT_CMD' ) ); - if( empty( $WPT_PHPUNIT_CMD ) ) { - $WPT_PHPUNIT_CMD = 'cd ' . escapeshellarg( $WPT_TEST_DIR_MULTI ) . ' && ' . $php_multi['bin'] . ' ./vendor/phpunit/phpunit/phpunit --dont-report-useless-tests' . $WPT_FLAVOR_TXT . $WPT_EXTRATESTS_TXT; - } +foreach ( $php_executables as $php_multi ) { + // Generate a unique test directory name based on the PHP version. + $WPT_TEST_DIR_MULTI = $WPT_TEST_DIR . '-' . crc32( $php_multi['version'] ); - // If an SSH connection string is provided, prepend the SSH command to the PHPUnit execution command. - if ( ! empty( $WPT_SSH_CONNECT ) ) { - $WPT_PHPUNIT_CMD = 'ssh ' . $WPT_SSH_OPTIONS . ' ' . escapeshellarg( $WPT_SSH_CONNECT ) . ' ' . escapeshellarg( $WPT_PHPUNIT_CMD ); - } - - // Execute the PHPUnit command. - perform_operations( array( - $WPT_PHPUNIT_CMD - ) ); - - } -} else { - /** * Determines the PHPUnit command to execute the test suite. * Retrieves the PHPUnit command from the environment variable 'WPT_PHPUNIT_CMD'. If the environment @@ -129,8 +110,8 @@ * avoid reporting useless tests. */ $WPT_PHPUNIT_CMD = trim( getenv( 'WPT_PHPUNIT_CMD' ) ); - if( empty( $WPT_PHPUNIT_CMD ) ) { - $WPT_PHPUNIT_CMD = 'cd ' . escapeshellarg( $WPT_TEST_DIR ) . ' && ' . $WPT_PHP_EXECUTABLE . ' ./vendor/phpunit/phpunit/phpunit --dont-report-useless-tests' . $WPT_FLAVOR_TXT . $WPT_EXTRATESTS_TXT; + if ( empty( $WPT_PHPUNIT_CMD ) ) { + $WPT_PHPUNIT_CMD = 'cd ' . escapeshellarg( $WPT_TEST_DIR_MULTI ) . ' && ' . $php_multi['bin'] . ' ./vendor/phpunit/phpunit/phpunit --dont-report-useless-tests' . $WPT_FLAVOR_TXT . $WPT_EXTRATESTS_TXT; } // If an SSH connection string is provided, prepend the SSH command to the PHPUnit execution command. @@ -139,9 +120,7 @@ } // Execute the PHPUnit command. - perform_operations( array( + perform_operations([ $WPT_PHPUNIT_CMD - ) ); - + ]); } - \ No newline at end of file From 234a0e33c3e28a59d5cff167259c1923f7ba5934 Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Sun, 29 Sep 2024 08:04:24 +0200 Subject: [PATCH 27/30] 80 columns --- .env.default | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/.env.default b/.env.default index ca64b7d..991aceb 100644 --- a/.env.default +++ b/.env.default @@ -11,38 +11,45 @@ # $ source .env ### -# Label for the environment. Can be empty (default) or be like "shared", "vps", "cloud" or similar. -# Please use only alphanumeric keywords, and try to be descriptive +# Label for the environment. Can be empty (default) or be like "shared", "vps", +# "cloud" or similar. Please use only alphanumeric keywords, and try to be +# descriptive export WPT_LABEL="" -# Path to the directory where files can be prepared before being delivered to the environment. +# Path to the directory where files can be prepared before being delivered to +# the environment. export WPT_PREPARE_DIR="/tmp/wp-test-runner" -# Path to the directory where the WordPress develop checkout can be placed and tests can be run. -# When running tests in the same environment, set WPT_TEST_DIR to WPT_PREPARE_DIR +# Path to the directory where the WordPress develop checkout can be placed and +# tests can be run. When running tests in the same environment, set WPT_TEST_DIR +# to WPT_PREPARE_DIR export WPT_TEST_DIR="/tmp/wp-test-runner" -# API key to authenticate with the reporting service in 'username:password' format. +# API key to authenticate with the reporting service in 'username:password' +# format. export WPT_REPORT_API_KEY="" # (Optionally) define an alternate reporting URL export WPT_REPORT_URL="" # Credentials for a database that can be written to and reset. -# WARNING!!! This database will be destroyed between tests. Only use safe database credentials. -# Please note that you must escape _or_ refrain from using # as special character in your credentials. +# WARNING!!! This database will be destroyed between tests. Only use safe +# database credentials. Please note that you must escape _or_ refrain from +# using # as special character in your credentials. export WPT_DB_NAME="" export WPT_DB_USER="" export WPT_DB_PASSWORD="" export WPT_DB_HOST="" -# (Optionally) set a custom table prefix to permit concurrency against the same database. +# (Optionally) set a custom table prefix to permit concurrency against the same +# database. export WPT_TABLE_PREFIX=${WPT_TABLE_PREFIX-wptests_} # (Optionally) define the PHP executable to be called export WPT_PHP_EXECUTABLE=${WPT_PHP_EXECUTABLE-php} -# (Optionally) array of versions (like: 8.0=/bin/php8.0;8.1=/bin/php8.1) +# (Optionally) array of versions +# like: "8.1=/bin/php8.1;8.2=/bin/php8.2;8.3=/bin/php8.3" export WPT_PHP_EXECUTABLE_MULTI="" # (Optionally) define the PHPUnit command execution call. From 398b8090123e68ba42d9bc773eceaeda71bfefd9 Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Sun, 29 Sep 2024 08:05:15 +0200 Subject: [PATCH 28/30] readme changes - Improved readability - default .env updated from source - other fixes --- README.md | 76 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 384f7a7..6597e1d 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,10 @@ With a direct Git clone, you can: ```bash # Copy the default .env file. cp .env.default .env + # Edit the .env file to define your variables. vim .env + # Load your variables into scope. source .env ``` @@ -47,8 +49,10 @@ Connect to a remote environment over SSH by having the CI job provision the SSH ```bash # 1. Create a SSH key pair for the controller to use ssh-keygen -t rsa -b 4096 -C "travis@travis-ci.org" + # 2. base64 encode the private key for use with the environment variable cat ~/.ssh/id_rsa | base64 --wrap=0 + # 3. Append id_rsa.pub to authorized_keys so the CI service can SSH in cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys ``` @@ -61,6 +65,7 @@ Host wpt Hostname 123.45.67.89 User wpt Port 1234 + # 2. Use 'wpt' wherever you might normally use a SSH connection string ssh wpt ``` @@ -161,55 +166,69 @@ The content (in summary form) can be something like this: # $ source .env ### -# Path to the directory where files can be prepared before being delivered to the environment. -export WPT_PREPARE_DIR=/tmp/wp-test-runner +# Label for the environment. Can be empty (default) or be like "shared", "vps", +# "cloud" or similar. Please use only alphanumeric keywords, and try to be +# descriptive +export WPT_LABEL="" -# Path to the directory where the WordPress develop checkout can be placed and tests can be run. -# When running tests in the same environment, set WPT_TEST_DIR to WPT_PREPARE_DIR -export WPT_TEST_DIR=/tmp/wp-test-runner +# Path to the directory where files can be prepared before being delivered to +# the environment. +export WPT_PREPARE_DIR="/tmp/wp-test-runner" -# API key to authenticate with the reporting service in 'username:password' format. -export WPT_REPORT_API_KEY= +# Path to the directory where the WordPress develop checkout can be placed and +# tests can be run. When running tests in the same environment, set WPT_TEST_DIR +# to WPT_PREPARE_DIR +export WPT_TEST_DIR="/tmp/wp-test-runner" + +# API key to authenticate with the reporting service in 'username:password' +# format. +export WPT_REPORT_API_KEY="" # (Optionally) define an alternate reporting URL -export WPT_REPORT_URL= +export WPT_REPORT_URL="" # Credentials for a database that can be written to and reset. -# WARNING!!! This database will be destroyed between tests. Only use safe database credentials. -# Please note that you must escape _or_ refrain from using # as special character in your credentials. -export WPT_DB_NAME= -export WPT_DB_USER= -export WPT_DB_PASSWORD= -export WPT_DB_HOST= - -# (Optionally) set a custom table prefix to permit concurrency against the same database. +# WARNING!!! This database will be destroyed between tests. Only use safe +# database credentials. Please note that you must escape _or_ refrain from +# using # as special character in your credentials. +export WPT_DB_NAME="" +export WPT_DB_USER="" +export WPT_DB_PASSWORD="" +export WPT_DB_HOST="" + +# (Optionally) set a custom table prefix to permit concurrency against the same +# database. export WPT_TABLE_PREFIX=${WPT_TABLE_PREFIX-wptests_} # (Optionally) define the PHP executable to be called export WPT_PHP_EXECUTABLE=${WPT_PHP_EXECUTABLE-php} +# (Optionally) array of versions +# like: "8.1=/bin/php8.1;8.2=/bin/php8.2;8.3=/bin/php8.3" +export WPT_PHP_EXECUTABLE_MULTI="" + # (Optionally) define the PHPUnit command execution call. # Use if `php phpunit.phar` can't be called directly for some reason. -export WPT_PHPUNIT_CMD= +export WPT_PHPUNIT_CMD="" # (Optionally) define the command execution to remove the test directory # Use if `rm -r` can't be called directly for some reason. -export WPT_RM_TEST_DIR_CMD= +export WPT_RM_TEST_DIR_CMD="" # SSH connection string (can also be an alias). # Leave empty if tests are meant to run in the same environment. -export WPT_SSH_CONNECT= +export WPT_SSH_CONNECT="" # Any options to be passed to the SSH connection # Defaults to '-o StrictHostKeyChecking=no' -export WPT_SSH_OPTIONS= +export WPT_SSH_OPTIONS="" # SSH private key, base64 encoded. -export WPT_SSH_PRIVATE_KEY_BASE64= +export WPT_SSH_PRIVATE_KEY_BASE64="" # Output logging # Use 'verbose' to increase verbosity -export WPT_DEBUG= +export WPT_DEBUG="" # Certificate validation # Use 1 to validate, and 0 to not validate @@ -218,7 +237,7 @@ export WPT_CERTIFICATE_VALIDATION=1 # WordPress flavor # 0 = WordPress (simple version) # 1 = WordPress Multisite -export WPT_FLAVOR=1 +export WPT_FLAVOR=0 # Extra tests (groups) # 0 = none @@ -226,6 +245,13 @@ export WPT_FLAVOR=1 # 2 = ms-files # 3 = external-http export WPT_EXTRATESTS=0 + +# Check all commits +# 0 = latest +# 1 = all +export WPT_COMMITS=0 + + ``` Configure the folder where the WordPress software downloads and the database accesses will be made in order to prepare the tests. @@ -290,7 +316,7 @@ If you follow these steps, everything should work perfectly and not make any mis Even if the test has failed, a report will be made. The first one shows the information about our environment. Among the most important elements are the extensions that are commonly used in WordPress and some utilities that are also generally useful. ```bash -cat /tmp/wp-test-runner/tests/phpunit/build/logs/env.json +cat /tmp/wp-test-runner-(hash)/tests/phpunit/build/logs/env.json ``` The content of this file is somewhat similar to this: @@ -328,7 +354,7 @@ The content of this file is somewhat similar to this: In addition to this report, a definitive file with all the information of what happened in the tests. This is the one that includes all the tests that are made (more than 10,000) giving information of the time that they take to be executed, problems that have arisen… ```bash -cat /tmp/wp-test-runner/tests/phpunit/build/logs/junit.xml +cat /tmp/wp-test-runner-(hash)/tests/phpunit/build/logs/junit.xml ``` At this point we can generate the reports by sending them to WordPress.org, if necessary. Even if you haven’t included the WordPress user (see below for how to create it), you can still run this file. From e1abdadb9b07344d47b6a7a57e6689c772f0ad96 Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Sun, 29 Sep 2024 08:41:19 +0200 Subject: [PATCH 29/30] documentation will be on the readme --- docs/0-intro.md | 15 --- docs/1-setup.md | 333 ------------------------------------------------ 2 files changed, 348 deletions(-) delete mode 100644 docs/0-intro.md delete mode 100644 docs/1-setup.md diff --git a/docs/0-intro.md b/docs/0-intro.md deleted file mode 100644 index 2630b66..0000000 --- a/docs/0-intro.md +++ /dev/null @@ -1,15 +0,0 @@ -# About the PHPUnit Test Runner - -Hosting companies can have several to millions of websites hosted with WordPress, so it's important to make sure their configuration is as compatible as possible with the software. - -To verify this compatibility, the WordPress Community provides a series of PHPUnit tests with which to check the operation of WordPress in any environment. - -The Runner tests generates a report with the test results related to a bot user (a hosting company), and this captures and displays those test results at the [Host Test Result](https://make.wordpress.org/hosting/test-results/) page. - -## What's the phpunit-test-runner - -The [phpunit-test-runner](https://github.com/WordPress/phpunit-test-runner) is a tool designed to make it easier for hosting companies to run the WordPress project’s automated tests. - -There is a [whole documentation about this tool](https://make.wordpress.org/hosting/test-results-getting-started/). Also, if you want, you can make your test results appear in the [Host Test Results](https://make.wordpress.org/hosting/test-results/) page of WordPress. - -The tool can be run manually or through an automated system like Travis. To see how it works and the purpose of this document, will be shown how to run the tests manually. diff --git a/docs/1-setup.md b/docs/1-setup.md deleted file mode 100644 index c7f2156..0000000 --- a/docs/1-setup.md +++ /dev/null @@ -1,333 +0,0 @@ -# Requirements - -To use the Runner, the following is required: - -- A server / hosting (infrastructure) with the usual configuration you have. -- A database (MySQL, MariaDB, or WordPress compatible) where you can test (it will be created and destroyed several times) -- NodeJS 20.x -- git, wget, rsync, zip, unzip... - -## NodeJS installation - -If you are using Debian / Ubuntu, install or update NodeJS 20 with this command: - -``` -curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - -sudo apt -y install nodejs -node -v -npm install -g npm@latest -npm --version -``` - -If you are using RHEL / CentOS, install or update NodeJS 20 with this command: - -``` -curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo -E bash - -sudo yum install -y nodejs -node -v -npm install -g npm@latest -npm --version -``` - -## Composer installation - -``` -curl -sS https://getcomposer.org/installer -o composer-setup.php -php composer-setup.php --install-dir=/usr/local/bin --filename=composer -composer --version -``` - -# Installing the Runner - -First, download the software. This example use `/home/wptestrunner/` folder, but set the best for this environment. - -``` -cd /home/wptestrunner/ -git clone https://github.com/WordPress/phpunit-test-runner.git -cd phpunit-test-runner/ -``` - -The next step will be to configure the environment. To do this, make a copy of the example file and then configure it. - -``` -cp .env.default .env -vim .env -``` - -This is the default configuraton file: - -``` -### -# Configuration environment variables used by the test runner -# -# # Create a copy for your local environment -# $ cp .env.default .env -# -# # Make any necessary changes to the default values -# $ vim .env -# -# # Load your variables into your environment -# $ source .env -### - -# Label for the environment. Can be empty (default) or be like "shared", "vps", "cloud" or similar. -# Please use only alphanumeric keywords, and try to be descriptive -export WPT_LABEL= - -# Path to the directory where files can be prepared before being delivered to the environment. -export WPT_PREPARE_DIR=/tmp/wp-test-runner - -# Path to the directory where the WordPress develop checkout can be placed and tests can be run. -# When running tests in the same environment, set WPT_TEST_DIR to WPT_PREPARE_DIR -export WPT_TEST_DIR=/tmp/wp-test-runner - -# API key to authenticate with the reporting service in 'username:password' format. -export WPT_REPORT_API_KEY= - -# (Optionally) define an alternate reporting URL -export WPT_REPORT_URL= - -# Credentials for a database that can be written to and reset. -# WARNING!!! This database will be destroyed between tests. Only use safe database credentials. -# Please note that you must escape _or_ refrain from using # as special character in your credentials. -export WPT_DB_NAME= -export WPT_DB_USER= -export WPT_DB_PASSWORD= -export WPT_DB_HOST= - -# (Optionally) set a custom table prefix to permit concurrency against the same database. -export WPT_TABLE_PREFIX=${WPT_TABLE_PREFIX-wptests_} - -# (Optionally) define the PHP executable to be called -export WPT_PHP_EXECUTABLE=${WPT_PHP_EXECUTABLE-php} - -# (Optionally) array of versions (like: 8.0=/bin/php8.0;8.1=/bin/php8.1) -export WPT_PHP_EXECUTABLE_MULTI= - -# (Optionally) define the PHPUnit command execution call. -# Use if `php phpunit.phar` can't be called directly for some reason. -export WPT_PHPUNIT_CMD= - -# (Optionally) define the command execution to remove the test directory -# Use if `rm -r` can't be called directly for some reason. -export WPT_RM_TEST_DIR_CMD= - -# SSH connection string (can also be an alias). -# Leave empty if tests are meant to run in the same environment. -export WPT_SSH_CONNECT= - -# Any options to be passed to the SSH connection -# Defaults to '-o StrictHostKeyChecking=no' -export WPT_SSH_OPTIONS= - -# SSH private key, base64 encoded. -export WPT_SSH_PRIVATE_KEY_BASE64= - -# Output logging -# Use 'verbose' to increase verbosity -export WPT_DEBUG= - -# Certificate validation -# Use 1 to validate, and 0 to not validate -export WPT_CERTIFICATE_VALIDATION=0 - -# WordPress flavor -# 0 = WordPress (simple version) -# 1 = WordPress Multisite -export WPT_FLAVOR=0 - -# Extra tests (groups) -# 0 = none -# 1 = ajax -# 2 = ms-files -# 3 = external-http -export WPT_EXTRATESTS=0 - -# Check all commits -# 0 = latest -# 1 = all -export WPT_COMMITS=0 -```` - -And this could be an example of each part: - -**Label** - -Label for the environment. Can be empty (default) or be like "shared", "vps", "cloud" or similar. Please use only alphanumeric keywords, and try to be descriptive - -``` -export WPT_LABEL=shared -``` - -**Preparation directory** - -Path to the directory where files can be prepared before being delivered to the environment. - -Usually can be a /tmp/ folder so it does everything temporary. - -``` -export WPT_PREPARE_DIR=/tmp/wp-test-runner -``` - -**Test directory** - -Path to the directory where the WordPress develop checkout can be placed and tests can be run. When running tests in the same environment, set WPT_TEST_DIR to WPT_PREPARE_DIR equally. - - -``` -export WPT_TEST_DIR=/tmp/wp-test-runner -``` - -**API KEY** - -API key to authenticate with the reporting service in 'username:password' format. This is only needed if you want to publish your results in the WordPress site, so data can help developers to improve WordPress. - -Read: [How to report: Creating your bot for WordPress.org](https://make.wordpress.org/hosting/handbook/tests/#how-to-report-creating-your-bot-for-wordpress-org) - -``` -export WPT_REPORT_API_KEY=userbot:12345ABCDE67890F -``` - -**Reporting URL** - -(Optionally) Define an alternate reporting URL, if you are running your own website. - -It should look like: -`https://reporter.example.com/wp-json/wp-unit-test-api/v1/results` - -``` -export WPT_REPORT_URL=https://reporter.example.com/wp-json/wp-unit-test-api/v1/results -``` - -**Database credentials** - -Credentials for a database that can be written to and reset. - -WARNING: This database will be destroyed between tests. Only use safe database credentials. - -Please note that you must escape _or_ refrain from using # as special character in your credentials. - - -``` -export WPT_DB_NAME=testbot -export WPT_DB_USER=wpuser -export WPT_DB_PASSWORD=wppassword -export WPT_DB_HOST=localhost -``` - -**Tables Custom prefix** - -(Optionally) Set a custom table prefix to allow concurrency against the same database. This is very useful if you activate the multi-php or multi-environment part. - -``` -export WPT_TABLE_PREFIX=${WPT_TABLE_PREFIX-wptests_} -``` - -**PHP versions** - -_There are two options for backward compatibility._ - -The first one is the binary file / path for the default PHP. If it's empty it will use "php" as the command. - -``` -export WPT_PHP_EXECUTABLE=${WPT_PHP_EXECUTABLE-php} -``` - -The second one is the optional part. This allow to test more than one PHP versions. The format to use is: - -_majorversion1+binary_path1,majorversion2+binary_path2_ - -something like: - -`8.0=/bin/php8.0;8.1=/bin/php8.1` - -Use as much versions as you want, but it will take more time. The idea is to put all the versions offered to users. - -``` -export WPT_PHP_EXECUTABLE_MULTI=7.4=/bin/php7.4;8.3=/bin/php8.3 -``` - -**PHPUnit execution call** - -(Optionally) define the PHPUnit command execution call. Use if `php phpunit.phar` can't be called directly for some reason. - -``` -export WPT_PHPUNIT_CMD= -``` - -**** - -``` -# (Optionally) define the command execution to remove the test directory -# Use if `rm -r` can't be called directly for some reason. -export WPT_RM_TEST_DIR_CMD= -``` - - -``` -# SSH connection string (can also be an alias). -# Leave empty if tests are meant to run in the same environment. -export WPT_SSH_CONNECT= -``` - - -``` -# Any options to be passed to the SSH connection -# Defaults to '-o StrictHostKeyChecking=no' -export WPT_SSH_OPTIONS= -``` - - -``` -# SSH private key, base64 encoded. -export WPT_SSH_PRIVATE_KEY_BASE64= -``` - - -``` -# Output logging -# Use 'verbose' to increase verbosity -export WPT_DEBUG= -``` - -``` -# Certificate validation -# Use 1 to validate, and 0 to not validate -export WPT_CERTIFICATE_VALIDATION=0 -``` - -``` -# WordPress flavor -# 0 = WordPress (simple version) -# 1 = WordPress Multisite -export WPT_FLAVOR=0 -``` - -``` -# Extra tests (groups) -# 0 = none -# 1 = ajax -# 2 = ms-files -# 3 = external-http -export WPT_EXTRATESTS=0 -``` - -``` -# Check all commits -# 0 = latest -# 1 = all -export WPT_COMMITS=0 -``` - - -Configure the folder where the WordPress software downloads and the database accesses will be made in order to prepare the tests. - -# Preparing the environment - -Before performing the first test, let’s update all the components. This process can be run before each test in this environment if wanted to keep it up to date, although it will depend more if it is in a production environment. - -``` -cd /home/wptestrunner/phpunit-test-runner/ -git pull -source .env -``` From 592e6c7dab5d63e77989269d72738a0814056627 Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Sun, 29 Sep 2024 09:00:54 +0200 Subject: [PATCH 30/30] Documentation - better documentation - details on each value at the configuration file - scripts so you can run (and update) the software automatically. --- README.md | 324 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 286 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 6597e1d..815d6d0 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,21 @@ +# About the PHPUnit Test Runner + +Hosting companies can have several to millions of websites hosted with WordPress, so it's important to make sure their configuration is as compatible as possible with the software. + +To verify this compatibility, the WordPress Community provides a series of PHPUnit tests with which to check the operation of WordPress in any environment. + +The Runner tests generates a report with the test results related to a bot user (a hosting company), and this captures and displays those test results at the [Host Test Result](https://make.wordpress.org/hosting/test-results/) page. + +## What's the phpunit-test-runner + +The [phpunit-test-runner](https://github.com/WordPress/phpunit-test-runner) is a tool designed to make it easier for hosting companies to run the WordPress project’s automated tests. + +There is a [whole documentation about this tool](https://make.wordpress.org/hosting/test-results-getting-started/). Also, if you want, you can make your test results appear in the [Host Test Results](https://make.wordpress.org/hosting/test-results/) page of WordPress. + +The tool can be run manually or through an automated system like Travis. To see how it works and the purpose of this document, will be shown how to run the tests manually. + +--- + # PHPUnit Test Runner Thanks for running the WordPress PHPUnit test suite on your infrastructure. We appreciate you helping to ensure WordPress’s compatibility for your users. @@ -13,7 +31,7 @@ At a high level, the test suite runner: 3. Reports the PHPUnit test results to WordPress.org 4. Cleans up the test suite environment. -## Setup +## Quick setup The test suite runner can be used in one of two ways: @@ -70,13 +88,9 @@ Host wpt ssh wpt ``` -## Running - -The test suite runner is run in four steps. This explanation is for the local execution. - -### Requirements +## Requirements -To use the Runner, the following is required (testing WordPress 6.5): +To use the Runner, the following is required (testing WordPress 6.7-alpha): - Server / hosting (infrastructure) with the usual configuration you use - A database where you can test (tables will be created and destroyed several times) @@ -91,9 +105,9 @@ Test environment: - Writable filesystem for the entire test directory (see [#40910](https://core.trac.wordpress.org/ticket/40910)). - Run with a non-root user, both for security and practical purposes (see [#44233](https://core.trac.wordpress.org/ticket/44233#comment:34)/[#46577](https://core.trac.wordpress.org/ticket/46577)). -#### Database creation +### Database creation -_This is an example for MySQL / MariaDB._ +_This is a simple example for MySQL / MariaDB._ ```sql CREATE DATABASE wordpressdatabase CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci; @@ -102,9 +116,9 @@ GRANT ALL ON wordpressdatabase.* TO 'wordpressusername'@'127.0.0.1' IDENTIFIED B FLUSH PRIVILEGES; ``` -#### NodeJS installation +### NodeJS installation -_This is an example for Debian / Ubuntu._ +_This is a simple example for Debian / Ubuntu._ ```bash curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - @@ -114,9 +128,9 @@ nodejs --version npm --version ``` -#### PHP Composer +### PHP Composer -_This is an example for Debian / Ubuntu._ +_This is a simple example for Debian / Ubuntu._ ```bash curl -sS https://getcomposer.org/installer -o composer-setup.php @@ -124,35 +138,37 @@ php composer-setup.php --install-dir=/usr/local/bin --filename=composer composer --version ``` -#### Git +### Git -_This is an example for Debian / Ubuntu._ +_This is a simple example for Debian / Ubuntu._ ```bash apt -y install git git --version ``` -### Installing the Test Runner +## Installing the Runner -First, download the software. This example uses `/home/wptestrunner/` folder, but set the best for this environment. +First, download the software. This example use `/home/wptestrunner/` folder, but set the best for this environment. -```bash +``` cd /home/wptestrunner/ git clone https://github.com/WordPress/phpunit-test-runner.git cd phpunit-test-runner/ ``` +## Configuring the Runner + The next step will be to configure the environment. To do this, make a copy of the example file and then configure it. -```bash +``` cp .env.default .env vim .env ``` -The content (in summary form) can be something like this: +This is the default configuraton file: -```bash +``` ### # Configuration environment variables used by the test runner # @@ -251,16 +267,204 @@ export WPT_EXTRATESTS=0 # 1 = all export WPT_COMMITS=0 +```` + +### Configuration file + +And this could be an example of each part: + +**Label** + +Label for the environment. Can be empty (default) or be like "shared", "vps", "cloud" or similar. Please use only alphanumeric keywords, and try to be descriptive + +``` +export WPT_LABEL="shared" +``` + +**Preparation directory** + +Path to the directory where files can be prepared before being delivered to the environment. + +Usually can be a /tmp/ folder so it does everything temporary. + +``` +export WPT_PREPARE_DIR="/tmp/wp-test-runner" +``` + +**Test directory** + +Path to the directory where the WordPress develop checkout can be placed and tests can be run. When running tests in the same environment, set WPT_TEST_DIR to WPT_PREPARE_DIR equally. + + +``` +export WPT_TEST_DIR="/tmp/wp-test-runner" +``` + +**API KEY** + +API key to authenticate with the reporting service in 'username:password' format. This is only needed if you want to publish your results in the WordPress site, so data can help developers to improve WordPress. + +Read: [How to report: Creating your bot for WordPress.org](https://make.wordpress.org/hosting/handbook/tests/#how-to-report-creating-your-bot-for-wordpress-org) + +``` +export WPT_REPORT_API_KEY="userbot:12345ABCDE67890F" +``` + +**Reporting URL** + +(Optionally) Define an alternate reporting URL, if you are running your own website. + +It should look like: +`https://reporter.example.com/wp-json/wp-unit-test-api/v1/results` + +``` +export WPT_REPORT_URL="https://reporter.example.com/wp-json/wp-unit-test-api/v1/results" +``` + +**Database credentials** + +Credentials for a database that can be written to and reset. + +WARNING: This database will be destroyed between tests. Only use safe database credentials. + +Please note that you must escape _or_ refrain from using # as special character in your credentials. + + +``` +export WPT_DB_NAME="testbot" +export WPT_DB_USER="wpuser" +export WPT_DB_PASSWORD="wppassword" +export WPT_DB_HOST="localhost" +``` + +**Tables Custom prefix** + +(Optionally) Set a custom table prefix to allow concurrency against the same database. This is very useful if you activate the multi-php or multi-environment part. + +``` +export WPT_TABLE_PREFIX=${WPT_TABLE_PREFIX-wptests_} +``` + +**PHP versions** + +_There are two options for backward compatibility._ + +The first one is the binary file / path for the default PHP. If it's empty it will use "php" as the command. + +``` +export WPT_PHP_EXECUTABLE=${WPT_PHP_EXECUTABLE-php} +``` + +The second one is the optional part. This allow to test more than one PHP versions. The format to use is: + +_majorversion1=binary_path1;majorversion2=binary_path2_ + +or, something like: + +`8.1=/bin/php8.1;8.2=/bin/php8.2;8.3=/bin/php8.3` + +Use as much versions as you want, but it will take more time. The idea is to put all the versions offered to users. + +``` +export WPT_PHP_EXECUTABLE_MULTI="7.4=/bin/php7.4;8.3=/bin/php8.3" +``` + +**PHPUnit execution call** + +(Optionally) define the PHPUnit command execution call. Use if `php phpunit.phar` can't be called directly for some reason. + +``` +export WPT_PHPUNIT_CMD="" +``` + +**Remove directory command** + +(Optionally) define the command execution to remove the test directory. Use if `rm -r` can't be called directly for some reason. + +``` +export WPT_RM_TEST_DIR_CMD="" +``` + +**SSH connection** + +SSH connection string (can also be an alias). Leave empty if tests are meant to run in the same environment. + +``` +export WPT_SSH_CONNECT="" +``` + +**SSH connection options** + +Any options to be passed to the SSH connection. Defaults to `-o StrictHostKeyChecking=no`. + +``` +export WPT_SSH_OPTIONS="" +``` + +**SSH private key** + +SSH private key, base64 encoded. + +``` +export WPT_SSH_PRIVATE_KEY_BASE64="" +``` + +**Output logging** + +Output logging. Use 'verbose' to increase verbosity. + +``` +export WPT_DEBUG="" +``` + +**Certificate validation** + +TLS Certificate validation. Use `1` to validate, and `0` to not validate. + +This may be useful in some environments with OpenSSL limitations (usually local environments). + +``` +export WPT_CERTIFICATE_VALIDATION=0 +``` + +**WordPress flavor** + +tests can check a simple WordPress installation or a WordPress Multisite installation. Pick your favor: +Use `0` for a simple WordPress or `1` for a WordPress Multisite. + +``` +export WPT_FLAVOR=0 +``` + +**Extra tests (groups)** + +- `0` = none +- `1` = ajax +- `2` = ms-files +- `3` = external-http + +``` +export WPT_EXTRATESTS=0 +``` + +**Commits** + +Check all commits or only the latest. testing only the latest may cause a gap on tests, because, some times, there are more than two tests in a few minutes (and the software is not so quick). + +By default, use `0` to check the latest, or `1` to test all the commits (the latest 10 commits). + +``` +export WPT_COMMITS=0 ``` Configure the folder where the WordPress software downloads and the database accesses will be made in order to prepare the tests. -### Preparing the environment +## Preparing the environment Before performing the first test, let’s update all the components. This process can be run before each test in this environment if wanted to keep it up to date, although it will depend more if it is in a production environment. -```bash +``` cd /home/wptestrunner/phpunit-test-runner/ git pull source .env @@ -281,7 +485,7 @@ Now there is the environment ready, run the test preparation. php prepare.php ``` -The system will run a long series of installations, configurations and compilations of different elements in order to prepare the test. If warnings and warnings come out you should not worry too much, as it is quite normal. At the end of the process it will warn you if it needs something it doesn’t have. If it works, you should see something like this at the end: +The system will run a long series of installations, configurations and compilations of different elements in order to prepare the test. If warnings and warnings come out you should not worry too much, as it is quite normal. At the end of the process it will warn you if it needs something it doesn't have. If it works, you should see something like this at the end: ``` Success: Prepared environment. @@ -289,7 +493,7 @@ Success: Prepared environment. Now that the environment has been prepared, the next step is to run the tests for the first time. -### Running the test +## Running the test Now that the environment is ready, let’s run the tests. To do this, execute the file that will perform it. @@ -311,7 +515,7 @@ What do the symbols mean? If you follow these steps, everything should work perfectly and not make any mistakes. In case you get any error, it may be normal due to some missing adjustment or extension of PHP, among others. We recommend that you adjust the configuration until it works correctly. After all, this tool is to help you improve the optimal configuration for WordPress in that infrastructure. -### Creating a report +## Creating a report Even if the test has failed, a report will be made. The first one shows the information about our environment. Among the most important elements are the extensions that are commonly used in WordPress and some utilities that are also generally useful. @@ -319,6 +523,8 @@ Even if the test has failed, a report will be made. The first one shows the info cat /tmp/wp-test-runner-(hash)/tests/phpunit/build/logs/env.json ``` +_The hash (a CRC32) will depend on the PHP version you are using._ + The content of this file is somewhat similar to this: ```bash @@ -351,19 +557,21 @@ The content of this file is somewhat similar to this: } ``` -In addition to this report, a definitive file with all the information of what happened in the tests. This is the one that includes all the tests that are made (more than 10,000) giving information of the time that they take to be executed, problems that have arisen… +In addition to this report, a definitive file with all the information of what happened in the tests. This is the one that includes all the tests that are made (more than 20,000) giving information of the time that they take to be executed, problems that have arisen… ```bash cat /tmp/wp-test-runner-(hash)/tests/phpunit/build/logs/junit.xml ``` +_The hash (a CRC32) will depend on the PHP version you are using._ + At this point we can generate the reports by sending them to WordPress.org, if necessary. Even if you haven’t included the WordPress user (see below for how to create it), you can still run this file. ```bash php report.php ``` -### Cleaning up the environment for other tests +## Cleaning up the environment for other tests Having the tests working, all that remains is to delete all the files that have been created so that we can start over. To do this, execute the following command: @@ -371,24 +579,64 @@ Having the tests working, all that remains is to delete all the files that have php cleanup.php ``` -### Automatic running +## Automatic running + +There are many ways to execute tests automatically. In many cases, it will depend on what you want to do and test. + +Keep in mind some aspects: + +- Tests usually take several minutes, but the system includes a mechanism to avoid repeating them. This can result in durations ranging from hours to seconds. +- When configuring the system, ensure that tests do not overlap by implementing a system that prevents a test from being re-executed if it is already running. +- Tests update their software. The configuration file is typically compatible with previous versions. + +Some suggestions: + +- Create a script (for example, in Bash) that includes all the steps to execute, including: + - Updating the Git branch + - Loading the configuration file + - Executing the 4 steps / PHP files. +- Use a task scheduling system: + - Using cron + - Using systemd.timer + +### Script en Bash + +This is a simple example of a Bash script that could be placed in the directory above the software. For example, at `/home/wptestrunner/`. + +```bash +cat > /home/wptestrunner/testrunner.sh << EOF +cd /home/wptestrunner/phpunit-test-runner/ +git pull +git checkout master +source .env +php prepare.php +php test.php +php report.php +php cleanup.php +EOF +chmod +x /home/wptestrunner/testrunner.sh +``` + +From this moment on, if you run `bash /home/wptestrunner/testrunner.sh`, the test will execute once. + +### systemd timer -The best way to run this test is to create a cron that runs everything. Having in mind that the tests can overlap, the best way can be using a systemd timer. +The best way to run this test is to create a cron that runs everything. Having in mind that the tests can overlap, the best way can be using a systemd timer and the bash script. ```bash -cat > /etc/systemd/system/wordpressphpunittestrunner.service << EOF +cat > /etc/systemd/system/testrunner.service << EOF [Unit] Description=WordPress PHPUnit Test Runner [Service] Type=oneshot -ExecStart=cd /home/wptestrunner/phpunit-test-runner/ && source .env && php prepare.php && php test.php && php report.php && php cleanup.php +ExecStart=bash /home/wptestrunner/testrunner.sh User=wptestrunner Group=wptestrunner EOF ``` ```bash -cat > /etc/systemd/system/wordpressphpunittestrunner.timer << EOF +cat > /etc/systemd/system/testrunner.timer << EOF [Unit] Description=WordPress PHPUnit Test Runner [Timer] @@ -401,16 +649,16 @@ EOF ```bash systemctl daemon-reload -systemctl enable wordpressphpunittestrunner.timer -systemctl start wordpressphpunittestrunner.timer -systemctl status wordpressphpunittestrunner.timer +systemctl enable testrunner.timer +systemctl start testrunner.timer +systemctl status testrunner.timer ``` If you want to check how is everything working... ```bash -journalctl -u wordpressphpunittestrunner.timer -journalctl -n 120 -u wordpressphpunittestrunner.service +journalctl -u testrunner.timer +journalctl -n 120 -u testrunner.service ``` ## Contributing