2 use Liuggio\StatsdClient\Factory\StatsdDataFactory
;
3 use MediaWiki\Interwiki\InterwikiLookup
;
4 use MediaWiki\MediaWikiServices
;
5 use MediaWiki\Services\DestructibleService
;
6 use MediaWiki\Services\SalvageableService
;
7 use MediaWiki\Services\ServiceDisabledException
;
10 * @covers MediaWiki\MediaWikiServices
14 class MediaWikiServicesTest
extends MediaWikiTestCase
{
19 private function newTestConfig() {
20 $globalConfig = new GlobalVarConfig();
22 $testConfig = new HashConfig();
23 $testConfig->set( 'ServiceWiringFiles', $globalConfig->get( 'ServiceWiringFiles' ) );
24 $testConfig->set( 'ConfigRegistry', $globalConfig->get( 'ConfigRegistry' ) );
30 * @return MediaWikiServices
32 private function newMediaWikiServices( Config
$config = null ) {
33 if ( $config === null ) {
34 $config = $this->newTestConfig();
37 $instance = new MediaWikiServices( $config );
39 // Load the default wiring from the specified files.
40 $wiringFiles = $config->get( 'ServiceWiringFiles' );
41 $instance->loadWiringFiles( $wiringFiles );
46 public function testGetInstance() {
47 $services = MediaWikiServices
::getInstance();
48 $this->assertInstanceOf( 'MediaWiki\\MediaWikiServices', $services );
51 public function testForceGlobalInstance() {
52 $newServices = $this->newMediaWikiServices();
53 $oldServices = MediaWikiServices
::forceGlobalInstance( $newServices );
55 $this->assertInstanceOf( 'MediaWiki\\MediaWikiServices', $oldServices );
56 $this->assertNotSame( $oldServices, $newServices );
58 $theServices = MediaWikiServices
::getInstance();
59 $this->assertSame( $theServices, $newServices );
61 MediaWikiServices
::forceGlobalInstance( $oldServices );
63 $theServices = MediaWikiServices
::getInstance();
64 $this->assertSame( $theServices, $oldServices );
67 public function testResetGlobalInstance() {
68 $newServices = $this->newMediaWikiServices();
69 $oldServices = MediaWikiServices
::forceGlobalInstance( $newServices );
71 $service1 = $this->getMock( SalvageableService
::class );
72 $service1->expects( $this->never() )
73 ->method( 'salvage' );
75 $newServices->defineService(
77 function() use ( $service1 ) {
82 // force instantiation
83 $newServices->getService( 'Test' );
85 MediaWikiServices
::resetGlobalInstance( $this->newTestConfig() );
86 $theServices = MediaWikiServices
::getInstance();
90 $theServices->getService( 'Test' ),
91 'service definition should survive reset'
94 $this->assertNotSame( $theServices, $newServices );
95 $this->assertNotSame( $theServices, $oldServices );
97 MediaWikiServices
::forceGlobalInstance( $oldServices );
100 public function testResetGlobalInstance_quick() {
101 $newServices = $this->newMediaWikiServices();
102 $oldServices = MediaWikiServices
::forceGlobalInstance( $newServices );
104 $service1 = $this->getMock( SalvageableService
::class );
105 $service1->expects( $this->never() )
106 ->method( 'salvage' );
108 $service2 = $this->getMock( SalvageableService
::class );
109 $service2->expects( $this->once() )
110 ->method( 'salvage' )
113 // sequence of values the instantiator will return
114 $instantiatorReturnValues = [
119 $newServices->defineService(
121 function() use ( &$instantiatorReturnValues ) {
122 return array_shift( $instantiatorReturnValues );
126 // force instantiation
127 $newServices->getService( 'Test' );
129 MediaWikiServices
::resetGlobalInstance( $this->newTestConfig(), 'quick' );
130 $theServices = MediaWikiServices
::getInstance();
132 $this->assertSame( $service2, $theServices->getService( 'Test' ) );
134 $this->assertNotSame( $theServices, $newServices );
135 $this->assertNotSame( $theServices, $oldServices );
137 MediaWikiServices
::forceGlobalInstance( $oldServices );
140 public function testDisableStorageBackend() {
141 $newServices = $this->newMediaWikiServices();
142 $oldServices = MediaWikiServices
::forceGlobalInstance( $newServices );
144 $lbFactory = $this->getMockBuilder( 'LBFactorySimple' )
145 ->disableOriginalConstructor()
148 $lbFactory->expects( $this->once() )
149 ->method( 'destroy' );
151 $newServices->redefineService(
152 'DBLoadBalancerFactory',
153 function() use ( $lbFactory ) {
158 // force the service to become active, so we can check that it does get destroyed
159 $newServices->getService( 'DBLoadBalancerFactory' );
161 MediaWikiServices
::disableStorageBackend(); // should destroy DBLoadBalancerFactory
164 MediaWikiServices
::getInstance()->getService( 'DBLoadBalancerFactory' );
165 $this->fail( 'DBLoadBalancerFactory shoudl have been disabled' );
167 catch ( ServiceDisabledException
$ex ) {
170 catch ( Throwable
$ex ) {
171 $this->fail( 'ServiceDisabledException expected, caught ' . get_class( $ex ) );
174 MediaWikiServices
::forceGlobalInstance( $oldServices );
175 $newServices->destroy();
178 public function testResetChildProcessServices() {
179 $newServices = $this->newMediaWikiServices();
180 $oldServices = MediaWikiServices
::forceGlobalInstance( $newServices );
182 $service1 = $this->getMock( DestructibleService
::class );
183 $service1->expects( $this->once() )
184 ->method( 'destroy' );
186 $service2 = $this->getMock( DestructibleService
::class );
187 $service2->expects( $this->never() )
188 ->method( 'destroy' );
190 // sequence of values the instantiator will return
191 $instantiatorReturnValues = [
196 $newServices->defineService(
198 function() use ( &$instantiatorReturnValues ) {
199 return array_shift( $instantiatorReturnValues );
203 // force the service to become active, so we can check that it does get destroyed
204 $oldTestService = $newServices->getService( 'Test' );
206 MediaWikiServices
::resetChildProcessServices();
207 $finalServices = MediaWikiServices
::getInstance();
209 $newTestService = $finalServices->getService( 'Test' );
210 $this->assertNotSame( $oldTestService, $newTestService );
212 MediaWikiServices
::forceGlobalInstance( $oldServices );
215 public function testResetServiceForTesting() {
216 $services = $this->newMediaWikiServices();
219 $services->defineService(
221 function() use ( &$serviceCounter ) {
223 $service = $this->getMock( 'MediaWiki\Services\DestructibleService' );
224 $service->expects( $this->once() )->method( 'destroy' );
229 // This should do nothing. In particular, it should not create a service instance.
230 $services->resetServiceForTesting( 'Test' );
231 $this->assertEquals( 0, $serviceCounter, 'No service instance should be created yet.' );
233 $oldInstance = $services->getService( 'Test' );
234 $this->assertEquals( 1, $serviceCounter, 'A service instance should exit now.' );
236 // The old instance should be detached, and destroy() called.
237 $services->resetServiceForTesting( 'Test' );
238 $newInstance = $services->getService( 'Test' );
240 $this->assertNotSame( $oldInstance, $newInstance );
242 // Satisfy the expectation that destroy() is called also for the second service instance.
243 $newInstance->destroy();
246 public function testResetServiceForTesting_noDestroy() {
247 $services = $this->newMediaWikiServices();
249 $services->defineService(
252 $service = $this->getMock( 'MediaWiki\Services\DestructibleService' );
253 $service->expects( $this->never() )->method( 'destroy' );
258 $oldInstance = $services->getService( 'Test' );
260 // The old instance should be detached, but destroy() not called.
261 $services->resetServiceForTesting( 'Test', false );
262 $newInstance = $services->getService( 'Test' );
264 $this->assertNotSame( $oldInstance, $newInstance );
267 public function provideGetters() {
268 $getServiceCases = $this->provideGetService();
271 // All getters should be named just like the service, with "get" added.
272 foreach ( $getServiceCases as $name => $case ) {
273 if ( $name[0] === '_' ) {
274 // Internal service, no getter
277 list( $service, $class ) = $case;
278 $getterCases[$name] = [
288 * @dataProvider provideGetters
290 public function testGetters( $getter, $type ) {
291 // Test against the default instance, since the dummy will not know the default services.
292 $services = MediaWikiServices
::getInstance();
293 $service = $services->$getter();
294 $this->assertInstanceOf( $type, $service );
297 public function provideGetService() {
298 // NOTE: This should list all service getters defined in ServiceWiring.php.
299 // NOTE: For every test case defined here there should be a corresponding
300 // test case defined in provideGetters().
302 'BootstrapConfig' => [ 'BootstrapConfig', Config
::class ],
303 'ConfigFactory' => [ 'ConfigFactory', ConfigFactory
::class ],
304 'MainConfig' => [ 'MainConfig', Config
::class ],
305 'SiteStore' => [ 'SiteStore', SiteStore
::class ],
306 'SiteLookup' => [ 'SiteLookup', SiteLookup
::class ],
307 'StatsdDataFactory' => [ 'StatsdDataFactory', StatsdDataFactory
::class ],
308 'InterwikiLookup' => [ 'InterwikiLookup', InterwikiLookup
::class ],
309 'EventRelayerGroup' => [ 'EventRelayerGroup', EventRelayerGroup
::class ],
310 'SearchEngineFactory' => [ 'SearchEngineFactory', SearchEngineFactory
::class ],
311 'SearchEngineConfig' => [ 'SearchEngineConfig', SearchEngineConfig
::class ],
312 'SkinFactory' => [ 'SkinFactory', SkinFactory
::class ],
313 'DBLoadBalancerFactory' => [ 'DBLoadBalancerFactory', 'LBFactory' ],
314 'DBLoadBalancer' => [ 'DBLoadBalancer', 'LoadBalancer' ],
315 'WatchedItemStore' => [ 'WatchedItemStore', WatchedItemStore
::class ],
316 'GenderCache' => [ 'GenderCache', GenderCache
::class ],
317 'LinkCache' => [ 'LinkCache', LinkCache
::class ],
318 '_MediaWikiTitleCodec' => [ '_MediaWikiTitleCodec', MediaWikiTitleCodec
::class ],
319 'TitleFormatter' => [ 'TitleFormatter', TitleFormatter
::class ],
320 'TitleParser' => [ 'TitleParser', TitleParser
::class ],
325 * @dataProvider provideGetService
327 public function testGetService( $name, $type ) {
328 // Test against the default instance, since the dummy will not know the default services.
329 $services = MediaWikiServices
::getInstance();
331 $service = $services->getService( $name );
332 $this->assertInstanceOf( $type, $service );
335 public function testDefaultServiceInstantiation() {
336 // Check all services in the default instance, not a dummy instance!
337 // Note that we instantiate all services here, including any that
338 // were registered by extensions.
339 $services = MediaWikiServices
::getInstance();
340 $names = $services->getServiceNames();
342 foreach ( $names as $name ) {
343 $this->assertTrue( $services->hasService( $name ) );
344 $service = $services->getService( $name );
345 $this->assertInternalType( 'object', $service );