X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=tests%2Fphpunit%2FMediaWikiTestCase.php;h=114ad7e2ae2fff2da21831ac572c579085e81980;hb=01cdb1762c7207bd261ad03726a88cb9afc97bfb;hp=78636b111cc522309124367112eed444aa6d35ee;hpb=2901b84c495edb9ebf70798e12f5695fd19c1b42;p=lhc%2Fweb%2Fwiklou.git diff --git a/tests/phpunit/MediaWikiTestCase.php b/tests/phpunit/MediaWikiTestCase.php index 78636b111c..114ad7e2ae 100644 --- a/tests/phpunit/MediaWikiTestCase.php +++ b/tests/phpunit/MediaWikiTestCase.php @@ -8,7 +8,6 @@ use Psr\Log\LoggerInterface; use Wikimedia\Rdbms\IDatabase; use Wikimedia\Rdbms\IMaintainableDatabase; use Wikimedia\Rdbms\Database; -use Wikimedia\Rdbms\LBFactory; use Wikimedia\TestingAccessWrapper; /** @@ -28,6 +27,7 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase { /** * The local service locator, created during setUp(). + * @var MediaWikiServices */ private $localServices; @@ -107,9 +107,10 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase { private $loggers = []; /** - * @var LoggerInterface + * The CLI arguments passed through from phpunit.php + * @var array */ - private $testLogger; + private $cliArgs = []; /** * Table name prefixes. Oracle likes it shorter. @@ -133,11 +134,6 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase { $this->backupGlobals = false; $this->backupStaticAttributes = false; - $this->testLogger = self::getTestLogger(); - } - - private static function getTestLogger() { - return LoggerFactory::getInstance( 'tests-phpunit' ); } public function __destruct() { @@ -295,38 +291,6 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase { return $testConfig; } - /** - * @param ConfigFactory $oldConfigFactory - * @param LBFactory $oldLoadBalancerFactory - * @param MediaWikiServices $newServices - * - * @throws MWException - */ - private static function installTestServices( - ConfigFactory $oldConfigFactory, - LBFactory $oldLoadBalancerFactory, - MediaWikiServices $newServices - ) { - // Use bootstrap config for all configuration. - // This allows config overrides via global variables to take effect. - $bootstrapConfig = $newServices->getBootstrapConfig(); - $newServices->resetServiceForTesting( 'ConfigFactory' ); - $newServices->redefineService( - 'ConfigFactory', - self::makeTestConfigFactoryInstantiator( - $oldConfigFactory, - [ 'main' => $bootstrapConfig ] - ) - ); - $newServices->resetServiceForTesting( 'DBLoadBalancerFactory' ); - $newServices->redefineService( - 'DBLoadBalancerFactory', - function ( MediaWikiServices $services ) use ( $oldLoadBalancerFactory ) { - return $oldLoadBalancerFactory; - } - ); - } - /** * @param ConfigFactory $oldFactory * @param Config[] $configurations @@ -362,7 +326,7 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase { * Resets some non-service singleton instances and other static caches. It's not necessary to * reset services here. */ - private function resetNonServiceCaches() { + public static function resetNonServiceCaches() { global $wgRequest, $wgJobClasses; foreach ( $wgJobClasses as $type => $class ) { @@ -386,13 +350,20 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase { } public function run( PHPUnit_Framework_TestResult $result = null ) { + if ( $result instanceof MediaWikiTestResult ) { + $this->cliArgs = $result->getMediaWikiCliArgs(); + } $this->overrideMwServices(); - $needsResetDB = false; + if ( $this->needsDB() && !$this->isTestInDatabaseGroup() ) { + throw new Exception( + get_class( $this ) . ' apparently needsDB but is not in the Database group' + ); + } + $needsResetDB = false; if ( !self::$dbSetup || $this->needsDB() ) { // set up a DB connection for this test to use - $this->testLogger->info( "Setting up DB for " . $this->toString() ); self::$useTemporaryTables = !$this->getCliArg( 'use-normal-tables' ); self::$reuseDB = $this->getCliArg( 'reuse-db' ); @@ -419,18 +390,14 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase { $needsResetDB = true; } - $this->testLogger->info( "Starting test " . $this->toString() ); parent::run( $result ); - $this->testLogger->info( "Finished test " . $this->toString() ); if ( $needsResetDB ) { - $this->testLogger->info( "Resetting DB" ); $this->resetDB( $this->db, $this->tablesUsed ); } - $this->localServices->destroy(); + self::restoreMwServices(); $this->localServices = null; - MediaWikiServices::forceGlobalInstance( self::$originalServices ); } /** @@ -524,7 +491,7 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase { } // Reset all caches between tests. - $this->resetNonServiceCaches(); + self::resetNonServiceCaches(); // XXX: reset maintenance triggers // Hook into period lag checks which often happen in long-running scripts @@ -640,6 +607,11 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase { throw new Exception( __METHOD__ . ' must be called after MediaWikiTestCase::run()' ); } + if ( $this->localServices !== MediaWikiServices::getInstance() ) { + throw new Exception( __METHOD__ . ' will not work because the global MediaWikiServices ' + . 'instance has been replaced by test code.' ); + } + $this->localServices->disableService( $name ); $this->localServices->redefineService( $name, @@ -726,6 +698,12 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase { if ( !$this->localServices ) { throw new Exception( __METHOD__ . ' must be called after MediaWikiTestCase::run()' ); } + + if ( $this->localServices !== MediaWikiServices::getInstance() ) { + throw new Exception( __METHOD__ . ' will not work because the global MediaWikiServices ' + . 'instance has been replaced by test code.' ); + } + MWNamespace::clearCaches(); Language::clearCaches(); @@ -895,6 +873,44 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase { protected function overrideMwServices( Config $configOverrides = null, array $services = [] ) { + $newInstance = self::installMockMwServices( $configOverrides ); + + if ( $this->localServices ) { + $this->localServices->destroy(); + } + + $this->localServices = $newInstance; + + foreach ( $services as $name => $callback ) { + $newInstance->redefineService( $name, $callback ); + } + + return $newInstance; + } + + /** + * Creates a new "mock" MediaWikiServices instance, and installs it. + * This effectively resets all cached states in services, with the exception of + * the ConfigFactory and the DBLoadBalancerFactory service, which are inherited from + * the original MediaWikiServices. + * + * @note The new original MediaWikiServices instance can later be restored by calling + * restoreMwServices(). That original is determined by the first call to this method, or + * by setUpBeforeClass, whichever is called first. The caller is responsible for managing + * and, when appropriate, destroying any other MediaWikiServices instances that may get + * replaced when calling this method. + * + * @param Config|null $configOverrides Configuration overrides for the new MediaWikiServices + * instance. + * + * @return MediaWikiServices the new mock service locator. + */ + public static function installMockMwServices( Config $configOverrides = null ) { + // Make sure we have the original service locator + if ( !self::$originalServices ) { + self::$originalServices = MediaWikiServices::getInstance(); + } + if ( !$configOverrides ) { $configOverrides = new HashConfig(); } @@ -903,34 +919,65 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase { $oldLoadBalancerFactory = self::$originalServices->getDBLoadBalancerFactory(); $testConfig = self::makeTestConfig( null, $configOverrides ); - $newInstance = new MediaWikiServices( $testConfig ); + $newServices = new MediaWikiServices( $testConfig ); // Load the default wiring from the specified files. // NOTE: this logic mirrors the logic in MediaWikiServices::newInstance. $wiringFiles = $testConfig->get( 'ServiceWiringFiles' ); - $newInstance->loadWiringFiles( $wiringFiles ); + $newServices->loadWiringFiles( $wiringFiles ); // Provide a traditional hook point to allow extensions to configure services. - Hooks::run( 'MediaWikiServices', [ $newInstance ] ); + Hooks::run( 'MediaWikiServices', [ $newServices ] ); - foreach ( $services as $name => $callback ) { - $newInstance->redefineService( $name, $callback ); + // Use bootstrap config for all configuration. + // This allows config overrides via global variables to take effect. + $bootstrapConfig = $newServices->getBootstrapConfig(); + $newServices->resetServiceForTesting( 'ConfigFactory' ); + $newServices->redefineService( + 'ConfigFactory', + self::makeTestConfigFactoryInstantiator( + $oldConfigFactory, + [ 'main' => $bootstrapConfig ] + ) + ); + $newServices->resetServiceForTesting( 'DBLoadBalancerFactory' ); + $newServices->redefineService( + 'DBLoadBalancerFactory', + function ( MediaWikiServices $services ) use ( $oldLoadBalancerFactory ) { + return $oldLoadBalancerFactory; + } + ); + + MediaWikiServices::forceGlobalInstance( $newServices ); + return $newServices; + } + + /** + * Restores the original, non-mock MediaWikiServices instance. + * The previously active MediaWikiServices instance is destroyed, + * if it is different from the original that is to be restored. + * + * @note this if for internal use by test framework code. It should never be + * called from inside a test case, a data provider, or a setUp or tearDown method. + * + * @return bool true if the original service locator was restored, + * false if there was nothing too do. + */ + public static function restoreMwServices() { + if ( !self::$originalServices ) { + return false; } - self::installTestServices( - $oldConfigFactory, - $oldLoadBalancerFactory, - $newInstance - ); + $currentServices = MediaWikiServices::getInstance(); - if ( $this->localServices ) { - $this->localServices->destroy(); + if ( self::$originalServices === $currentServices ) { + return false; } - MediaWikiServices::forceGlobalInstance( $newInstance ); - $this->localServices = $newInstance; + MediaWikiServices::forceGlobalInstance( self::$originalServices ); + $currentServices->destroy(); - return $newInstance; + return true; } /** @@ -1068,18 +1115,18 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase { */ public function needsDB() { // If the test says it uses database tables, it needs the database - if ( $this->tablesUsed ) { - return true; - } + return $this->tablesUsed || $this->isTestInDatabaseGroup(); + } + /** + * @return bool + * @since 1.32 + */ + protected function isTestInDatabaseGroup() { // If the test class says it belongs to the Database group, it needs the database. // NOTE: This ONLY checks for the group in the class level doc comment. $rc = new ReflectionClass( $this ); - if ( preg_match( '/@group +Database/im', $rc->getDocComment() ) ) { - return true; - } - - return false; + return (bool)preg_match( '/@group +Database/im', $rc->getDocComment() ); } /** @@ -1154,7 +1201,6 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase { * @since 1.32 */ protected function addCoreDBData() { - $this->testLogger->info( __METHOD__ ); if ( $this->db->getType() == 'oracle' ) { # Insert 0 user to prevent FK violations # Anonymous user @@ -1633,7 +1679,7 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase { $originalTables = $db->listTables( $db->_originalTablePrefix, __METHOD__ ); if ( $prefix === 'unprefixed' ) { - $originalPrefixRegex = '/^' . preg_quote( $db->_originalTablePrefix ) . '/'; + $originalPrefixRegex = '/^' . preg_quote( $db->_originalTablePrefix, '/' ) . '/'; $originalTables = array_map( function ( $pt ) use ( $originalPrefixRegex ) { return preg_replace( $originalPrefixRegex, '', $pt ); @@ -1838,11 +1884,7 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase { * @return mixed */ public function getCliArg( $offset ) { - if ( isset( PHPUnitMaintClass::$additionalOptions[$offset] ) ) { - return PHPUnitMaintClass::$additionalOptions[$offset]; - } - - return null; + return $this->cliArgs[$offset] ?? null; } /** @@ -1851,7 +1893,7 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase { * @param mixed $value */ public function setCliArg( $offset, $value ) { - PHPUnitMaintClass::$additionalOptions[$offset] = $value; + $this->cliArgs[$offset] = $value; } /** @@ -2228,7 +2270,7 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase { protected function assertFileContains( $fileName, $actualData, - $createIfMissing = true, + $createIfMissing = false, $msg = '' ) { if ( $createIfMissing ) {