Build/Test Tools: Add support for PHPUnit 7.x.

* Create an abstract `WP_UnitTestCase_Base` class to share between PHPUnit 7.x and older versions.
* Add a speed-trap loader to determine which `SpeedTrapListener` class needs to be loaded for the current PHPUnit version.
* Remove unnecessary `PHPUnit\Util\Test` and `PHPUnit_Util_Getopt` inheritances.
* Update Travis CI config to use PHPUnit 7.x for PHP 7.1, 7.2, and nightly PHP versions.

Props jipmoors, netweb, desrosj, ayeshrajans, soulseekah, SergeyBiryukov.
See #43218.

git-svn-id: https://develop.svn.wordpress.org/trunk@44701 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Sergey Biryukov 2019-01-28 14:10:24 +00:00
parent 639f66aade
commit 001c6498e8
13 changed files with 1556 additions and 1108 deletions

View File

@ -67,7 +67,11 @@ before_script:
# Install the specified version of PHPUnit depending on the PHP version:
if [[ "$WP_TRAVISCI" == "travis:phpunit" ]]; then
case "$TRAVIS_PHP_VERSION" in
7.3|7.2|7.1|7.0|nightly)
7.3|7.2|7.1|nightly)
echo "Using PHPUnit 7.x"
travis_retry composer global require "phpunit/phpunit:^7"
;;
7.0)
echo "Using PHPUnit 6.x"
travis_retry composer global require "phpunit/phpunit:^6"
;;

View File

@ -40,7 +40,7 @@
<const name="WP_RUN_CORE_TESTS" value="1" />
</php>
<listeners>
<listener class="SpeedTrapListener" file="tests/phpunit/includes/speed-trap-listener.php">
<listener class="SpeedTrapListener" file="tests/phpunit/includes/listener-loader.php">
<arguments>
<array>
<element key="slowThreshold">

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@
* Compatibility with PHPUnit 6+
*/
if ( class_exists( 'PHPUnit\Runner\Version' ) ) {
require_once dirname( __FILE__ ) . '/phpunit6-compat.php';
require_once dirname( __FILE__ ) . '/phpunit6/compat.php';
}
if ( defined( 'WP_TESTS_CONFIG_FILE_PATH' ) ) {
@ -118,7 +118,12 @@ require_once ABSPATH . '/wp-settings.php';
// Delete any default posts & related data
_delete_all_posts();
require dirname( __FILE__ ) . '/testcase.php';
if ( version_compare( tests_get_phpunit_version(), '7.0', '>=' ) ) {
require dirname( __FILE__ ) . '/phpunit7/testcase.php';
} else {
require dirname( __FILE__ ) . '/testcase.php';
}
require dirname( __FILE__ ) . '/testcase-rest-api.php';
require dirname( __FILE__ ) . '/testcase-rest-controller.php';
require dirname( __FILE__ ) . '/testcase-rest-post-type-controller.php';
@ -144,10 +149,11 @@ require dirname( __FILE__ ) . '/class-wp-fake-block-type.php';
* If WP_TESTS_FORCE_KNOWN_BUGS is already set in wp-tests-config.php, then
* how you call phpunit has no effect.
*/
class WP_PHPUnit_Util_Getopt extends PHPUnit_Util_Getopt {
class WP_PHPUnit_Util_Getopt {
protected $longOptions = array(
'exclude-group=',
'group=',
'verbose=',
);
function __construct( $argv ) {
array_shift( $argv );
@ -157,7 +163,7 @@ class WP_PHPUnit_Util_Getopt extends PHPUnit_Util_Getopt {
next( $argv );
try {
if ( strlen( $arg ) > 1 && $arg[0] === '-' && $arg[1] === '-' ) {
PHPUnit_Util_Getopt::parseLongOption( substr( $arg, 2 ), $this->longOptions, $options, $argv );
self::parseLongOption( substr( $arg, 2 ), $this->longOptions, $options, $argv );
}
} catch ( PHPUnit_Framework_Exception $e ) {
// Enforcing recognized arguments or correctly formed arguments is
@ -208,5 +214,68 @@ class WP_PHPUnit_Util_Getopt extends PHPUnit_Util_Getopt {
echo PHP_EOL;
}
}
/**
* Copied from https://raw.githubusercontent.com/sebastianbergmann/phpunit/6.5.7/src/Util/Getopt.php
*
* @param $arg
* @param $long_options
* @param $opts
* @param $args
*/
protected static function parseLongOption( $arg, $long_options, &$opts, &$args ) {
$count = count( $long_options );
$list = explode( '=', $arg );
$opt = $list[0];
$opt_arg = null;
if ( count( $list ) > 1 ) {
$opt_arg = $list[1];
}
$opt_len = strlen( $opt );
for ( $i = 0; $i < $count; $i++ ) {
$long_opt = $long_options[ $i ];
$opt_start = substr( $long_opt, 0, $opt_len );
if ( $opt_start != $opt ) {
continue;
}
$opt_rest = substr( $long_opt, $opt_len );
if ( $opt_rest != '' && $opt[0] != '=' && $i + 1 < $count &&
$opt == substr( $long_options[ $i + 1 ], 0, $opt_len ) ) {
throw new Exception(
"option --$opt is ambiguous"
);
}
if ( substr( $long_opt, -1 ) == '=' ) {
if ( substr( $long_opt, -2 ) != '==' ) {
if ( ! strlen( $opt_arg ) ) {
if ( false === $opt_arg = current( $args ) ) {
throw new Exception(
"option --$opt requires an argument"
);
}
next( $args );
}
}
} elseif ( $opt_arg ) {
throw new Exception(
"option --$opt doesn't allow an argument"
);
}
$full_option = '--' . preg_replace( '/={1,2}$/', '', $long_opt );
$opts[] = array( $full_option, $opt_arg );
return;
}
throw new Exception( "unrecognized option --$opt" );
}
}
new WP_PHPUnit_Util_Getopt( $_SERVER['argv'] );

View File

@ -1,5 +1,21 @@
<?php
/**
* Retrieves PHPUnit runner version.
*/
function tests_get_phpunit_version() {
if ( class_exists( 'PHPUnit_Runner_Version' ) ) {
$version = PHPUnit_Runner_Version::id();
} elseif ( class_exists( 'PHPUnit\Runner\Version' ) ) {
// Must be parsable by PHP 5.2.x.
$version = call_user_func( 'PHPUnit\Runner\Version::id' );
} else {
$version = 0;
}
return $version;
}
/**
* Resets various `$_SERVER` variables that can get altered during tests.
*/

View File

@ -0,0 +1,7 @@
<?php
if ( version_compare( tests_get_phpunit_version(), '7.0', '>=' ) ) {
require dirname( __FILE__ ) . '/phpunit7/speed-trap-listener.php';
} else {
require dirname( __FILE__ ) . '/speed-trap-listener.php';
}

View File

@ -15,10 +15,10 @@ if ( class_exists( 'PHPUnit\Runner\Version' ) && version_compare( PHPUnit\Runner
class_alias( 'PHPUnit\Util\GlobalState', 'PHPUnit_Util_GlobalState' );
class_alias( 'PHPUnit\Util\Getopt', 'PHPUnit_Util_Getopt' );
class PHPUnit_Util_Test extends PHPUnit\Util\Test {
class PHPUnit_Util_Test {
public static function getTickets( $className, $methodName ) {
$annotations = self::parseTestMethodAnnotations( $className, $methodName );
$annotations = PHPUnit\Util\Test::parseTestMethodAnnotations( $className, $methodName );
$tickets = array();

View File

@ -0,0 +1,306 @@
<?php
/**
* A PHPUnit TestListener that exposes your slowest running tests by outputting
* results directly to the console.
*/
class SpeedTrapListener implements PHPUnit_Framework_TestListener {
/**
* Internal tracking for test suites.
*
* Increments as more suites are run, then decremented as they finish. All
* suites have been run when returns to 0.
*
* @var integer
*/
protected $suites = 0;
/**
* Time in milliseconds at which a test will be considered "slow" and be
* reported by this listener.
*
* @var int
*/
protected $slowThreshold;
/**
* Number of tests to report on for slowness.
*
* @var int
*/
protected $reportLength;
/**
* Collection of slow tests.
*
* @var array
*/
protected $slow = array();
/**
* Construct a new instance.
*
* @param array $options
*/
public function __construct( array $options = array() ) {
$this->loadOptions( $options );
}
/**
* An error occurred.
*
* @param PHPUnit_Framework_Test $test
* @param Exception $e
* @param float $time
*/
public function addError( PHPUnit\Framework\Test $test, Throwable $t, float $time ): void {
}
/**
* A warning occurred.
*
* @param PHPUnit_Framework_Test $test
* @param PHPUnit_Framework_Warning $e
* @param float $time
* @since Method available since Release 5.1.0
*/
public function addWarning( PHPUnit\Framework\Test $test, PHPUnit\Framework\Warning $e, float $time ): void {
}
/**
* A failure occurred.
*
* @param PHPUnit_Framework_Test $test
* @param PHPUnit_Framework_AssertionFailedError $e
* @param float $time
*/
public function addFailure( PHPUnit\Framework\Test $test, PHPUnit\Framework\AssertionFailedError $e, float $time ): void {
}
/**
* Incomplete test.
*
* @param PHPUnit_Framework_Test $test
* @param Exception $e
* @param float $time
*/
public function addIncompleteTest( PHPUnit\Framework\Test $test, Throwable $t, float $time ): void {
}
/**
* Risky test.
*
* @param PHPUnit_Framework_Test $test
* @param Exception $e
* @param float $time
* @since Method available since Release 4.0.0
*/
public function addRiskyTest( PHPUnit\Framework\Test $test, Throwable $t, float $time ): void {
}
/**
* Skipped test.
*
* @param PHPUnit_Framework_Test $test
* @param Exception $e
* @param float $time
*/
public function addSkippedTest( PHPUnit\Framework\Test $test, Throwable $t, float $time ): void {
}
/**
* A test started.
*
* @param PHPUnit_Framework_Test $test
*/
public function startTest( PHPUnit\Framework\Test $test ): void {
}
/**
* A test ended.
*
* @param PHPUnit_Framework_Test $test
* @param float $time
*/
public function endTest( PHPUnit\Framework\Test $test, float $time ): void {
if ( ! $test instanceof PHPUnit_Framework_TestCase ) {
return;
}
$time = $this->toMilliseconds( $time );
$threshold = $this->getSlowThreshold( $test );
if ( $this->isSlow( $time, $threshold ) ) {
$this->addSlowTest( $test, $time );
}
}
/**
* A test suite started.
*
* @param PHPUnit_Framework_TestSuite $suite
*/
public function startTestSuite( PHPUnit\Framework\TestSuite $suite ): void {
$this->suites++;
}
/**
* A test suite ended.
*
* @param PHPUnit_Framework_TestSuite $suite
*/
public function endTestSuite( PHPUnit\Framework\TestSuite $suite ): void {
$this->suites--;
if ( 0 === $this->suites && $this->hasSlowTests() ) {
arsort( $this->slow ); // Sort longest running tests to the top
$this->renderHeader();
$this->renderBody();
$this->renderFooter();
}
}
/**
* Whether the given test execution time is considered slow.
*
* @param int $time Test execution time in milliseconds
* @param int $slowThreshold Test execution time at which a test should be considered slow (milliseconds)
* @return bool
*/
protected function isSlow( $time, $slowThreshold ) {
return $time >= $slowThreshold;
}
/**
* Stores a test as slow.
*
* @param PHPUnit_Framework_TestCase $test
* @param int $time Test execution time in milliseconds
*/
protected function addSlowTest( PHPUnit_Framework_TestCase $test, $time ) {
$label = $this->makeLabel( $test );
$this->slow[ $label ] = $time;
}
/**
* Whether at least one test has been considered slow.
*
* @return bool
*/
protected function hasSlowTests() {
return ! empty( $this->slow );
}
/**
* Convert PHPUnit's reported test time (microseconds) to milliseconds.
*
* @param float $time
* @return int
*/
protected function toMilliseconds( $time ) {
return (int) round( $time * 1000 );
}
/**
* Label for describing a test.
*
* @param PHPUnit_Framework_TestCase $test
* @return string
*/
protected function makeLabel( PHPUnit_Framework_TestCase $test ) {
return sprintf( '%s:%s', get_class( $test ), $test->getName() );
}
/**
* Calculate number of slow tests to report about.
*
* @return int
*/
protected function getReportLength() {
return min( count( $this->slow ), $this->reportLength );
}
/**
* Find how many slow tests occurred that won't be shown due to list length.
*
* @return int Number of hidden slow tests
*/
protected function getHiddenCount() {
$total = count( $this->slow );
$showing = $this->getReportLength( $this->slow );
$hidden = 0;
if ( $total > $showing ) {
$hidden = $total - $showing;
}
return $hidden;
}
/**
* Renders slow test report header.
*/
protected function renderHeader() {
echo sprintf( "\n\nYou should really fix these slow tests (>%sms)...\n", $this->slowThreshold );
}
/**
* Renders slow test report body.
*/
protected function renderBody() {
$slowTests = $this->slow;
$length = $this->getReportLength( $slowTests );
for ( $i = 1; $i <= $length; ++$i ) {
$label = key( $slowTests );
$time = array_shift( $slowTests );
echo sprintf( " %s. %sms to run %s\n", $i, $time, $label );
}
}
/**
* Renders slow test report footer.
*/
protected function renderFooter() {
if ( $hidden = $this->getHiddenCount( $this->slow ) ) {
echo sprintf( '...and there %s %s more above your threshold hidden from view', $hidden == 1 ? 'is' : 'are', $hidden );
}
}
/**
* Populate options into class internals.
*
* @param array $options
*/
protected function loadOptions( array $options ) {
$this->slowThreshold = isset( $options['slowThreshold'] ) ? $options['slowThreshold'] : 500;
$this->reportLength = isset( $options['reportLength'] ) ? $options['reportLength'] : 10;
}
/**
* Get slow test threshold for given test. A TestCase can override the
* suite-wide slow threshold by using the annotation @slowThreshold with
* the threshold value in milliseconds.
*
* The following test will only be considered slow when its execution time
* reaches 5000ms (5 seconds):
*
* <code>
*
* @slowThreshold 5000
* public function testLongRunningProcess() {}
* </code>
*
* @param PHPUnit_Framework_TestCase $test
* @return int
*/
protected function getSlowThreshold( PHPUnit_Framework_TestCase $test ) {
$ann = $test->getAnnotations();
return isset( $ann['method']['slowThreshold'][0] ) ? $ann['method']['slowThreshold'][0] : $this->slowThreshold;
}
}

View File

@ -0,0 +1,31 @@
<?php
require_once dirname( dirname( __FILE__ ) ) . '/abstract-testcase.php';
/**
* Defines a basic fixture to run multiple tests.
*
* Resets the state of the WordPress installation before and after every test.
*
* Includes utility functions and assertions useful for testing WordPress.
*
* All WordPress unit tests should inherit from this class.
*/
class WP_UnitTestCase extends WP_UnitTestCase_Base {
/**
* Asserts that a condition is not false.
*
* This method has been backported from a more recent PHPUnit version, as tests running on PHP 5.2 use
* PHPUnit 3.6.x.
*
* @since 4.7.4
*
* @param bool $condition Condition to check.
* @param string $message Optional. Message to display when the assertion fails.
*
* @throws PHPUnit_Framework_AssertionFailedError
*/
public static function assertNotFalse( $condition, string $message = '' ): void {
self::assertThat( $condition, self::logicalNot( self::isFalse() ), $message );
}
}

View File

@ -236,7 +236,7 @@ class WP_Canonical_UnitTestCase extends WP_UnitTestCase {
public function assertCanonical( $test_url, $expected, $ticket = 0, $expected_doing_it_wrong = array() ) {
$this->expected_doing_it_wrong = array_merge( $this->expected_doing_it_wrong, (array) $expected_doing_it_wrong );
$ticket_ref = ( $ticket > 0 ) ? 'Ticket #' . $ticket : null;
$ticket_ref = ( $ticket > 0 ) ? 'Ticket #' . $ticket : '';
if ( is_string( $expected ) ) {
$expected = array( 'url' => $expected );

File diff suppressed because it is too large Load Diff

View File

@ -45,7 +45,7 @@
<const name="WP_RUN_CORE_TESTS" value="1" />
</php>
<listeners>
<listener class="SpeedTrapListener" file="tests/phpunit/includes/speed-trap-listener.php">
<listener class="SpeedTrapListener" file="tests/phpunit/includes/listener-loader.php">
<arguments>
<array>
<element key="slowThreshold">

View File

@ -719,7 +719,7 @@ class Tests_Post_Query extends WP_UnitTestCase {
$q->posts = $posts;
$methd = new \ReflectionMethod( 'WP_Query', 'set_found_posts' );
$methd = new ReflectionMethod( 'WP_Query', 'set_found_posts' );
$methd->setAccessible( true );
$methd->invoke( $q, array( 'no_found_rows' => false ), array() );