2 use Liuggio\StatsdClient\Factory\StatsdDataFactory
;
3 use MediaWiki\MediaWikiServices
;
4 use MediaWiki\Services\DestructibleService
;
5 use MediaWiki\Services\SalvageableService
;
6 use MediaWiki\Services\ServiceDisabledException
;
9 * @covers MediaWiki\MediaWikiServices
13 class MediaWikiServicesTest
extends MediaWikiTestCase
{
18 private function newTestConfig() {
19 $globalConfig = new GlobalVarConfig();
21 $testConfig = new HashConfig();
22 $testConfig->set( 'ServiceWiringFiles', $globalConfig->get( 'ServiceWiringFiles' ) );
23 $testConfig->set( 'ConfigRegistry', $globalConfig->get( 'ConfigRegistry' ) );
29 * @return MediaWikiServices
31 private function newMediaWikiServices( Config
$config = null ) {
32 if ( $config === null ) {
33 $config = $this->newTestConfig();
36 $instance = new MediaWikiServices( $config );
38 // Load the default wiring from the specified files.
39 $wiringFiles = $config->get( 'ServiceWiringFiles' );
40 $instance->loadWiringFiles( $wiringFiles );
45 public function testGetInstance() {
46 $services = MediaWikiServices
::getInstance();
47 $this->assertInstanceOf( 'MediaWiki\\MediaWikiServices', $services );
50 public function testForceGlobalInstance() {
51 $newServices = $this->newMediaWikiServices();
52 $oldServices = MediaWikiServices
::forceGlobalInstance( $newServices );
54 $this->assertInstanceOf( 'MediaWiki\\MediaWikiServices', $oldServices );
55 $this->assertNotSame( $oldServices, $newServices );
57 $theServices = MediaWikiServices
::getInstance();
58 $this->assertSame( $theServices, $newServices );
60 MediaWikiServices
::forceGlobalInstance( $oldServices );
62 $theServices = MediaWikiServices
::getInstance();
63 $this->assertSame( $theServices, $oldServices );
66 public function testResetGlobalInstance() {
67 $newServices = $this->newMediaWikiServices();
68 $oldServices = MediaWikiServices
::forceGlobalInstance( $newServices );
70 $service1 = $this->getMock( SalvageableService
::class );
71 $service1->expects( $this->never() )
72 ->method( 'salvage' );
74 $newServices->defineService(
76 function() use ( $service1 ) {
81 // force instantiation
82 $newServices->getService( 'Test' );
84 MediaWikiServices
::resetGlobalInstance( $this->newTestConfig() );
85 $theServices = MediaWikiServices
::getInstance();
89 $theServices->getService( 'Test' ),
90 'service definition should survive reset'
93 $this->assertNotSame( $theServices, $newServices );
94 $this->assertNotSame( $theServices, $oldServices );
96 MediaWikiServices
::forceGlobalInstance( $oldServices );
99 public function testResetGlobalInstance_quick() {
100 $newServices = $this->newMediaWikiServices();
101 $oldServices = MediaWikiServices
::forceGlobalInstance( $newServices );
103 $service1 = $this->getMock( SalvageableService
::class );
104 $service1->expects( $this->never() )
105 ->method( 'salvage' );
107 $service2 = $this->getMock( SalvageableService
::class );
108 $service2->expects( $this->once() )
109 ->method( 'salvage' )
112 // sequence of values the instantiator will return
113 $instantiatorReturnValues = [
118 $newServices->defineService(
120 function() use ( &$instantiatorReturnValues ) {
121 return array_shift( $instantiatorReturnValues );
125 // force instantiation
126 $newServices->getService( 'Test' );
128 MediaWikiServices
::resetGlobalInstance( $this->newTestConfig(), 'quick' );
129 $theServices = MediaWikiServices
::getInstance();
131 $this->assertSame( $service2, $theServices->getService( 'Test' ) );
133 $this->assertNotSame( $theServices, $newServices );
134 $this->assertNotSame( $theServices, $oldServices );
136 MediaWikiServices
::forceGlobalInstance( $oldServices );
139 public function testDisableStorageBackend() {
140 $newServices = $this->newMediaWikiServices();
141 $oldServices = MediaWikiServices
::forceGlobalInstance( $newServices );
143 $lbFactory = $this->getMockBuilder( 'LBFactorySimple' )
144 ->disableOriginalConstructor()
147 $lbFactory->expects( $this->once() )
148 ->method( 'destroy' );
150 $newServices->redefineService(
151 'DBLoadBalancerFactory',
152 function() use ( $lbFactory ) {
157 // force the service to become active, so we can check that it does get destroyed
158 $newServices->getService( 'DBLoadBalancerFactory' );
160 MediaWikiServices
::disableStorageBackend(); // should destroy DBLoadBalancerFactory
163 MediaWikiServices
::getInstance()->getService( 'DBLoadBalancerFactory' );
164 $this->fail( 'DBLoadBalancerFactory shoudl have been disabled' );
166 catch ( ServiceDisabledException
$ex ) {
169 catch ( Throwable
$ex ) {
170 $this->fail( 'ServiceDisabledException expected, caught ' . get_class( $ex ) );
173 MediaWikiServices
::forceGlobalInstance( $oldServices );
174 $newServices->destroy();
177 public function testResetChildProcessServices() {
178 $newServices = $this->newMediaWikiServices();
179 $oldServices = MediaWikiServices
::forceGlobalInstance( $newServices );
181 $service1 = $this->getMock( DestructibleService
::class );
182 $service1->expects( $this->once() )
183 ->method( 'destroy' );
185 $service2 = $this->getMock( DestructibleService
::class );
186 $service2->expects( $this->never() )
187 ->method( 'destroy' );
189 // sequence of values the instantiator will return
190 $instantiatorReturnValues = [
195 $newServices->defineService(
197 function() use ( &$instantiatorReturnValues ) {
198 return array_shift( $instantiatorReturnValues );
202 // force the service to become active, so we can check that it does get destroyed
203 $oldTestService = $newServices->getService( 'Test' );
205 MediaWikiServices
::resetChildProcessServices();
206 $finalServices = MediaWikiServices
::getInstance();
208 $newTestService = $finalServices->getService( 'Test' );
209 $this->assertNotSame( $oldTestService, $newTestService );
211 MediaWikiServices
::forceGlobalInstance( $oldServices );
214 public function testResetServiceForTesting() {
215 $services = $this->newMediaWikiServices();
218 $services->defineService(
220 function() use ( &$serviceCounter ) {
222 $service = $this->getMock( 'MediaWiki\Services\DestructibleService' );
223 $service->expects( $this->once() )->method( 'destroy' );
228 // This should do nothing. In particular, it should not create a service instance.
229 $services->resetServiceForTesting( 'Test' );
230 $this->assertEquals( 0, $serviceCounter, 'No service instance should be created yet.' );
232 $oldInstance = $services->getService( 'Test' );
233 $this->assertEquals( 1, $serviceCounter, 'A service instance should exit now.' );
235 // The old instance should be detached, and destroy() called.
236 $services->resetServiceForTesting( 'Test' );
237 $newInstance = $services->getService( 'Test' );
239 $this->assertNotSame( $oldInstance, $newInstance );
241 // Satisfy the expectation that destroy() is called also for the second service instance.
242 $newInstance->destroy();
245 public function testResetServiceForTesting_noDestroy() {
246 $services = $this->newMediaWikiServices();
248 $services->defineService(
251 $service = $this->getMock( 'MediaWiki\Services\DestructibleService' );
252 $service->expects( $this->never() )->method( 'destroy' );
257 $oldInstance = $services->getService( 'Test' );
259 // The old instance should be detached, but destroy() not called.
260 $services->resetServiceForTesting( 'Test', false );
261 $newInstance = $services->getService( 'Test' );
263 $this->assertNotSame( $oldInstance, $newInstance );
266 public function provideGetters() {
267 $getServiceCases = $this->provideGetService();
270 // All getters should be named just like the service, with "get" added.
271 foreach ( $getServiceCases as $name => $case ) {
272 if ( $name[0] === '_' ) {
273 // Internal service, no getter
276 list( $service, $class ) = $case;
277 $getterCases[$name] = [
287 * @dataProvider provideGetters
289 public function testGetters( $getter, $type ) {
290 // Test against the default instance, since the dummy will not know the default services.
291 $services = MediaWikiServices
::getInstance();
292 $service = $services->$getter();
293 $this->assertInstanceOf( $type, $service );
296 public function provideGetService() {
297 // NOTE: This should list all service getters defined in ServiceWiring.php.
298 // NOTE: For every test case defined here there should be a corresponding
299 // test case defined in provideGetters().
301 'BootstrapConfig' => [ 'BootstrapConfig', Config
::class ],
302 'ConfigFactory' => [ 'ConfigFactory', ConfigFactory
::class ],
303 'MainConfig' => [ 'MainConfig', Config
::class ],
304 'SiteStore' => [ 'SiteStore', SiteStore
::class ],
305 'SiteLookup' => [ 'SiteLookup', SiteLookup
::class ],
306 'StatsdDataFactory' => [ 'StatsdDataFactory', StatsdDataFactory
::class ],
307 'EventRelayerGroup' => [ 'EventRelayerGroup', EventRelayerGroup
::class ],
308 'SearchEngineFactory' => [ 'SearchEngineFactory', SearchEngineFactory
::class ],
309 'SearchEngineConfig' => [ 'SearchEngineConfig', SearchEngineConfig
::class ],
310 'SkinFactory' => [ 'SkinFactory', SkinFactory
::class ],
311 'DBLoadBalancerFactory' => [ 'DBLoadBalancerFactory', 'LBFactory' ],
312 'DBLoadBalancer' => [ 'DBLoadBalancer', 'LoadBalancer' ],
313 'WatchedItemStore' => [ 'WatchedItemStore', WatchedItemStore
::class ],
314 'GenderCache' => [ 'GenderCache', GenderCache
::class ],
315 '_MediaWikiTitleCodec' => [ '_MediaWikiTitleCodec', MediaWikiTitleCodec
::class ],
316 'TitleFormatter' => [ 'TitleFormatter', TitleFormatter
::class ],
317 'TitleParser' => [ 'TitleParser', TitleParser
::class ],
322 * @dataProvider provideGetService
324 public function testGetService( $name, $type ) {
325 // Test against the default instance, since the dummy will not know the default services.
326 $services = MediaWikiServices
::getInstance();
328 $service = $services->getService( $name );
329 $this->assertInstanceOf( $type, $service );
332 public function testDefaultServiceInstantiation() {
333 // Check all services in the default instance, not a dummy instance!
334 // Note that we instantiate all services here, including any that
335 // were registered by extensions.
336 $services = MediaWikiServices
::getInstance();
337 $names = $services->getServiceNames();
339 foreach ( $names as $name ) {
340 $this->assertTrue( $services->hasService( $name ) );
341 $service = $services->getService( $name );
342 $this->assertInternalType( 'object', $service );