public static function resetNonServiceCaches() {
global $wgRequest, $wgJobClasses;
+ User::resetGetDefaultOptionsForTestsOnly();
foreach ( $wgJobClasses as $type => $class ) {
JobQueueGroup::singleton()->get( $type )->delete();
}
self::$useTemporaryTables = !$this->getCliArg( 'use-normal-tables' );
self::$reuseDB = $this->getCliArg( 'reuse-db' );
- $this->db = wfGetDB( DB_MASTER );
+ $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+ $this->db = $lb->getConnection( DB_MASTER );
$this->checkDbIsSupported();
* @return string Absolute name of the temporary file
*/
protected function getNewTempFile() {
- $fileName = tempnam( wfTempDir(), 'MW_PHPUnit_' . static::class . '_' );
+ $fileName = tempnam(
+ wfTempDir(),
+ // Avoid backslashes here as they result in inconsistent results
+ // between Windows and other OS, as well as between functions
+ // that try to normalise these in one or both directions.
+ // For example, tempnam rejects directory separators in the prefix which
+ // means it rejects any namespaced class on Windows.
+ // And then there is, wfMkdirParents which normalises paths always
+ // whereas most other PHP and MW functions do not.
+ 'MW_PHPUnit_' . strtr( static::class, [ '\\' => '_' ] ) . '_'
+ );
$this->tmpFiles[] = $fileName;
return $fileName;
* @return string Absolute name of the temporary directory
*/
protected function getNewTempDirectory() {
- // Starting of with a temporary /file/.
+ // Starting of with a temporary *file*.
$fileName = $this->getNewTempFile();
- // Converting the temporary /file/ to a /directory/
+ // Converting the temporary file to a *directory*.
// The following is not atomic, but at least we now have a single place,
- // where temporary directory creation is bundled and can be improved
+ // where temporary directory creation is bundled and can be improved.
unlink( $fileName );
- $this->assertTrue( wfMkdirParents( $fileName ) );
+ // If this fails for some reason, PHP will warn and fail the test.
+ mkdir( $fileName, 0777, /* recursive = */ true );
return $fileName;
}
$this->tmpFiles = array_merge( $this->tmpFiles, (array)$files );
}
+ // @todo Make const when we no longer support HHVM (T192166)
+ private static $namespaceAffectingSettings = [
+ 'wgAllowImageMoving',
+ 'wgCanonicalNamespaceNames',
+ 'wgCapitalLinkOverrides',
+ 'wgCapitalLinks',
+ 'wgContentNamespaces',
+ 'wgExtensionMessagesFiles',
+ 'wgExtensionNamespaces',
+ 'wgExtraNamespaces',
+ 'wgExtraSignatureNamespaces',
+ 'wgNamespaceContentModels',
+ 'wgNamespaceProtection',
+ 'wgNamespacesWithSubpages',
+ 'wgNonincludableNamespaces',
+ 'wgRestrictionLevels',
+ ];
+
protected function tearDown() {
global $wgRequest, $wgSQLMode;
ini_set( $name, $value );
}
if (
- array_key_exists( 'wgExtraNamespaces', $this->mwGlobals ) ||
- in_array( 'wgExtraNamespaces', $this->mwGlobalsToUnset )
+ array_intersect( self::$namespaceAffectingSettings, array_keys( $this->mwGlobals ) ) ||
+ array_intersect( self::$namespaceAffectingSettings, $this->mwGlobalsToUnset )
) {
$this->resetNamespaces();
}
$GLOBALS[$key] = $value;
}
- if ( array_key_exists( 'wgExtraNamespaces', $pairs ) ) {
+ if ( array_intersect( self::$namespaceAffectingSettings, array_keys( $pairs ) ) ) {
$this->resetNamespaces();
}
}
. 'instance has been replaced by test code.' );
}
- MWNamespace::clearCaches();
Language::clearCaches();
-
- // We can't have the TitleFormatter holding on to an old Language object either
- // @todo We shouldn't need to reset all the aliases here.
- $this->localServices->resetServiceForTesting( 'TitleFormatter' );
- $this->localServices->resetServiceForTesting( 'TitleParser' );
- $this->localServices->resetServiceForTesting( '_MediaWikiTitleCodec' );
}
/**
return false;
}
- /**
- * Stashes the global, will be restored in tearDown()
- *
- * Individual test functions may override globals through the setMwGlobals() function
- * or directly. When directly overriding globals their keys should first be passed to this
- * method in setUp to avoid breaking global state for other tests
- *
- * That way all other tests are executed with the same settings (instead of using the
- * unreliable local settings for most tests and fix it only for some tests).
- *
- * @param array|string $globalKeys Key to the global variable, or an array of keys.
- *
- * @note To allow changes to global variables to take effect on global service instances,
- * call overrideMwServices().
- *
- * @since 1.23
- * @deprecated since 1.32, use setMwGlobals() and don't alter globals directly
- */
- protected function stashMwGlobals( $globalKeys ) {
- wfDeprecated( __METHOD__, '1.32' );
- $this->doStashMwGlobals( $globalKeys );
- }
-
private function doStashMwGlobals( $globalKeys ) {
if ( is_string( $globalKeys ) ) {
$globalKeys = [ $globalKeys ];
$newInstance->redefineService( $name, $callback );
}
+ self::resetGlobalParser();
+
return $newInstance;
}
);
MediaWikiServices::forceGlobalInstance( $newServices );
+
+ self::resetGlobalParser();
+
return $newServices;
}
MediaWikiServices::forceGlobalInstance( self::$originalServices );
$currentServices->destroy();
+ self::resetGlobalParser();
+
return true;
}
+ /**
+ * If $wgParser has been unstubbed, replace it with a fresh one so it picks up any config
+ * changes. $wgParser is deprecated, but we still support it for now.
+ */
+ private static function resetGlobalParser() {
+ // phpcs:ignore MediaWiki.Usage.DeprecatedGlobalVariables.Deprecated$wgParser
+ global $wgParser;
+ if ( $wgParser instanceof StubObject ) {
+ return;
+ }
+ $wgParser = new StubObject( 'wgParser', function () {
+ return MediaWikiServices::getInstance()->getParser();
+ } );
+ }
+
/**
* @since 1.27
* @param string|Language $lang
JobQueueGroup::singleton()->get( $type )->delete();
}
+ // T219673: close any connections from code that failed to call reuseConnection()
+ // or is still holding onto a DBConnRef instance (e.g. in a singleton).
+ MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->closeAll();
CloneDatabase::changePrefix( self::$oldTablePrefix );
self::$oldTablePrefix = false;
* @note this method only works when first called. Subsequent calls have no effect,
* even if using different parameters.
*
- * @param Database $db The database connection
+ * @param IMaintainableDatabase $db The database connection
* @param string $prefix The prefix to use for the new table set (aka schema).
*
* @throws MWException If the database table prefix is already $prefix
*/
- public static function setupTestDB( Database $db, $prefix ) {
+ public static function setupTestDB( IMaintainableDatabase $db, $prefix ) {
if ( self::$dbSetup ) {
return;
}
if ( $tablesToRestore ) {
$this->recloneMockTables( $db, $tablesToRestore );
+
+ // Reset the restored tables, mainly for the side effect of
+ // re-calling $this->addCoreDBData() if necessary.
+ $this->resetDB( $db, $tablesToRestore );
}
}
if ( $oldOverrides['alter'] || $oldOverrides['create'] || $oldOverrides['drop'] ) {
$this->undoSchemaOverrides( $db, $oldOverrides );
+ unset( $db->_schemaOverrides );
}
// Determine new overrides.
if ( array_intersect( $tablesUsed, $userTables ) ) {
$tablesUsed = array_unique( array_merge( $tablesUsed, $userTables ) );
TestUserRegistry::clear();
+
+ // Reset $wgUser, which is probably 127.0.0.1, as its loaded data is probably not valid
+ // @todo Should we start setting $wgUser to something nondeterministic
+ // to encourage tests to be updated to not depend on it?
+ global $wgUser;
+ $wgUser->clearInstanceCache( $wgUser->mFrom );
}
if ( array_intersect( $tablesUsed, $pageTables ) ) {
$tablesUsed = array_unique( array_merge( $tablesUsed, $pageTables ) );
}
// NOTE: prefer content namespaces
+ $nsInfo = MediaWikiServices::getInstance()->getNamespaceInfo();
$namespaces = array_unique( array_merge(
- MWNamespace::getContentNamespaces(),
+ $nsInfo->getContentNamespaces(),
[ NS_MAIN, NS_HELP, NS_PROJECT ], // prefer these
- MWNamespace::getValidNamespaces()
+ $nsInfo->getValidNamespaces()
) );
$namespaces = array_diff( $namespaces, [
NS_FILE, NS_CATEGORY, NS_MEDIAWIKI, NS_USER // don't mess with magic namespaces
] );
- $talk = array_filter( $namespaces, function ( $ns ) {
- return MWNamespace::isTalk( $ns );
+ $talk = array_filter( $namespaces, function ( $ns ) use ( $nsInfo ) {
+ return $nsInfo->isTalk( $ns );
} );
// prefer non-talk pages
if ( $createIfMissing ) {
if ( !file_exists( $fileName ) ) {
file_put_contents( $fileName, $actualData );
- $this->markTestSkipped( 'Data file $fileName does not exist' );
+ $this->markTestSkipped( "Data file $fileName does not exist" );
}
} else {
self::assertFileExists( $fileName );
* @param string $text Content of the page
* @param string $summary Optional summary string for the revision
* @param int $defaultNs Optional namespace id
- * @return array Array as returned by WikiPage::doEditContent()
+ * @return Status Object as returned by WikiPage::doEditContent()
* @throws MWException If this test cases's needsDB() method doesn't return true.
* Test cases can use "@group Database" to enable database test support,
* or list the tables under testing in $this->tablesUsed, or override the
'comment' => $comment,
] );
}
+
+ /**
+ * Returns a PHPUnit constraint that matches anything other than a fixed set of values. This can
+ * be used to whitelist values, e.g.
+ * $mock->expects( $this->never() )->method( $this->anythingBut( 'foo', 'bar' ) );
+ * which will throw if any unexpected method is called.
+ *
+ * @param mixed ...$values Values that are not matched
+ */
+ protected function anythingBut( ...$values ) {
+ return $this->logicalNot( $this->logicalOr(
+ ...array_map( [ $this, 'matches' ], $values )
+ ) );
+ }
}