diff --git a/.readme-partials/USING.md b/.readme-partials/USING.md index 66f1af28..6904731e 100644 --- a/.readme-partials/USING.md +++ b/.readme-partials/USING.md @@ -202,6 +202,7 @@ The following environment variables can be set to override the default database - `WP_CLI_TEST_DBUSER` is the user that the tests run under (defaults to "wp_cli_test"). - `WP_CLI_TEST_DBPASS` is the password to use for the above user (defaults to "password1"). - `WP_CLI_TEST_DBTYPE` is the database engine type to use, i.e. "sqlite" for running tests on SQLite instead of MySQL (defaults to "mysql"). + - `WP_CLI_TEST_OBJECT_CACHE` is the persistent object cache backend to use. Only supports "sqlite". Environment variables can be set for the whole session via the following syntax: `export WP_CLI_TEST_DBNAME=custom_db`. diff --git a/features/testing.feature b/features/testing.feature index 7facf1ef..8705258c 100644 --- a/features/testing.feature +++ b/features/testing.feature @@ -46,6 +46,15 @@ Feature: Test that WP-CLI loads. false """ + @require-object-cache + Scenario: Uses Object Cache + Given a WP install + When I run `wp eval 'var_export( wp_using_ext_object_cache() );'` + Then STDOUT should be: + """ + true + """ + @require-sqlite Scenario: Custom wp-content directory Given a WP install diff --git a/src/Context/FeatureContext.php b/src/Context/FeatureContext.php index 9a576f71..494d3454 100644 --- a/src/Context/FeatureContext.php +++ b/src/Context/FeatureContext.php @@ -85,6 +85,11 @@ class FeatureContext implements Context { */ private static $sqlite_cache_dir; + /** + * @var ?string + */ + private static $sqlite_object_cache_dir; + /** * The directory that the WP-CLI cache (WP_CLI_CACHE_DIR, normally "$HOME/.wp-cli/cache") is set to on a "Given an empty cache" step. * Variable SUITE_CACHE_DIR. Lives until the end of the scenario (or until another "Given an empty cache" step within the scenario). @@ -599,6 +604,42 @@ private static function download_sqlite_plugin( $dir ): void { } } + /** + * Download the SQLite object cache plugin. + * + * @param string $dir + */ + private static function download_sqlite_object_cache_plugin( $dir ): void { + $download_url = 'https://downloads.wordpress.org/plugin/sqlite-object-cache.zip'; + $download_location = $dir . '/sqlite-object-cache.zip'; + + if ( ! is_dir( $dir ) ) { + mkdir( $dir ); + } + + $response = \WP_CLI\Utils\http_request( 'GET', $download_url, null, [], [ 'filename' => $download_location ] ); + + if ( 200 !== $response->status_code ) { + throw new RuntimeException( "Could not download SQLite object cache plugin (HTTP code {$response->status_code})" ); + } + + $zip = new \ZipArchive(); + $new_zip_file = $download_location; + + if ( $zip->open( $new_zip_file ) === true ) { + if ( $zip->extractTo( $dir ) ) { + $zip->close(); + unlink( $new_zip_file ); + } else { + $error_message = $zip->getStatusString(); + throw new RuntimeException( sprintf( 'Failed to extract files from the zip: %s', $error_message ) ); + } + } else { + $error_message = $zip->getStatusString(); + throw new RuntimeException( sprintf( 'Failed to open the zip file: %s', $error_message ) ); + } + } + /** * Given a WordPress installation with the sqlite-database-integration plugin, * configure it to use SQLite as the database by placing the db.php dropin file @@ -651,6 +692,13 @@ private static function cache_wp_files( $version = '' ): void { } } + if ( 'sqlite' === getenv( 'WP_CLI_TEST_OBJECT_CACHE' ) ) { + self::$sqlite_object_cache_dir = sys_get_temp_dir() . '/wp-cli-test-sqlite-object-cache'; + if ( ! is_dir( self::$sqlite_object_cache_dir . '/sqlite-object-cache' ) ) { + self::download_sqlite_object_cache_plugin( self::$sqlite_object_cache_dir ); + } + } + if ( is_readable( self::$cache_dir . '/wp-config-sample.php' ) ) { return; } @@ -1539,6 +1587,12 @@ public function download_wp( $subdir = '', $version = '' ): void { self::copy_dir( self::$sqlite_cache_dir, $dest_dir . '/wp-content/mu-plugins' ); self::configure_sqlite( $dest_dir ); } + + if ( 'sqlite' === getenv( 'WP_CLI_TEST_OBJECT_CACHE' ) ) { + self::copy_dir( self::$sqlite_object_cache_dir, $dest_dir . '/wp-content/mu-plugins' ); + copy( $dest_dir . '/wp-content/mu-plugins/sqlite-object-cache/assets/drop-in/object-cache.php', $dest_dir . '/wp-content/object-cache.php' ); + file_put_contents( $dest_dir . '/wp-content/mu-plugins/sqlite-object-cache.php', "create_db(); } @@ -1615,7 +1675,7 @@ public function install_wp( $subdir = '', $version = '' ): void { $run_dir = '' !== $subdir ? ( $this->variables['RUN_DIR'] . "/$subdir" ) : $this->variables['RUN_DIR']; - $install_cache_path = self::$install_cache_dir . '/install_' . md5( implode( ':', $install_args ) . ':subdir=' . $subdir ); + $install_cache_path = self::$install_cache_dir . '/install_' . md5( implode( ':', $install_args ) . ':subdir=' . $subdir . ':object_cache=' . getenv( 'WP_CLI_TEST_OBJECT_CACHE' ) ); $install_cache_is_valid = is_dir( $install_cache_path ) && ( 'sqlite' !== self::$db_type || file_exists( "{$install_cache_path}.sqlite" ) ); @@ -1726,6 +1786,11 @@ public function install_wp_with_composer( $vendor_directory = 'vendor' ): void { $config_extra_php .= "require_once dirname(__DIR__) . '/" . $vendor_directory . "/autoload.php';\n"; + if ( 'sqlite' === getenv( 'WP_CLI_TEST_OBJECT_CACHE' ) ) { + // Use a deterministic salt per install to allow create_config() to reuse cached configs. + $config_extra_php .= "define( 'WP_CACHE_KEY_SALT', '" . md5( $this->variables['RUN_DIR'] ) . "' );\n"; + } + $this->create_config( 'WordPress', $config_extra_php ); $install_args = [ @@ -1747,6 +1812,12 @@ public function install_wp_with_composer( $vendor_directory = 'vendor' ): void { self::configure_sqlite( $this->variables['RUN_DIR'] . '/WordPress' ); } + if ( 'sqlite' === getenv( 'WP_CLI_TEST_OBJECT_CACHE' ) ) { + self::copy_dir( self::$sqlite_object_cache_dir, $this->variables['RUN_DIR'] . '/WordPress/wp-content/mu-plugins' ); + copy( $this->variables['RUN_DIR'] . '/WordPress/wp-content/mu-plugins/sqlite-object-cache/assets/drop-in/object-cache.php', $this->variables['RUN_DIR'] . '/WordPress/wp-content/object-cache.php' ); + file_put_contents( $this->variables['RUN_DIR'] . '/WordPress/wp-content/mu-plugins/sqlite-object-cache.php', "proc( 'wp core install', $install_args )->run_check(); } @@ -1894,9 +1965,16 @@ private static function dir_diff_copy( $upd_dir, $src_dir, $cop_dir ): void { throw new RuntimeException( sprintf( "Failed to create copy directory '%s': %s. " . __FILE__ . ':' . __LINE__, $cop_file, $error['message'] ) ); } self::copy_dir( $upd_file, $cop_file ); - } elseif ( ! copy( $upd_file, $cop_file ) ) { - $error = error_get_last(); - throw new RuntimeException( sprintf( "Failed to copy '%s' to '%s': %s. " . __FILE__ . ':' . __LINE__, $upd_file, $cop_file, $error['message'] ) ); + } elseif ( ! file_exists( $upd_file ) ) { + continue; // File vanished before it could be copied. + } else { + if ( ! is_dir( dirname( $cop_file ) ) ) { + mkdir( dirname( $cop_file ), 0777, true ); + } + if ( ! copy( $upd_file, $cop_file ) ) { + $error = error_get_last(); + throw new RuntimeException( sprintf( "Failed to copy '%s' to '%s': %s. " . __FILE__ . ':' . __LINE__, $upd_file, $cop_file, $error['message'] ) ); + } } } elseif ( is_dir( $upd_file ) ) { self::dir_diff_copy( $upd_file, $src_file, $cop_file ); diff --git a/tests/tests/TestBehatTags.php b/tests/tests/TestBehatTags.php index 25a515b6..c804d59a 100644 --- a/tests/tests/TestBehatTags.php +++ b/tests/tests/TestBehatTags.php @@ -113,6 +113,9 @@ public function test_behat_tags_wp_version_github_token( $env, $expected ): void $output = $this->run_behat_tags_script( $env ); $expected .= '&&~@broken'; + if ( 'sqlite' !== getenv( 'WP_CLI_TEST_OBJECT_CACHE' ) ) { + $expected .= '&&~@require-object-cache'; + } if ( in_array( $env, array( 'WP_VERSION=trunk', 'WP_VERSION=nightly' ), true ) ) { $expected .= '&&~@broken-trunk'; } @@ -200,6 +203,9 @@ public function test_behat_tags_php_version(): void { } $expected .= '&&~@github-api&&~@broken'; + if ( 'sqlite' !== getenv( 'WP_CLI_TEST_OBJECT_CACHE' ) ) { + $expected .= '&&~@require-object-cache'; + } $db_type = getenv( 'WP_CLI_TEST_DBTYPE' ); switch ( $db_type ) { @@ -270,7 +276,11 @@ public function test_behat_tags_extension(): void { $expecteds[] = '~@require-extension-curl'; } - $expected = '--tags=' . implode( '&&', array_merge( array( '~@github-api', '~@broken' ), $expecteds ) ); + $base_expecteds = array( '~@github-api', '~@broken' ); + if ( 'sqlite' !== getenv( 'WP_CLI_TEST_OBJECT_CACHE' ) ) { + $base_expecteds[] = '~@require-object-cache'; + } + $expected = '--tags=' . implode( '&&', array_merge( $base_expecteds, $expecteds ) ); $output = $this->run_behat_tags_script(); $this->assertSame( $expected, $output ); @@ -322,7 +332,11 @@ public function test_behat_tags_db_version(): void { file_put_contents( $this->temp_dir . DIRECTORY_SEPARATOR . 'features' . DIRECTORY_SEPARATOR . 'extension.feature', $contents ); - $expected = '--tags=' . implode( '&&', array_merge( array( '~@github-api', '~@broken' ), $expecteds ) ); + $base_expecteds = array( '~@github-api', '~@broken' ); + if ( 'sqlite' !== getenv( 'WP_CLI_TEST_OBJECT_CACHE' ) ) { + $base_expecteds[] = '~@require-object-cache'; + } + $expected = '--tags=' . implode( '&&', array_merge( $base_expecteds, $expecteds ) ); $output = $this->run_behat_tags_script(); $this->assertSame( $expected, $output ); } @@ -380,7 +394,11 @@ public function test_behat_tags_os(): void { $expecteds[] = '~@skip-linux'; } - $expected = '--tags=' . implode( '&&', array_merge( array( '~@github-api', '~@broken' ), $expecteds ) ); + $base_expecteds = array( '~@github-api', '~@broken' ); + if ( 'sqlite' !== getenv( 'WP_CLI_TEST_OBJECT_CACHE' ) ) { + $base_expecteds[] = '~@require-object-cache'; + } + $expected = '--tags=' . implode( '&&', array_merge( $base_expecteds, $expecteds ) ); $output = $this->run_behat_tags_script(); $this->assertSame( $expected, $output ); @@ -419,7 +437,11 @@ public function test_behat_tags_skip_db_type(): void { break; } - $expected = '--tags=' . implode( '&&', array_merge( array( '~@github-api', '~@broken' ), $expecteds ) ); + $base_expecteds = array( '~@github-api', '~@broken' ); + if ( 'sqlite' !== getenv( 'WP_CLI_TEST_OBJECT_CACHE' ) ) { + $base_expecteds[] = '~@require-object-cache'; + } + $expected = '--tags=' . implode( '&&', array_merge( $base_expecteds, $expecteds ) ); $output = $this->run_behat_tags_script(); $this->assertSame( $expected, $output ); diff --git a/utils/behat-tags.php b/utils/behat-tags.php index 1293ed22..a032917d 100644 --- a/utils/behat-tags.php +++ b/utils/behat-tags.php @@ -166,6 +166,10 @@ function get_db_version() { # Skip tests known to be broken. $skip_tags[] = '@broken'; +if ( 'sqlite' !== getenv( 'WP_CLI_TEST_OBJECT_CACHE' ) ) { + $skip_tags[] = '@require-object-cache'; +} + if ( $wp_version && in_array( $wp_version, array( 'nightly', 'trunk' ), true ) ) { $skip_tags[] = '@broken-trunk'; }