getFileName(), "${dirSeparator}unit${dirSeparator}" ) === false ) { self::fail( 'This unit test needs to be in "tests/phpunit/unit"!' ); } self::$unitGlobals =& TestSetup::$bootstrapGlobals; foreach ( self::getGlobalsWhitelist() as $global ) { self::$unitGlobals[ $global ] =& $GLOBALS[ $global ]; } self::$temporaryHooks = []; // Would be nice if we could simply replace $GLOBALS as a whole, // but un-setting or re-assigning that breaks the reference of this magic // variable. Thus we have to modify it in place. self::$originalGlobals = []; foreach ( $GLOBALS as $key => $_ ) { // Stash current values self::$originalGlobals[$key] =& $GLOBALS[$key]; // Remove globals not part of the snapshot (see bootstrap.php, phpunit.php). if ( $key !== 'GLOBALS' && !array_key_exists( $key, self::$unitGlobals ) ) { unset( $GLOBALS[$key] ); } } // Restore values from the early snapshot // Not by ref because tests must not be able to modify the snapshot. foreach ( self::$unitGlobals as $key => $value ) { $GLOBALS[ $key ] = $value; } } /** * @inheritDoc */ protected function runTest() { try { return parent::runTest(); } catch ( ConfigException $exception ) { throw new Exception( 'Config variables must be mocked, they cannot be accessed directly in tests which extend ' . self::class, $exception->getCode(), $exception ); } // Don't let LoggerFactory::getProvider() access globals or other things we don't want. LoggerFactory::registerProvider( ObjectFactory::getObjectFromSpec( [ 'class' => \MediaWiki\Logger\NullSpi::class ] ) ); } /** * @stable for overriding */ protected function tearDown() : void { // Quick reset between tests foreach ( $GLOBALS as $key => $_ ) { if ( $key !== 'GLOBALS' && !array_key_exists( $key, self::$unitGlobals ) ) { unset( $GLOBALS[$key] ); } } foreach ( self::$unitGlobals as $key => $value ) { $GLOBALS[ $key ] = $value; } self::$temporaryHooks = []; parent::tearDown(); } /** * @stable for overriding */ public static function tearDownAfterClass() : void { // Remove globals created by the test foreach ( $GLOBALS as $key => $_ ) { if ( $key !== 'GLOBALS' && !array_key_exists( $key, self::$originalGlobals ) ) { unset( $GLOBALS[$key] ); } } // Restore values (including reference!) foreach ( self::$originalGlobals as $key => &$value ) { $GLOBALS[ $key ] =& $value; } parent::tearDownAfterClass(); } /** * Create a temporary hook handler which will be reset by tearDown. * @param string $hookName Hook name * @param mixed $handler Value suitable for a hook handler * @since 1.34 */ protected function setTemporaryHook( $hookName, $handler ) { // Adds handler to list of hook handlers $hookContainer = MediaWikiServices::getInstance()->getHookContainer(); $hookToRemove = $hookContainer->scopedRegister( $hookName, $handler, true ); // Keep reference to the ScopedCallback self::$temporaryHooks[] = $hookToRemove; } }