Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .readme-partials/USING.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,20 @@ WP_VERSION=6.4.2 WP_CLI_TEST_CORE_ZIP=~/Downloads/wordpress-6.4.2.zip composer b
Note that steps requesting an explicit version, such as `Given a WP 6.4.2 installation`, keep
downloading that version from WordPress.org and ignore the archive.

#### Environment-specific scenarios

Some scenarios can only run in certain environments. Tagging them makes the test framework
filter them out everywhere else, rather than having them fail for reasons unrelated to what
they test.

* `@require-wp-stable` — the scenario needs a version of WordPress that WordPress.org knows
about, such as one verifying an installation against the published checksums. It is
skipped when `WP_CLI_TEST_CORE_ZIP` is set, and when `WP_VERSION` is `trunk` or `nightly`.
* `@require-mysql-socket` — the scenario connects to the database through a socket. It is
skipped when there is none, which is the case when the database server runs in a
container and is only reachable over TCP. Set `WP_CLI_TEST_DBSOCKET` to point at the
socket if it lives somewhere unusual.

#### WP-CLI Binary

You can run the tests against a specific WP-CLI binary, instead of using the one that has been built in your project's `vendor/bin` folder.
Expand Down
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,20 @@ WP_VERSION=6.4.2 WP_CLI_TEST_CORE_ZIP=~/Downloads/wordpress-6.4.2.zip composer b
Note that steps requesting an explicit version, such as `Given a WP 6.4.2 installation`, keep
downloading that version from WordPress.org and ignore the archive.

#### Environment-specific scenarios

Some scenarios can only run in certain environments. Tagging them makes the test framework
filter them out everywhere else, rather than having them fail for reasons unrelated to what
they test.

* `@require-wp-stable` — the scenario needs a version of WordPress that WordPress.org knows
about, such as one verifying an installation against the published checksums. It is
skipped when `WP_CLI_TEST_CORE_ZIP` is set, and when `WP_VERSION` is `trunk` or `nightly`.
* `@require-mysql-socket` — the scenario connects to the database through a socket. It is
skipped when there is none, which is the case when the database server runs in a
container and is only reachable over TCP. Set `WP_CLI_TEST_DBSOCKET` to point at the
socket if it lives somewhere unusual.

#### WP-CLI Binary

You can run the tests against a specific WP-CLI binary, instead of using the one that has been built in your project's `vendor/bin` folder.
Expand Down
114 changes: 114 additions & 0 deletions tests/tests/TestBehatTags.php
Original file line number Diff line number Diff line change
Expand Up @@ -459,4 +459,118 @@ public function test_behat_tags_skip_db_type(): void {

putenv( false === $env_github_token ? 'GITHUB_TOKEN' : "GITHUB_TOKEN=$env_github_token" );
}

/**
* Write a feature file carrying the given tag.
*
* @param string $tag
*/
private function write_tagged_feature( $tag ): void {
file_put_contents(
$this->temp_dir . DIRECTORY_SEPARATOR . 'features' . DIRECTORY_SEPARATOR . 'tagged.feature',
$tag . "\nFeature: Tagged\n"
);
}

/**
* Run the script with the given environment, restoring it afterwards.
*
* @param array<string, ?string> $env Variables to set, or null to unset.
* @return string|false
*/
private function run_with_env( array $env ) {
$previous = array();

foreach ( $env as $name => $value ) {
$current = getenv( $name );
$previous[ $name ] = false === $current ? null : $current;

putenv( null === $value ? $name : "{$name}={$value}" );
}

try {
return $this->run_behat_tags_script();
} finally {
foreach ( $previous as $name => $value ) {
putenv( null === $value ? $name : "{$name}={$value}" );
}
}
}

public function test_require_wp_stable_is_skipped_for_an_archive(): void {
$this->write_tagged_feature( '@require-wp-stable' );

$output = $this->run_with_env(
array(
'WP_VERSION' => '6.4.2',
'WP_CLI_TEST_CORE_ZIP' => $this->temp_dir . DIRECTORY_SEPARATOR . 'wordpress.zip',
)
);

$this->assertStringContainsString( '~@require-wp-stable', (string) $output );
}

public function test_require_wp_stable_is_skipped_on_trunk(): void {
$this->write_tagged_feature( '@require-wp-stable' );

$output = $this->run_with_env(
array(
'WP_VERSION' => 'trunk',
'WP_CLI_TEST_CORE_ZIP' => null,
)
);

$this->assertStringContainsString( '~@require-wp-stable', (string) $output );
}

public function test_require_wp_stable_runs_on_a_release(): void {
$this->write_tagged_feature( '@require-wp-stable' );

$output = $this->run_with_env(
array(
'WP_VERSION' => '6.4.2',
'WP_CLI_TEST_CORE_ZIP' => null,
)
);

$this->assertStringNotContainsString( '@require-wp-stable', (string) $output );
}

public function test_require_mysql_socket_is_skipped_without_a_socket(): void {
$this->write_tagged_feature( '@require-mysql-socket' );

$output = $this->run_with_env(
array( 'WP_CLI_TEST_DBSOCKET' => $this->temp_dir . DIRECTORY_SEPARATOR . 'missing.sock' )
);

$this->assertStringContainsString( '~@require-mysql-socket', (string) $output );
}

public function test_require_mysql_socket_runs_with_a_socket(): void {
$this->write_tagged_feature( '@require-mysql-socket' );

$socket = $this->temp_dir . DIRECTORY_SEPARATOR . 'mysql.sock';
touch( $socket );

$output = $this->run_with_env( array( 'WP_CLI_TEST_DBSOCKET' => $socket ) );

$this->assertStringNotContainsString( '@require-mysql-socket', (string) $output );
}

/**
* Tags no scenario carries do not need to be filtered out.
*/
public function test_unused_tags_are_not_filtered_out(): void {
$this->write_tagged_feature( '@some-other-tag' );

$output = $this->run_with_env(
array(
'WP_VERSION' => 'trunk',
'WP_CLI_TEST_DBSOCKET' => $this->temp_dir . DIRECTORY_SEPARATOR . 'missing.sock',
)
);

$this->assertStringNotContainsString( '@require-wp-stable', (string) $output );
$this->assertStringNotContainsString( '@require-mysql-socket', (string) $output );
}
}
80 changes: 80 additions & 0 deletions utils/behat-tags.php
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,68 @@ function get_db_version() {
return $db_info['version'];
}

/**
* Whether any feature file uses the given tag.
*
* Tags that no scenario carries do not need to be filtered out, and leaving them
* out keeps the filter to what the suite being run actually uses.
*
* @param string $tag
* @param string $features_folder
* @return bool
*/
function tag_in_use( $tag, $features_folder = 'features' ) {
$feature_files = glob( $features_folder . DIRECTORY_SEPARATOR . '*.feature' );

if ( empty( $feature_files ) ) {
return false;
}

foreach ( $feature_files as $feature_file ) {
if ( false !== strpos( (string) file_get_contents( $feature_file ), $tag ) ) {
return true;
}
}

return false;
}

/**
* Whether the database server can be reached through a socket.
*
* A database server running in a container is only reachable over TCP, so the
* scenarios that connect through a socket cannot run against one. The locations
* checked here are the same ones those scenarios look in.
*
* @return bool
*/
function has_mysql_socket() {
$socket = getenv( 'WP_CLI_TEST_DBSOCKET' );

if ( is_string( $socket ) && '' !== $socket ) {
return file_exists( $socket );
}

// Anything but a port number after the colon in the host is a socket path.
$host = getenv( 'WP_CLI_TEST_DBHOST' );

if ( is_string( $host ) && false !== strpos( $host, ':' ) ) {
$after_colon = substr( $host, strrpos( $host, ':' ) + 1 );

if ( '' !== $after_colon && ! is_numeric( $after_colon ) ) {
return file_exists( $after_colon );
}
}

foreach ( array( '/var/run/mysqld/mysqld.sock', '/tmp/mysql.sock' ) as $location ) {
if ( file_exists( $location ) ) {
return true;
}
}

return false;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

$features_folder = getenv( 'BEHAT_FEATURES_FOLDER' ) ?: 'features';
$wp_version = getenv( 'WP_VERSION' );
$wp_version_reqs = array();
Expand Down Expand Up @@ -176,6 +238,24 @@ function get_db_version() {
$skip_tags[] = '@broken-trunk';
}

// Some scenarios need a version of WordPress that WordPress.org knows about, such
// as the ones verifying an installation against the published checksums. Neither a
// development build nor an archive supplied through WP_CLI_TEST_CORE_ZIP qualifies.
$core_zip = getenv( 'WP_CLI_TEST_CORE_ZIP' );
if (
tag_in_use( '@require-wp-stable', $features_folder )
&& (
( false !== $core_zip && '' !== $core_zip )
|| in_array( $wp_version, array( 'nightly', 'trunk' ), true )
)
) {
$skip_tags[] = '@require-wp-stable';
}

if ( tag_in_use( '@require-mysql-socket', $features_folder ) && ! has_mysql_socket() ) {
$skip_tags[] = '@require-mysql-socket';
}

$db_info = get_db_type_and_version();
$db_version = $db_info['version'];
// Use detected database type from server, unless WP_CLI_TEST_DBTYPE is 'sqlite'.
Expand Down
Loading