Skip to content

Commit

Permalink
Add phpunit-select-config package (#41860)
Browse files Browse the repository at this point in the history
This is a simple stub that selects a phpunit config file based on the
PHPUnit version installed.

Also, update `packages/codesniffer` to ignore another WP-only rule.
  • Loading branch information
anomiex authored Feb 18, 2025
1 parent 3d1051a commit bb8b31c
Show file tree
Hide file tree
Showing 22 changed files with 608 additions and 0 deletions.
3 changes: 3 additions & 0 deletions projects/packages/codesniffer/Jetpack-NoWP/ruleset.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
<rule ref="WordPress.Security.NonceVerification">
<severity>0</severity>
</rule>
<rule ref="WordPress.Security.ValidatedSanitizedInput">
<exclude name="WordPress.Security.ValidatedSanitizedInput.MissingUnslash" />
</rule>
<rule ref="WordPress.WP.AlternativeFunctions">
<severity>0</severity>
</rule>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: added

Ignore `WordPress.Security.ValidatedSanitizedInput.MissingUnslash` in `Jetpack-NoWP` ruleset.
16 changes: 16 additions & 0 deletions projects/packages/phpunit-select-config/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Files not needed to be distributed in the package.
.gitattributes export-ignore
.github/ export-ignore

# Files to include in the mirror repo, but excluded via gitignore
# Remember to end all directories with `/**` to properly tag every file.
# /src/js/example.min.js production-include

# Files to exclude from the mirror repo, but included in the monorepo.
# Remember to end all directories with `/**` to properly tag every file.
.gitignore production-exclude
changelog/** production-exclude
.phpcs.dir.xml production-exclude
.phpcs.dir.phpcompatibility.xml production-exclude
phpunit.*.xml.dist production-exclude
tests/** production-exclude
2 changes: 2 additions & 0 deletions projects/packages/phpunit-select-config/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
vendor/
node_modules/
13 changes: 13 additions & 0 deletions projects/packages/phpunit-select-config/.phan/config.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php
/**
* This configuration will be read and overlaid on top of the
* default configuration. Command-line arguments will be applied
* after this file is read.
*
* @package automattic/phpunit-select-config
*/

// Require base config.
require __DIR__ . '/../../../../.phan/config.base.php';

return make_phan_config( dirname( __DIR__ ) );
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0"?>
<ruleset>
<rule ref="Jetpack-Compat-NoWP" />
</ruleset>
4 changes: 4 additions & 0 deletions projects/packages/phpunit-select-config/.phpcs.dir.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0"?>
<ruleset>
<rule ref="Jetpack-NoWP" />
</ruleset>
9 changes: 9 additions & 0 deletions projects/packages/phpunit-select-config/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## 1.0.0-alpha - unreleased
- Initial release.
22 changes: 22 additions & 0 deletions projects/packages/phpunit-select-config/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# phpunit-select-config

This is a shim to run PHPUnit while selecting a configuration file based on the PHPUnit major version number.

This is intended for use in projects that have to test with a wide variety of PHP versions, which typically
also means a wide variety of PHPUnit versions which may have incompatible configuration file formats.

## Installation

Require using `composer require --dev automattic/phpunit-select-config`.

This will install the `phpunit-select-config` tool into `vendor/bin/`, which you might add to your PATH, or
you might run it via `composer exec -- phpunit-select-config`.

## Usage

Create PHPUnit configuration files for each relevant PHPUnit version following a common naming pattern.
For example, you might name the config for PHPUnit 8 `phpunit.8.xml.dist`, the one for PHPUnit 9 `phpunit.9.xml.dist`,
the one for PHPUnit 10 `phpunit.10.xml.dist`, and so on.

Then, instead of running `phpunit` directly, run `phpunit-select-config phpunit.#.xml.dist`. Any additional arguments to PHPUnit may follow.
The `#` character in the filename-pattern will be replaced with the PHPUnit major version and passed as the `--configuration` argument to PHPUnit.
10 changes: 10 additions & 0 deletions projects/packages/phpunit-select-config/bin/phpunit-select-config
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env php
<?php
/**
* A shim to select a config file based on the PHPUnit version installed.
*
* @package automattic/phpunit-select-config
*/

// This is a stub so the executable doesn't have the '.php' extension
require __DIR__ . '/phpunit-select-config.php';
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php
/**
* A shim to select a config file based on the PHPUnit version installed.
*
* @package automattic/phpunit-select-config
*/

// Make sure this script is being run over the PHP CLI.
if ( 'cli' !== php_sapi_name() && 'phpdbg' !== php_sapi_name() ) {
throw new RuntimeException( 'This file must be run from the command line.' );
}

if ( ! isset( $argv ) || ! is_array( $argv ) ) {
if ( ! isset( $_SERVER['argv'] ) || ! is_array( $_SERVER['argv'] ) ) {
throw new RuntimeException( 'Neither $argv nor $_SERVER[\'argv\'] is an array.' );
}
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$argv = $_SERVER['argv'];
}

if ( count( $argv ) < 2 ) {
fprintf( STDERR, "USAGE: %s pattern [args...]\n", $argv[0] );
fprintf( STDERR, "\n" );
fprintf( STDERR, "The `pattern` argument should contain a `#` character, which will be replaced\n" );
fprintf( STDERR, "by the major version of PHPUnit in use.\n" );
exit( 1 );
}

require_once $GLOBALS['_composer_autoload_path'];

if ( ! class_exists( '\\PHPUnit\\Runner\\Version' ) ) {
// @phan-suppress-next-line PhanUndeclaredClassMethod -- May as well support super-old phpunit since it's simple to.
list( $version ) = explode( '.', PHPUnit_Runner_Version::id() ); // @codeCoverageIgnore
} else {
list( $version ) = explode( '.', \PHPUnit\Runner\Version::id() );
}
array_shift( $argv );
$pattern = array_shift( $argv );
array_unshift( $argv, $GLOBALS['_composer_bin_dir'] . '/phpunit', '--configuration', str_replace( '#', $version, $pattern ) );

// Handle pcov or xdebug being configured on the command line.
foreach ( array( 'pcov', 'xdebug' ) as $ext ) {
if ( extension_loaded( $ext ) ) {
foreach ( ini_get_all( $ext, false ) as $k => $v ) {
array_unshift( $argv, "-d$k=$v" );
}
}
}

// Assume phpdbg is being run with -qrr.
if ( 'phpdbg' === php_sapi_name() ) {
array_unshift( $argv, '-qrr' );
}

pcntl_exec( PHP_BINARY, $argv );
fprintf( STDERR, "Failed to execute %s/phpunit\n", $GLOBALS['_composer_bin_dir'] );
exit( 1 );
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: added
Comment: Initial version.

56 changes: 56 additions & 0 deletions projects/packages/phpunit-select-config/composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"name": "automattic/phpunit-select-config",
"description": "Run PHPUnit, choosing a configuration file by version.",
"type": "library",
"license": "GPL-2.0-or-later",
"keywords": [
"phpunit",
"config",
"dev"
],
"require": {
"php": ">=5.4",
"composer-runtime-api": "^2.2.2",
"phpunit/phpunit": "*"
},
"require-dev": {
"automattic/jetpack-changelogger": "@dev",
"yoast/phpunit-polyfills": "^1.1.1",
"antecedent/patchwork": "^2.2"
},
"bin": [
"bin/phpunit-select-config"
],
"scripts": {
"phpunit": [
"./tests/phpunit-select-config phpunit.#.xml.dist --colors=always"
],
"test-coverage": [
"php -dpcov.directory=. ./tests/phpunit-select-config phpunit.#.xml.dist --coverage-php \"$COVERAGE_DIR/php.cov\""
],
"test-php": [
"@composer phpunit"
]
},
"repositories": [
{
"type": "path",
"url": "../../packages/*",
"options": {
"monorepo": true
}
}
],
"minimum-stability": "dev",
"prefer-stable": true,
"extra": {
"autotagger": true,
"branch-alias": {
"dev-trunk": "1.0.x-dev"
},
"changelogger": {
"link-template": "https://github.com/Automattic/phpunit-select-config/compare/v${old}...v${new}"
},
"mirror-repo": "Automattic/phpunit-select-config"
}
}
1 change: 1 addition & 0 deletions projects/packages/phpunit-select-config/phpunit.8.xml.dist
12 changes: 12 additions & 0 deletions projects/packages/phpunit-select-config/phpunit.9.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<phpunit bootstrap="tests/bootstrap.php" backupGlobals="true" colors="true" convertDeprecationsToExceptions="true">
<testsuites>
<testsuite name="main">
<directory suffix=".php">tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="false">
<directory suffix=".php">bin</directory>
</whitelist>
</filter>
</phpunit>
4 changes: 4 additions & 0 deletions projects/packages/phpunit-select-config/tests/.phpcs.dir.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0"?>
<ruleset>
<rule ref="Jetpack-Tests" />
</ruleset>
72 changes: 72 additions & 0 deletions projects/packages/phpunit-select-config/tests/ExitException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php
/**
* Exception class.
*
* @package automattic/phpunit-select-config
*/

/**
* Exception to represent the calling of `exit()` or `die()`.
*/
class ExitException extends RuntimeException {

/**
* Whether exit or die was called.
*
* @var string
*/
protected $func;

/**
* Argument passed to exit/die.
*
* @var string|int|null
*/
protected $arg;

/**
* Constructor.
*
* @param string $func 'exit' or 'die'.
* @param string|int|null $arg Argument passed to `exit` or `die`.
*/
public function __construct( $func, $arg ) {
$this->func = $func;
$this->arg = $arg;

$code = 0;
if ( is_int( $arg ) ) {
$code = $arg;
$message = ucfirst( $func ) . " called with code $arg";
} elseif ( $arg === '' ) {
$message = ucfirst( $func ) . ' called with an empty string';
} elseif ( is_string( $arg ) ) {
$message = ucfirst( $func ) . " called: $arg";
} elseif ( $arg === null ) {
$message = ucfirst( $func ) . ' called with no argument';
} else {
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export
$message = ucfirst( $func ) . ' called with argument ' . var_export( $arg, true );
}

parent::__construct( $message, $code );
}

/**
* Whether `exit` or `die` was called.
*
* @return string 'exit' or 'die'.
*/
public function getFunction() {
return $this->func;
}

/**
* Argument passed to `exit` or `die`.
*
* @return int|string|null
*/
public function getArg() {
return $this->arg;
}
}
Loading

0 comments on commit bb8b31c

Please sign in to comment.