2 use Liuggio\StatsdClient\Factory\StatsdDataFactory
;
3 use MediaWiki\Interwiki\InterwikiLookup
;
4 use MediaWiki\Linker\LinkRenderer
;
5 use MediaWiki\Linker\LinkRendererFactory
;
6 use MediaWiki\MediaWikiServices
;
7 use MediaWiki\Services\DestructibleService
;
8 use MediaWiki\Services\SalvageableService
;
9 use MediaWiki\Services\ServiceDisabledException
;
12 * @covers MediaWiki\MediaWikiServices
16 class MediaWikiServicesTest
extends MediaWikiTestCase
{
21 private function newTestConfig() {
22 $globalConfig = new GlobalVarConfig();
24 $testConfig = new HashConfig();
25 $testConfig->set( 'ServiceWiringFiles', $globalConfig->get( 'ServiceWiringFiles' ) );
26 $testConfig->set( 'ConfigRegistry', $globalConfig->get( 'ConfigRegistry' ) );
32 * @return MediaWikiServices
34 private function newMediaWikiServices( Config
$config = null ) {
35 if ( $config === null ) {
36 $config = $this->newTestConfig();
39 $instance = new MediaWikiServices( $config );
41 // Load the default wiring from the specified files.
42 $wiringFiles = $config->get( 'ServiceWiringFiles' );
43 $instance->loadWiringFiles( $wiringFiles );
48 public function testGetInstance() {
49 $services = MediaWikiServices
::getInstance();
50 $this->assertInstanceOf( 'MediaWiki\\MediaWikiServices', $services );
53 public function testForceGlobalInstance() {
54 $newServices = $this->newMediaWikiServices();
55 $oldServices = MediaWikiServices
::forceGlobalInstance( $newServices );
57 $this->assertInstanceOf( 'MediaWiki\\MediaWikiServices', $oldServices );
58 $this->assertNotSame( $oldServices, $newServices );
60 $theServices = MediaWikiServices
::getInstance();
61 $this->assertSame( $theServices, $newServices );
63 MediaWikiServices
::forceGlobalInstance( $oldServices );
65 $theServices = MediaWikiServices
::getInstance();
66 $this->assertSame( $theServices, $oldServices );
69 public function testResetGlobalInstance() {
70 $newServices = $this->newMediaWikiServices();
71 $oldServices = MediaWikiServices
::forceGlobalInstance( $newServices );
73 $service1 = $this->getMock( SalvageableService
::class );
74 $service1->expects( $this->never() )
75 ->method( 'salvage' );
77 $newServices->defineService(
79 function() use ( $service1 ) {
84 // force instantiation
85 $newServices->getService( 'Test' );
87 MediaWikiServices
::resetGlobalInstance( $this->newTestConfig() );
88 $theServices = MediaWikiServices
::getInstance();
92 $theServices->getService( 'Test' ),
93 'service definition should survive reset'
96 $this->assertNotSame( $theServices, $newServices );
97 $this->assertNotSame( $theServices, $oldServices );
99 MediaWikiServices
::forceGlobalInstance( $oldServices );
102 public function testResetGlobalInstance_quick() {
103 $newServices = $this->newMediaWikiServices();
104 $oldServices = MediaWikiServices
::forceGlobalInstance( $newServices );
106 $service1 = $this->getMock( SalvageableService
::class );
107 $service1->expects( $this->never() )
108 ->method( 'salvage' );
110 $service2 = $this->getMock( SalvageableService
::class );
111 $service2->expects( $this->once() )
112 ->method( 'salvage' )
115 // sequence of values the instantiator will return
116 $instantiatorReturnValues = [
121 $newServices->defineService(
123 function() use ( &$instantiatorReturnValues ) {
124 return array_shift( $instantiatorReturnValues );
128 // force instantiation
129 $newServices->getService( 'Test' );
131 MediaWikiServices
::resetGlobalInstance( $this->newTestConfig(), 'quick' );
132 $theServices = MediaWikiServices
::getInstance();
134 $this->assertSame( $service2, $theServices->getService( 'Test' ) );
136 $this->assertNotSame( $theServices, $newServices );
137 $this->assertNotSame( $theServices, $oldServices );
139 MediaWikiServices
::forceGlobalInstance( $oldServices );
142 public function testDisableStorageBackend() {
143 $newServices = $this->newMediaWikiServices();
144 $oldServices = MediaWikiServices
::forceGlobalInstance( $newServices );
146 $lbFactory = $this->getMockBuilder( 'LBFactorySimple' )
147 ->disableOriginalConstructor()
150 $lbFactory->expects( $this->once() )
151 ->method( 'destroy' );
153 $newServices->redefineService(
154 'DBLoadBalancerFactory',
155 function() use ( $lbFactory ) {
160 // force the service to become active, so we can check that it does get destroyed
161 $newServices->getService( 'DBLoadBalancerFactory' );
163 MediaWikiServices
::disableStorageBackend(); // should destroy DBLoadBalancerFactory
166 MediaWikiServices
::getInstance()->getService( 'DBLoadBalancerFactory' );
167 $this->fail( 'DBLoadBalancerFactory shoudl have been disabled' );
169 catch ( ServiceDisabledException
$ex ) {
172 catch ( Throwable
$ex ) {
173 $this->fail( 'ServiceDisabledException expected, caught ' . get_class( $ex ) );
176 MediaWikiServices
::forceGlobalInstance( $oldServices );
177 $newServices->destroy();
180 public function testResetChildProcessServices() {
181 $newServices = $this->newMediaWikiServices();
182 $oldServices = MediaWikiServices
::forceGlobalInstance( $newServices );
184 $service1 = $this->getMock( DestructibleService
::class );
185 $service1->expects( $this->once() )
186 ->method( 'destroy' );
188 $service2 = $this->getMock( DestructibleService
::class );
189 $service2->expects( $this->never() )
190 ->method( 'destroy' );
192 // sequence of values the instantiator will return
193 $instantiatorReturnValues = [
198 $newServices->defineService(
200 function() use ( &$instantiatorReturnValues ) {
201 return array_shift( $instantiatorReturnValues );
205 // force the service to become active, so we can check that it does get destroyed
206 $oldTestService = $newServices->getService( 'Test' );
208 MediaWikiServices
::resetChildProcessServices();
209 $finalServices = MediaWikiServices
::getInstance();
211 $newTestService = $finalServices->getService( 'Test' );
212 $this->assertNotSame( $oldTestService, $newTestService );
214 MediaWikiServices
::forceGlobalInstance( $oldServices );
217 public function testResetServiceForTesting() {
218 $services = $this->newMediaWikiServices();
221 $services->defineService(
223 function() use ( &$serviceCounter ) {
225 $service = $this->getMock( 'MediaWiki\Services\DestructibleService' );
226 $service->expects( $this->once() )->method( 'destroy' );
231 // This should do nothing. In particular, it should not create a service instance.
232 $services->resetServiceForTesting( 'Test' );
233 $this->assertEquals( 0, $serviceCounter, 'No service instance should be created yet.' );
235 $oldInstance = $services->getService( 'Test' );
236 $this->assertEquals( 1, $serviceCounter, 'A service instance should exit now.' );
238 // The old instance should be detached, and destroy() called.
239 $services->resetServiceForTesting( 'Test' );
240 $newInstance = $services->getService( 'Test' );
242 $this->assertNotSame( $oldInstance, $newInstance );
244 // Satisfy the expectation that destroy() is called also for the second service instance.
245 $newInstance->destroy();
248 public function testResetServiceForTesting_noDestroy() {
249 $services = $this->newMediaWikiServices();
251 $services->defineService(
254 $service = $this->getMock( 'MediaWiki\Services\DestructibleService' );
255 $service->expects( $this->never() )->method( 'destroy' );
260 $oldInstance = $services->getService( 'Test' );
262 // The old instance should be detached, but destroy() not called.
263 $services->resetServiceForTesting( 'Test', false );
264 $newInstance = $services->getService( 'Test' );
266 $this->assertNotSame( $oldInstance, $newInstance );
269 public function provideGetters() {
270 $getServiceCases = $this->provideGetService();
273 // All getters should be named just like the service, with "get" added.
274 foreach ( $getServiceCases as $name => $case ) {
275 if ( $name[0] === '_' ) {
276 // Internal service, no getter
279 list( $service, $class ) = $case;
280 $getterCases[$name] = [
290 * @dataProvider provideGetters
292 public function testGetters( $getter, $type ) {
293 // Test against the default instance, since the dummy will not know the default services.
294 $services = MediaWikiServices
::getInstance();
295 $service = $services->$getter();
296 $this->assertInstanceOf( $type, $service );
299 public function provideGetService() {
300 // NOTE: This should list all service getters defined in ServiceWiring.php.
301 // NOTE: For every test case defined here there should be a corresponding
302 // test case defined in provideGetters().
304 'BootstrapConfig' => [ 'BootstrapConfig', Config
::class ],
305 'ConfigFactory' => [ 'ConfigFactory', ConfigFactory
::class ],
306 'MainConfig' => [ 'MainConfig', Config
::class ],
307 'SiteStore' => [ 'SiteStore', SiteStore
::class ],
308 'SiteLookup' => [ 'SiteLookup', SiteLookup
::class ],
309 'StatsdDataFactory' => [ 'StatsdDataFactory', StatsdDataFactory
::class ],
310 'InterwikiLookup' => [ 'InterwikiLookup', InterwikiLookup
::class ],
311 'EventRelayerGroup' => [ 'EventRelayerGroup', EventRelayerGroup
::class ],
312 'SearchEngineFactory' => [ 'SearchEngineFactory', SearchEngineFactory
::class ],
313 'SearchEngineConfig' => [ 'SearchEngineConfig', SearchEngineConfig
::class ],
314 'SkinFactory' => [ 'SkinFactory', SkinFactory
::class ],
315 'DBLoadBalancerFactory' => [ 'DBLoadBalancerFactory', 'LBFactory' ],
316 'DBLoadBalancer' => [ 'DBLoadBalancer', 'LoadBalancer' ],
317 'WatchedItemStore' => [ 'WatchedItemStore', WatchedItemStore
::class ],
318 'WatchedItemQueryService' => [ 'WatchedItemQueryService', WatchedItemQueryService
::class ],
319 'MediaHandlerFactory' => [ 'MediaHandlerFactory', MediaHandlerFactory
::class ],
320 'GenderCache' => [ 'GenderCache', GenderCache
::class ],
321 'LinkCache' => [ 'LinkCache', LinkCache
::class ],
322 'LinkRenderer' => [ 'LinkRenderer', LinkRenderer
::class ],
323 'LinkRendererFactory' => [ 'LinkRendererFactory', LinkRendererFactory
::class ],
324 '_MediaWikiTitleCodec' => [ '_MediaWikiTitleCodec', MediaWikiTitleCodec
::class ],
325 'TitleFormatter' => [ 'TitleFormatter', TitleFormatter
::class ],
326 'TitleParser' => [ 'TitleParser', TitleParser
::class ],
327 'VirtualRESTServiceClient' => [ 'VirtualRESTServiceClient', VirtualRESTServiceClient
::class ]
332 * @dataProvider provideGetService
334 public function testGetService( $name, $type ) {
335 // Test against the default instance, since the dummy will not know the default services.
336 $services = MediaWikiServices
::getInstance();
338 $service = $services->getService( $name );
339 $this->assertInstanceOf( $type, $service );
342 public function testDefaultServiceInstantiation() {
343 // Check all services in the default instance, not a dummy instance!
344 // Note that we instantiate all services here, including any that
345 // were registered by extensions.
346 $services = MediaWikiServices
::getInstance();
347 $names = $services->getServiceNames();
349 foreach ( $names as $name ) {
350 $this->assertTrue( $services->hasService( $name ) );
351 $service = $services->getService( $name );
352 $this->assertInternalType( 'object', $service );