use Wikimedia\Rdbms\IDatabase;
use Wikimedia\Rdbms\IMaintainableDatabase;
use Wikimedia\Rdbms\Database;
-use Wikimedia\Rdbms\LBFactory;
use Wikimedia\TestingAccessWrapper;
/**
/**
* The local service locator, created during setUp().
+ * @var MediaWikiServices
*/
private $localServices;
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
* 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 ) {
$this->resetDB( $this->db, $this->tablesUsed );
}
- $this->localServices->destroy();
+ self::restoreMwServices();
$this->localServices = null;
- MediaWikiServices::forceGlobalInstance( self::$originalServices );
}
/**
}
// 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
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,
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();
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();
}
$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;
}
/**