Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .readme-partials/USING.md
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ WP_CLI_TEST_DOCKER=auto WP_CLI_TEST_PARALLEL=4 composer behat

Docker is not used on Windows, when collecting code coverage, when Docker is not available, when the image cannot be built or when the database server cannot be identified.

Each worker gets its own network, temporary directory and database server, of the same type and version as the one configured through the `WP_CLI_TEST_DB*` variables (or set the image through `WP_CLI_TEST_DOCKER_DB`, e.g. `mysql:8.0`). Nothing is shared between workers, so they can run any scenario. The database servers keep their data in memory. Scenarios selected by path or name run in a single container.
Each worker gets its own network, temporary directory and database server, of the same type and version as the one configured through the `WP_CLI_TEST_DB*` variables (or set the image through `WP_CLI_TEST_DOCKER_DB`, e.g. `mysql:8.0`, so that the host needs neither a database server nor a client). Nothing is shared between workers, so they can run any scenario. The database servers keep their data in memory. Scenarios selected by path or name run in a single container.

The containers run the same PHP version as the host, unless `WP_CLI_TEST_DOCKER_PHP` sets another one:
```bash
Expand Down
80 changes: 45 additions & 35 deletions bin/run-behat-tests
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,50 @@ is_numeric() {
esac
}

# With WP_CLI_TEST_DOCKER set, the scenarios run in throwaway containers instead, every worker in
# its own, with a database server of the same type and version, so they share nothing.
# "auto" runs them without Docker where it cannot be used, "1" (or "yes", "true", "on") fails instead.
case "${WP_CLI_TEST_DOCKER-}" in
""|0|no|false|off) DOCKER="";;
auto) DOCKER="auto";;
1|yes|true|on) DOCKER="force";;
*)
echo "Warning: WP_CLI_TEST_DOCKER is neither \"auto\" nor a yes or no, running the scenarios without Docker." >&2
DOCKER="";;
esac

without_docker() {
if [ "${DOCKER}" = "force" ]; then
echo "Error: $1 Set WP_CLI_TEST_DOCKER to \"auto\" to run the scenarios without Docker in that case." >&2
exit 1
fi
echo "$1 Running the scenarios without Docker." >&2
DOCKER=""
}

case "$(uname -s)" in
MINGW*|MSYS*|CYGWIN*)
if [ -n "${DOCKER}" ]; then
without_docker "The scenarios cannot run in Docker containers on Windows."
fi;;
esac

if [ -n "${DOCKER}" ]; then
if ! command -v docker > /dev/null 2>&1 || ! docker info > /dev/null 2>&1; then
without_docker "Docker is not available."
elif [[ "${WP_CLI_TEST_COVERAGE}" == "true" ]]; then
without_docker "Code coverage cannot be collected in Docker containers."
fi
fi

export WP_CLI_TEST_DOCKER="${DOCKER}"

# If DB type is already set to SQLite, there's nothing to do.
# Neither is there when the containers bring their own database server of a given image.
if [ "${WP_CLI_TEST_DBTYPE-}" = "sqlite" ]; then
echo "WP_CLI_TEST_DBTYPE is set to 'sqlite', skipping database check."
elif [ -n "${DOCKER}" ] && [ -n "${WP_CLI_TEST_DOCKER_DB-}" ]; then
Comment thread
schlessera marked this conversation as resolved.
Resolved
echo "The database servers run in Docker containers of ${WP_CLI_TEST_DOCKER_DB}, skipping database check."
else
# Check for database client and connectivity.
DB_CLIENT=""
Expand Down Expand Up @@ -90,6 +131,9 @@ else
fi
fi

# The version as requested, before it is resolved below, for a run that starts over without Docker.
export WP_CLI_TEST_REQUESTED_WP_VERSION="${WP_VERSION-}"

# When installing from an arbitrary archive, there is no version to resolve against
# WordPress.org. Default to "trunk" so that no @require-wp tags are filtered out, as
# such an archive is usually a development build. Set WP_VERSION explicitly alongside
Expand Down Expand Up @@ -276,44 +320,10 @@ done
# to be allowed to create, as set up by "composer prepare-tests".
PARALLEL="${WP_CLI_TEST_PARALLEL-}"

# With WP_CLI_TEST_DOCKER set, the scenarios run in throwaway containers instead, every worker in
# its own, with a database server of the same type and version, so they share nothing.
# "auto" runs them without Docker where it cannot be used, "1" (or "yes", "true", "on") fails instead.
case "${WP_CLI_TEST_DOCKER-}" in
""|0|no|false|off) DOCKER="";;
auto) DOCKER="auto";;
1|yes|true|on) DOCKER="force";;
*)
echo "Warning: WP_CLI_TEST_DOCKER is neither \"auto\" nor a yes or no, running the scenarios without Docker." >&2
DOCKER="";;
esac

without_docker() {
if [ "${DOCKER}" = "force" ]; then
echo "Error: $1 Set WP_CLI_TEST_DOCKER to \"auto\" to run the scenarios without Docker in that case." >&2
exit 1
fi
echo "$1 Running the scenarios without Docker." >&2
DOCKER=""
}

case "$(uname -s)" in
MINGW*|MSYS*|CYGWIN*)
PARALLEL=""
if [ -n "${DOCKER}" ]; then
without_docker "The scenarios cannot run in Docker containers on Windows."
fi;;
MINGW*|MSYS*|CYGWIN*) PARALLEL="";;
esac

if [ -n "${DOCKER}" ]; then
if ! command -v docker > /dev/null 2>&1 || ! docker info > /dev/null 2>&1; then
without_docker "Docker is not available."
elif [[ "${WP_CLI_TEST_COVERAGE}" == "true" ]]; then
without_docker "Code coverage cannot be collected in Docker containers."
fi
fi

export WP_CLI_TEST_DOCKER="${DOCKER}"

# A rerun after a parallel run covers exactly the scenarios that failed in it, which only the
# parallel runner knows, whether or not WP_CLI_TEST_PARALLEL is set for the rerun as well.
Expand Down
27 changes: 27 additions & 0 deletions tests/tests/TestBehatTags.php
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,33 @@ public function test_behat_tags_db_version(): void {
$this->assertSame( $expected, $output );
}

public function test_behat_tags_docker_db_image(): void {
if ( Utils\is_windows() ) {
$this->markTestSkipped( 'The scenarios do not run in Docker containers on Windows.' );
}

file_put_contents( $this->temp_dir . DIRECTORY_SEPARATOR . 'features' . DIRECTORY_SEPARATOR . 'db.feature', '@require-mysql @require-mariadb-10.6 @require-mariadb-11.4 @less-than-mariadb-10.5 @less-than-mariadb-11' );

$base_expecteds = array( '~@github-api', '~@broken' );
if ( 'sqlite' !== getenv( 'WP_CLI_TEST_OBJECT_CACHE' ) ) {
$base_expecteds[] = '~@require-object-cache';
} else {
$base_expecteds[] = '~@skip-object-cache';
}
$expecteds = array( '~@require-mysql', '~@require-sqlite', '~@skip-mariadb', '~@require-mariadb-11.4', '~@less-than-mariadb-10.5' );
$expected = '--tags=' . implode( '&&', array_merge( $base_expecteds, $expecteds ) );
$output = $this->run_with_env(
array(
'GITHUB_TOKEN' => null,
'WP_VERSION' => null,
'WP_CLI_TEST_DBTYPE' => 'mysql',
'WP_CLI_TEST_DOCKER' => 'force',
'WP_CLI_TEST_DOCKER_DB' => 'mariadb:10.11',
)
);
$this->assertSame( $expected, $output );
}

public function test_behat_tags_os(): void {
$env_github_token = getenv( 'GITHUB_TOKEN' );
$db_type = getenv( 'WP_CLI_TEST_DBTYPE' );
Expand Down
31 changes: 24 additions & 7 deletions utils/behat-parallel.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,28 @@ function wp_cli_tests_without_docker( $reason ) {
fwrite( STDERR, "{$reason} Running the scenarios without Docker.\n" );
}

/**
* Start the run over without Docker and exit with its exit code.
*
* The database check and the scenario selection were made for the containers,
* so run-behat-tests has to make them again for the host.
*
* @param array<string> $user_args
* @param array<string>|null $rerun_paths Scenarios of a rerun, which are put back for it.
*/
function wp_cli_tests_restart_without_docker( array $user_args, $rerun_paths ) {
if ( null !== $rerun_paths ) {
file_put_contents( wp_cli_tests_failures_file(), implode( "\n", $rerun_paths ) . "\n" );
$user_args[] = '--rerun';
}

$wp_version = getenv( 'WP_CLI_TEST_REQUESTED_WP_VERSION' );
putenv( false === $wp_version || '' === $wp_version ? 'WP_VERSION' : "WP_VERSION={$wp_version}" );
putenv( 'WP_CLI_TEST_DOCKER=0' );
passthru( escapeshellarg( dirname( __DIR__ ) . '/bin/run-behat-tests' ) . ' ' . implode( ' ', array_map( 'escapeshellarg', $user_args ) ), $exit_code );
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
exit( $exit_code );
}

/**
* Run Behat in the current process' environment and exit with its exit code.
*
Expand Down Expand Up @@ -567,15 +589,10 @@ function wp_cli_tests_docker_worker_command( $name, $php_image, $db_image, array

if ( null === $php_image ) {
wp_cli_tests_without_docker( 'The containers could not be prepared.' );
wp_cli_tests_restart_without_docker( $user_args, $rerun_paths );
} elseif ( null === $db_image && 'sqlite' !== getenv( 'WP_CLI_TEST_DBTYPE' ) ) {
wp_cli_tests_without_docker( 'The type and version of the database server could not be identified, set WP_CLI_TEST_DOCKER_DB to its Docker image.' );
}

if ( null === $php_image || ( null === $db_image && 'sqlite' !== getenv( 'WP_CLI_TEST_DBTYPE' ) ) ) {
$docker = false;
if ( $workers < 2 || null !== $rerun_paths || $selects_scenarios ) {
wp_cli_tests_run_behat_and_exit( $all_args );
}
wp_cli_tests_restart_without_docker( $user_args, $rerun_paths );
}
}

Expand Down
9 changes: 9 additions & 0 deletions utils/behat-tags.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@ function version_tags(
}

function get_db_type_and_version() {
// Docker workers run their own database server of the given image, so it decides.
$docker_db = getenv( 'WP_CLI_TEST_DOCKER_DB' );
if ( getenv( 'WP_CLI_TEST_DOCKER' ) && is_string( $docker_db ) && preg_match( '@^(?:[^/]+/)*(mysql|mariadb)(?::([0-9]+(?:\.[0-9]+)*))?@', $docker_db, $matches ) ) {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
return array(
'type' => $matches[1],
'version' => isset( $matches[2] ) ? $matches[2] : '',
);
}

// Detect which client binary is available.
$client_binary = null;
$output = array();
Expand Down
25 changes: 23 additions & 2 deletions utils/docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,34 @@ RUN install_packages() { \
libfreetype6-dev libjpeg-dev libpng-dev libzip-dev; \
} \
&& ( install_packages || { \
sed -i -e 's|deb.debian.org|archive.debian.org|g' -e '/-updates/d' /etc/apt/sources.list \
sed -i -e 's|deb.debian.org|archive.debian.org|g' -e 's|security.debian.org|archive.debian.org|g' -e '/-updates/d' /etc/apt/sources.list \
&& echo 'Acquire::Check-Valid-Until "false";' > /etc/apt/apt.conf.d/99archive \
&& install_packages; \
} ) \
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
&& ( docker-php-ext-configure gd --with-freetype --with-jpeg \
|| docker-php-ext-configure gd --with-freetype-dir=/usr --with-jpeg-dir=/usr ) \
&& docker-php-ext-install exif gd mysqli pdo_mysql zip \
&& ( php -m | grep -qi '^zend opcache$' || docker-php-ext-install opcache ) \
&& rm -rf /var/lib/apt/lists/*

# The SQLite integration plugin needs SQLite 3.37 or later, which older Debian releases do not ship.
# PHP links to the library, so a newer build in /usr/local/lib takes the place of the system one,
# with the same options as Debian's, as PHP uses the functions they enable.
ARG SQLITE_VERSION=3540100
ARG SQLITE_YEAR=2026
ARG SQLITE_SHA3_256=b6ced585c750916b1052667239a12c25a7ff34e7f06e1e6e01ecb0427657d9c4
RUN if dpkg --compare-versions "$(dpkg-query -W -f '${Version}' libsqlite3-0)" lt 3.37; then \
cd /tmp \
&& curl -fsSL -o sqlite.tar.gz "https://www.sqlite.org/${SQLITE_YEAR}/sqlite-autoconf-${SQLITE_VERSION}.tar.gz" \
&& [ "$(openssl dgst -sha3-256 -r sqlite.tar.gz | cut -d' ' -f1)" = "${SQLITE_SHA3_256}" ] \
&& tar xzf sqlite.tar.gz \
&& cd "sqlite-autoconf-${SQLITE_VERSION}" \
&& CFLAGS="-O2 -DSQLITE_ENABLE_COLUMN_METADATA=1 -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_RTREE -DSQLITE_ENABLE_UNLOCK_NOTIFY -DSQLITE_SECURE_DELETE" \
./configure --prefix=/usr/local --disable-static > /dev/null \
&& make -j"$(nproc)" > /dev/null \
&& make install > /dev/null \
&& ldconfig \
&& cd /tmp && rm -rf sqlite.tar.gz "sqlite-autoconf-${SQLITE_VERSION}"; \
fi

COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
Loading