Revert "phpunit: Default to vendor/bin/phpunit, remove suites.xml"

This caused unexpected problems with no obvious fixes. Needs more work.

This reverts commit 7238dff532.

Bug: T310255
Bug: T90875
Change-Id: I3758cbb6d0029b20ec1b0f67dbf2f422031c50ae
This commit is contained in:
Lucas Werkmeister (WMDE) 2022-06-09 14:15:54 +00:00
parent 7238dff532
commit acae1c5107
11 changed files with 237 additions and 222 deletions

View file

@ -77,15 +77,6 @@ For notes on 1.38.x and older releases, see HISTORY.
* …
=== New developer features in 1.39 ===
* tests/phpunit/phpunit.php has been deprecated (T90875) along with suites.xml
(T227900). The default entrypoint for running tests is
`composer phpunit` which now points to `vendor/bin/phpunit`.
`phpunit.xml.dist` replaces `suites.xml`, and can be overridden locally
with `phpunit.xml`. Please update your scripts to use
`composer phpunit --` or `vendor/bin/phpunit`. Note that if you are running
unit tests (not integration tests), you should use
`composer phpunit:unit` which loads a custom bootstrap file that does
not require MediaWiki to be installed.
* …
=== External library changes in 1.39 ===

View file

@ -145,7 +145,7 @@
"pre-update-cmd": "ComposerHookHandler::onPreUpdate",
"post-install-cmd": "ComposerVendorHtaccessCreator::onEvent",
"post-update-cmd": "ComposerVendorHtaccessCreator::onEvent",
"releasenotes": "@phpunit --group ReleaseNotes",
"releasenotes": "@phpunit:entrypoint --group ReleaseNotes",
"test": [
"@lint .",
"@phpcs ."
@ -154,12 +154,12 @@
"@lint",
"@phpcs"
],
"phpunit": "phpunit",
"phpunit:unit": "@phpunit --colors=always --bootstrap=tests/phpunit/bootstrap.php --testsuite=core:unit,extensions:unit,skins:unit",
"phpunit": "php tests/phpunit/phpunit.php",
"phpunit:unit": "phpunit --colors=always --testsuite=core:unit,extensions:unit,skins:unit",
"phpunit:integration": "@phpunit --colors=always --testsuite=core:integration,extensions:integration,skins:integration",
"phpunit:coverage": "@phpunit --testsuite=core:unit --exclude-group Dump,Broken",
"phpunit:coverage-edit": "ComposerPhpunitXmlCoverageEdit::onEvent",
"phpunit:entrypoint": "@phpunit"
"phpunit:entrypoint": "@phpunit -c tests/phpunit/suite.xml"
},
"config": {
"optimize-autoloader": true,

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="tests/phpunit/bootstrap.integration.php"
<phpunit bootstrap="tests/phpunit/bootstrap.php"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.8/phpunit.xsd"
@ -14,12 +14,10 @@
stopOnFailure="false"
beStrictAboutTestsThatDoNotTestAnything="true"
beStrictAboutOutputDuringTests="true"
verbose="false"
printerClass="MediaWikiPHPUnitResultPrinter"
stderr="true">
verbose="false">
<php>
<ini name="memory_limit" value="-1" />
<ini name="max_execution_time" value="0" />
<ini name="memory_limit" value="512M" />
</php>
<testsuites>
<testsuite name="core:unit">

View file

@ -144,7 +144,7 @@ class TestSetup {
}
/**
* @internal Should only be used in tests/phpunit/bootstrap.php.
* @internal Should only be used in bootstrap.php and boostrap.maintenance.php
*
* PHPUnit includes the bootstrap file inside a method body, while most MediaWiki startup files
* assume to be included in the global scope.

View file

@ -1,89 +0,0 @@
<?php
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
* @ingroup Testing
*/
use MediaWiki\MediaWikiServices;
/**
* Used by bootstrap.integration.php to do some final set up before integration tests are run.
*
* Most of this logic used to live in the custom tests/phpunit/phpunit.php wrapper script.
*/
class IntegrationBootstrapWrapper {
public function setup() {
// Send PHP warnings and errors to stderr instead of stdout.
// This aids in diagnosing problems, while keeping messages
// out of redirected output.
if ( ini_get( 'display_errors' ) ) {
ini_set( 'display_errors', 'stderr' );
}
$this->prepareEnvironment();
require_once __DIR__ . '/../common/TestSetup.php';
TestSetup::snapshotGlobals();
}
public function prepareEnvironment() {
global $wgCommandLineMode;
$wgCommandLineMode = true;
# Turn off output buffering if it's on
while ( ob_get_level() > 0 ) {
ob_end_flush();
}
}
public function finalSetup() {
global $wgDBadminuser, $wgDBadminpassword;
global $wgDBuser, $wgDBpassword, $wgDBservers, $wgLBFactoryConf;
// Prepare environment again, things might have changed in the settings files
$this->prepareEnvironment();
if ( isset( $wgDBadminuser ) ) {
$wgDBuser = $wgDBadminuser;
$wgDBpassword = $wgDBadminpassword;
if ( $wgDBservers ) {
/**
* @var array $wgDBservers
*/
foreach ( $wgDBservers as $i => $server ) {
$wgDBservers[$i]['user'] = $wgDBuser;
$wgDBservers[$i]['password'] = $wgDBpassword;
}
}
if ( isset( $wgLBFactoryConf['serverTemplate'] ) ) {
$wgLBFactoryConf['serverTemplate']['user'] = $wgDBuser;
$wgLBFactoryConf['serverTemplate']['password'] = $wgDBpassword;
}
$service = MediaWikiServices::getInstance()->peekService( 'DBLoadBalancerFactory' );
if ( $service ) {
$service->destroy();
}
}
require_once __DIR__ . '/../common/TestsAutoLoader.php';
TestSetup::applyInitialConfig();
ExtensionRegistry::getInstance()->setLoadTestClassesAndNamespaces( true );
}
public function execute() {
// Start an output buffer to avoid headers being sent by constructors,
// data providers, etc. (T206476)
ob_start();
fwrite( STDERR, 'Using PHP ' . PHP_VERSION . "\n" );
}
}

View file

@ -170,7 +170,7 @@ abstract class MediaWikiIntegrationTestCase extends PHPUnit\Framework\TestCase {
$this->backupStaticAttributes = false;
}
public static function initializeForStandardPhpunitEntrypointIfNeeded() {
private static function initializeForStandardPhpunitEntrypointIfNeeded() {
if ( defined( 'MW_PHPUNIT_UNIT' ) ) {
$IP = realpath( __DIR__ . '/../..' );
TestSetup::requireOnceInGlobalScope( "$IP/includes/Defines.php" );

View file

@ -1,88 +0,0 @@
<?php
/**
* Bootstrap file used by default in phpunit.xml.dist.
*
* Takes care of preparation needed for integration tests to run.
*
* If you want to run unit tests with more strict isolation (ensuring MediaWiki does not bootstrap),
* then pass the bootstrap.php file, not this one, to vendor/bin/phpunit --bootstrap.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
* @ingroup Testing
*/
require_once __DIR__ . "/bootstrap.php";
require_once __DIR__ . "/IntegrationBootstrapWrapper.php";
if ( PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg' ) {
exit( 'This script must be run from the command line' );
}
if ( strval( getenv( 'MW_INSTALL_PATH' ) ) === '' ) {
putenv( 'MW_INSTALL_PATH=' . realpath( __DIR__ . '/../..' ) );
}
if ( getenv( 'PHPUNIT_WIKI' ) ) {
$wikiName = getenv( 'PHPUNIT_WIKI' );
$bits = explode( '-', $wikiName, 2 );
define( 'MW_DB', $bits[0] );
define( 'MW_PREFIX', $bits[1] ?? '' );
define( 'MW_WIKI_NAME', $wikiName );
}
$IP = getenv( 'MW_INSTALL_PATH' );
global $wgIntegrationBootstrapWrapper;
$wgIntegrationBootstrapWrapper = new IntegrationBootstrapWrapper();
$wgIntegrationBootstrapWrapper->setup();
require_once "$IP/includes/BootstrapHelperFunctions.php";
// ensure MW_INSTALL_PATH is defined
$IP = wfDetectInstallPath();
wfDetectLocalSettingsFile( $IP );
/**
* Will be called when MW_SETUP_CALLBACK is evaluated during bootstrapping.
*
* @return void
*/
function wfPHPUnitSetup(): void {
global $wgIntegrationBootstrapWrapper;
$wgIntegrationBootstrapWrapper->finalSetup();
}
define( 'MW_SETUP_CALLBACK', 'wfPHPUnitSetup' );
MediaWikiIntegrationTestCase::initializeForStandardPhpunitEntrypointIfNeeded();
// Deregister handler from MWExceptionHandler::installHandle so that PHPUnit's own handler
// stays intact. Needs to happen after including Setup.php, which calls MWExceptionHandler::installHandle().
restore_error_handler();
$wgIntegrationBootstrapWrapper->execute();
// The TestRunner class will run each test suite and may call
// exit() with an exit status code. As such, we cannot run code "after the last test"
// by adding statements to PHPUnitMaintClass::execute.
// Instead, we work around it by registering a shutdown callback from the bootstrap
// file, which runs before PHPUnit starts.
// @todo Once we use PHPUnit 8 or higher, use the 'AfterLastTestHook' feature.
// https://phpunit.readthedocs.io/en/8.0/extending-phpunit.html#available-hook-interfaces
register_shutdown_function( static function () {
// This will:
// - clear the temporary job queue.
// - allow extensions to delete any temporary tables they created.
// - restore ability to connect to the real database.
MediaWikiIntegrationTestCase::teardownTestDB();
} );

View file

@ -0,0 +1,32 @@
<?php
/**
* Bootstrapping for MediaWiki PHPUnit tests when called via the maintenance class tests runner.
* This file is included by phpunit and is NOT in the global scope.
*
* @file
*/
if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
echo <<<EOF
You are running these tests directly from phpunit. You may not have all globals correctly set.
Running phpunit.php instead is recommended.
EOF;
require_once __DIR__ . "/phpunit.php";
}
// The TestRunner class will run each test suite and may call
// exit() with an exit status code. As such, we cannot run code "after the last test"
// by adding statements to PHPUnitMaintClass::execute.
// Instead, we work around it by registering a shutdown callback from the bootstrap
// file, which runs before PHPUnit starts.
// @todo Once we use PHPUnit 8 or higher, use the 'AfterLastTestHook' feature.
// https://phpunit.readthedocs.io/en/8.0/extending-phpunit.html#available-hook-interfaces
register_shutdown_function( static function () {
// This will:
// - clear the temporary job queue.
// - allow extensions to delete any temporary tables they created.
// - restore ability to connect to the real database.
MediaWikiIntegrationTestCase::teardownTestDB();
} );
MediaWikiCliOptions::initialize();

View file

@ -6,24 +6,89 @@
* @file
*/
use MediaWiki\MediaWikiServices;
use PHPUnit\TextUI\Command;
require_once __DIR__ . '/bootstrap.integration.php';
require_once __DIR__ . '/IntegrationBootstrapWrapper.php';
class PHPUnitMaintClass {
public function setup() {
// Set a flag which can be used to detect when other scripts have been entered
// through this entry point or not.
define( 'MW_PHPUNIT_TEST', true );
/**
* Temporary class overriding the IntegrationBootstrapWrapper.
*/
class PHPUnitMaintClass extends IntegrationBootstrapWrapper {
// Send PHP warnings and errors to stderr instead of stdout.
// This aids in diagnosing problems, while keeping messages
// out of redirected output.
if ( ini_get( 'display_errors' ) ) {
ini_set( 'display_errors', 'stderr' );
}
$this->prepareEnvironment();
require_once __DIR__ . '/../common/TestSetup.php';
TestSetup::snapshotGlobals();
}
public function prepareEnvironment() {
global $wgCommandLineMode;
# Disable the memory limit as it's not needed for tests.
ini_set( 'memory_limit', -1 );
# Set max execution time to 0 (no limit). PHP.net says that
# "When running PHP from the command line the default setting is 0."
# But sometimes this doesn't seem to be the case.
ini_set( 'max_execution_time', 0 );
$wgCommandLineMode = true;
# Turn off output buffering if it's on
while ( ob_get_level() > 0 ) {
ob_end_flush();
}
}
public function finalSetup() {
global $wgDBadminuser, $wgDBadminpassword;
global $wgDBuser, $wgDBpassword, $wgDBservers, $wgLBFactoryConf;
# Prepare environment again, things might have changed in the settings files
$this->prepareEnvironment();
if ( isset( $wgDBadminuser ) ) {
$wgDBuser = $wgDBadminuser;
$wgDBpassword = $wgDBadminpassword;
if ( $wgDBservers ) {
/**
* @var array $wgDBservers
*/
foreach ( $wgDBservers as $i => $server ) {
$wgDBservers[$i]['user'] = $wgDBuser;
$wgDBservers[$i]['password'] = $wgDBpassword;
}
}
if ( isset( $wgLBFactoryConf['serverTemplate'] ) ) {
$wgLBFactoryConf['serverTemplate']['user'] = $wgDBuser;
$wgLBFactoryConf['serverTemplate']['password'] = $wgDBpassword;
}
$service = MediaWikiServices::getInstance()->peekService( 'DBLoadBalancerFactory' );
if ( $service ) {
$service->destroy();
}
}
require_once __DIR__ . '/../common/TestsAutoLoader.php';
TestSetup::applyInitialConfig();
ExtensionRegistry::getInstance()->setLoadTestClassesAndNamespaces( true );
}
public function execute() {
// Start an output buffer to avoid headers being sent by constructors,
// data providers, etc. (T206476)
ob_start();
wfDeprecatedMsg( 'tests/phpunit/phpunit.php is deprecated and will be removed. ' .
'Please use `composer phpunit` or `vendor/bin/phpunit`',
'1.39'
);
fwrite( STDERR, 'Using PHP ' . PHP_VERSION . "\n" );
$command = new Command();
$args = $_SERVER['argv'];
@ -31,16 +96,22 @@ class PHPUnitMaintClass extends IntegrationBootstrapWrapper {
if ( !isset( $knownOpts['c'] ) && !isset( $knownOpts['configuration'] ) ) {
// XXX HAX: Use our default file. This is a temporary hack, to be removed when this file goes away
// or when T227900 is resolved.
$args[] = '--configuration=' . dirname( __DIR__, 2 ) . '/phpunit.xml.dist';
$args[] = '--configuration=' . __DIR__ . '/suite.xml';
}
$command->run( $args, true );
}
}
if ( defined( 'MEDIAWIKI' ) ) {
exit( 'Wrong entry point?' );
}
if ( PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg' ) {
exit( 'This script must be run from the command line' );
}
define( 'MW_ENTRY_POINT', 'cli' );
if ( strval( getenv( 'MW_INSTALL_PATH' ) ) === '' ) {
putenv( 'MW_INSTALL_PATH=' . realpath( __DIR__ . '/../..' ) );
}
@ -54,22 +125,29 @@ if ( getenv( 'PHPUNIT_WIKI' ) ) {
}
// Define the MediaWiki entrypoint
define( 'MEDIAWIKI', true );
$IP = getenv( 'MW_INSTALL_PATH' );
global $wgIntegrationBootstrapWrapper;
$wgIntegrationBootstrapWrapper = new PHPUnitMaintClass();
$wgIntegrationBootstrapWrapper->setup();
$wrapper = new PHPUnitMaintClass();
$wrapper->setup();
require_once "$IP/includes/BootstrapHelperFunctions.php";
// ensure MW_INSTALL_PATH is defined
$IP = wfDetectInstallPath();
$IP = wfDetectInstallPath(); // ensure MW_INSTALL_PATH is defined
wfDetectLocalSettingsFile( $IP );
function wfPHPUnitSetup() {
// phpcs:ignore MediaWiki.NamingConventions.ValidGlobalName.allowedPrefix
global $wrapper;
$wrapper->finalSetup();
}
define( 'MW_SETUP_CALLBACK', 'wfPHPUnitSetup' );
require_once "$IP/includes/Setup.php";
// Deregister handler from MWExceptionHandler::installHandle so that PHPUnit's own handler
// stays in tact. Needs to happen after including Setup.php, which calls MWExceptionHandler::installHandle().
restore_error_handler();
$wgIntegrationBootstrapWrapper->execute();
$wrapper->execute();

View file

@ -1,22 +1,17 @@
<?php
/**
* The tests here verify that phpunit.xml.dist covers all of the tests under /tests/phpunit
* The tests here verify that phpunit/suite.xml covers all of the tests under /tests/phpunit
* @group medium
*/
class SuiteDirectoryTest extends PHPUnit\Framework\TestCase {
/**
* @coversNothing
*/
public function testSuiteXmlDirectories() {
// realpath() also normalizes directory separator on windows for prefix compares
$rootPath = realpath( __DIR__ . '/' );
$rootPath = realpath( __DIR__ . '/..' );
$dom = new DOMDocument();
$ip = dirname( __DIR__, 3 );
$dom->load( "$ip/phpunit.xml.dist" );
$dom->load( "$rootPath/suite.xml" );
/** @var DOMElement $suites */
$suites = $dom->documentElement->getElementsByTagName( 'testsuites' )[0];

98
tests/phpunit/suite.xml Normal file
View file

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="./bootstrap.maintenance.php"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.8/phpunit.xsd"
colors="true"
backupGlobals="false"
convertDeprecationsToExceptions="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
forceCoversAnnotation="true"
failOnWarning="true"
stopOnFailure="false"
failOnRisky="true"
beStrictAboutTestsThatDoNotTestAnything="true"
beStrictAboutOutputDuringTests="true"
verbose="false"
printerClass="MediaWikiPHPUnitResultPrinter"
stderr="true">
<!-- Output only to stderr to avoid "Headers already sent" problems -->
<testsuites>
<testsuite name="includes">
<directory>includes</directory>
</testsuite>
<testsuite name="languages">
<directory>languages</directory>
</testsuite>
<testsuite name="parsertests">
<file>suites/CoreParserTestSuite.php</file>
<file>suites/ExtensionsParserTestSuite.php</file>
</testsuite>
<testsuite name="skins">
<directory>skins</directory>
<directory>structure</directory>
<file>suites/ExtensionsTestSuite.php</file>
<file>suites/LessTestSuite.php</file>
</testsuite>
<!-- As there is a class Maintenance, we cannot use the name "maintenance" directly -->
<testsuite name="maintenance_suite">
<directory>maintenance</directory>
</testsuite>
<testsuite name="structure">
<directory>structure</directory>
</testsuite>
<testsuite name="tests">
<directory>tests</directory>
</testsuite>
<testsuite name="extensions">
<directory>structure</directory>
<file>suites/ExtensionsTestSuite.php</file>
<file>suites/ExtensionsParserTestSuite.php</file>
<file>suites/LessTestSuite.php</file>
</testsuite>
<testsuite name="documentation">
<directory>documentation</directory>
</testsuite>
<testsuite name="unit">
<directory>unit</directory>
</testsuite>
<testsuite name="integration">
<directory>integration</directory>
</testsuite>
</testsuites>
<groups>
<exclude>
<group>Broken</group>
</exclude>
</groups>
<filter>
<whitelist addUncoveredFilesFromWhitelist="true">
<directory suffix=".php">../../includes</directory>
<directory suffix=".php">../../languages</directory>
<directory suffix=".php">../../maintenance</directory>
<exclude>
<directory suffix=".php">../../languages/messages</directory>
<directory suffix=".php">../../maintenance/benchmarks</directory>
</exclude>
</whitelist>
</filter>
<listeners>
<listener class="JohnKary\PHPUnit\Listener\SpeedTrapListener">
<arguments>
<array>
<element key="slowThreshold">
<integer>50</integer>
</element>
<element key="reportLength">
<integer>50</integer>
</element>
</array>
</arguments>
</listener>
</listeners>
<extensions>
<extension class="MediaWikiLoggerPHPUnitExtension" />
</extensions>
</phpunit>