rdbms: Rename "memCache" to "memStash" in LBFactory
[mediawiki.git] / tests / phpunit / includes / MediaWikiServicesTest.php
blob9eae64348b510ef1530bdb2e87250daec78c92b9
1 <?php
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;
10 use Wikimedia\Rdbms\LBFactory;
12 /**
13 * @covers MediaWiki\MediaWikiServices
15 * @group MediaWiki
17 class MediaWikiServicesTest extends MediaWikiTestCase {
19 /**
20 * @return Config
22 private function newTestConfig() {
23 $globalConfig = new GlobalVarConfig();
25 $testConfig = new HashConfig();
26 $testConfig->set( 'ServiceWiringFiles', $globalConfig->get( 'ServiceWiringFiles' ) );
27 $testConfig->set( 'ConfigRegistry', $globalConfig->get( 'ConfigRegistry' ) );
29 return $testConfig;
32 /**
33 * @return MediaWikiServices
35 private function newMediaWikiServices( Config $config = null ) {
36 if ( $config === null ) {
37 $config = $this->newTestConfig();
40 $instance = new MediaWikiServices( $config );
42 // Load the default wiring from the specified files.
43 $wiringFiles = $config->get( 'ServiceWiringFiles' );
44 $instance->loadWiringFiles( $wiringFiles );
46 return $instance;
49 public function testGetInstance() {
50 $services = MediaWikiServices::getInstance();
51 $this->assertInstanceOf( 'MediaWiki\\MediaWikiServices', $services );
54 public function testForceGlobalInstance() {
55 $newServices = $this->newMediaWikiServices();
56 $oldServices = MediaWikiServices::forceGlobalInstance( $newServices );
58 $this->assertInstanceOf( 'MediaWiki\\MediaWikiServices', $oldServices );
59 $this->assertNotSame( $oldServices, $newServices );
61 $theServices = MediaWikiServices::getInstance();
62 $this->assertSame( $theServices, $newServices );
64 MediaWikiServices::forceGlobalInstance( $oldServices );
66 $theServices = MediaWikiServices::getInstance();
67 $this->assertSame( $theServices, $oldServices );
70 public function testResetGlobalInstance() {
71 $newServices = $this->newMediaWikiServices();
72 $oldServices = MediaWikiServices::forceGlobalInstance( $newServices );
74 $service1 = $this->createMock( SalvageableService::class );
75 $service1->expects( $this->never() )
76 ->method( 'salvage' );
78 $newServices->defineService(
79 'Test',
80 function () use ( $service1 ) {
81 return $service1;
85 // force instantiation
86 $newServices->getService( 'Test' );
88 MediaWikiServices::resetGlobalInstance( $this->newTestConfig() );
89 $theServices = MediaWikiServices::getInstance();
91 $this->assertSame(
92 $service1,
93 $theServices->getService( 'Test' ),
94 'service definition should survive reset'
97 $this->assertNotSame( $theServices, $newServices );
98 $this->assertNotSame( $theServices, $oldServices );
100 MediaWikiServices::forceGlobalInstance( $oldServices );
103 public function testResetGlobalInstance_quick() {
104 $newServices = $this->newMediaWikiServices();
105 $oldServices = MediaWikiServices::forceGlobalInstance( $newServices );
107 $service1 = $this->createMock( SalvageableService::class );
108 $service1->expects( $this->never() )
109 ->method( 'salvage' );
111 $service2 = $this->createMock( SalvageableService::class );
112 $service2->expects( $this->once() )
113 ->method( 'salvage' )
114 ->with( $service1 );
116 // sequence of values the instantiator will return
117 $instantiatorReturnValues = [
118 $service1,
119 $service2,
122 $newServices->defineService(
123 'Test',
124 function () use ( &$instantiatorReturnValues ) {
125 return array_shift( $instantiatorReturnValues );
129 // force instantiation
130 $newServices->getService( 'Test' );
132 MediaWikiServices::resetGlobalInstance( $this->newTestConfig(), 'quick' );
133 $theServices = MediaWikiServices::getInstance();
135 $this->assertSame( $service2, $theServices->getService( 'Test' ) );
137 $this->assertNotSame( $theServices, $newServices );
138 $this->assertNotSame( $theServices, $oldServices );
140 MediaWikiServices::forceGlobalInstance( $oldServices );
143 public function testDisableStorageBackend() {
144 $newServices = $this->newMediaWikiServices();
145 $oldServices = MediaWikiServices::forceGlobalInstance( $newServices );
147 $lbFactory = $this->getMockBuilder( 'LBFactorySimple' )
148 ->disableOriginalConstructor()
149 ->getMock();
151 $newServices->redefineService(
152 'DBLoadBalancerFactory',
153 function () use ( $lbFactory ) {
154 return $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
163 try {
164 MediaWikiServices::getInstance()->getService( 'DBLoadBalancerFactory' );
165 $this->fail( 'DBLoadBalancerFactory should have been disabled' );
167 catch ( ServiceDisabledException $ex ) {
168 // ok, as expected
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->createMock( DestructibleService::class );
182 $service1->expects( $this->once() )
183 ->method( 'destroy' );
185 $service2 = $this->createMock( DestructibleService::class );
186 $service2->expects( $this->never() )
187 ->method( 'destroy' );
189 // sequence of values the instantiator will return
190 $instantiatorReturnValues = [
191 $service1,
192 $service2,
195 $newServices->defineService(
196 'Test',
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();
216 $serviceCounter = 0;
218 $services->defineService(
219 'Test',
220 function () use ( &$serviceCounter ) {
221 $serviceCounter++;
222 $service = $this->createMock( 'MediaWiki\Services\DestructibleService' );
223 $service->expects( $this->once() )->method( 'destroy' );
224 return $service;
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(
249 'Test',
250 function () {
251 $service = $this->createMock( 'MediaWiki\Services\DestructibleService' );
252 $service->expects( $this->never() )->method( 'destroy' );
253 return $service;
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();
268 $getterCases = [];
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
274 continue;
276 list( $service, $class ) = $case;
277 $getterCases[$name] = [
278 'get' . $service,
279 $class,
283 return $getterCases;
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().
300 return [
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', IBufferingStatsdDataFactory::class ],
307 'InterwikiLookup' => [ 'InterwikiLookup', InterwikiLookup::class ],
308 'EventRelayerGroup' => [ 'EventRelayerGroup', EventRelayerGroup::class ],
309 'SearchEngineFactory' => [ 'SearchEngineFactory', SearchEngineFactory::class ],
310 'SearchEngineConfig' => [ 'SearchEngineConfig', SearchEngineConfig::class ],
311 'SkinFactory' => [ 'SkinFactory', SkinFactory::class ],
312 'DBLoadBalancerFactory' => [ 'DBLoadBalancerFactory', Wikimedia\Rdbms\LBFactory::class ],
313 'DBLoadBalancer' => [ 'DBLoadBalancer', 'LoadBalancer' ],
314 'WatchedItemStore' => [ 'WatchedItemStore', WatchedItemStore::class ],
315 'WatchedItemQueryService' => [ 'WatchedItemQueryService', WatchedItemQueryService::class ],
316 'CryptRand' => [ 'CryptRand', CryptRand::class ],
317 'CryptHKDF' => [ 'CryptHKDF', CryptHKDF::class ],
318 'MediaHandlerFactory' => [ 'MediaHandlerFactory', MediaHandlerFactory::class ],
319 'Parser' => [ 'Parser', Parser::class ],
320 'ParserCache' => [ 'ParserCache', ParserCache::class ],
321 'GenderCache' => [ 'GenderCache', GenderCache::class ],
322 'LinkCache' => [ 'LinkCache', LinkCache::class ],
323 'LinkRenderer' => [ 'LinkRenderer', LinkRenderer::class ],
324 'LinkRendererFactory' => [ 'LinkRendererFactory', LinkRendererFactory::class ],
325 '_MediaWikiTitleCodec' => [ '_MediaWikiTitleCodec', MediaWikiTitleCodec::class ],
326 'MimeAnalyzer' => [ 'MimeAnalyzer', MimeAnalyzer::class ],
327 'TitleFormatter' => [ 'TitleFormatter', TitleFormatter::class ],
328 'TitleParser' => [ 'TitleParser', TitleParser::class ],
329 'ProxyLookup' => [ 'ProxyLookup', ProxyLookup::class ],
330 'MainObjectStash' => [ 'MainObjectStash', BagOStuff::class ],
331 'MainWANObjectCache' => [ 'MainWANObjectCache', WANObjectCache::class ],
332 'LocalServerObjectCache' => [ 'LocalServerObjectCache', BagOStuff::class ],
333 'VirtualRESTServiceClient' => [ 'VirtualRESTServiceClient', VirtualRESTServiceClient::class ]
338 * @dataProvider provideGetService
340 public function testGetService( $name, $type ) {
341 // Test against the default instance, since the dummy will not know the default services.
342 $services = MediaWikiServices::getInstance();
344 $service = $services->getService( $name );
345 $this->assertInstanceOf( $type, $service );
348 public function testDefaultServiceInstantiation() {
349 // Check all services in the default instance, not a dummy instance!
350 // Note that we instantiate all services here, including any that
351 // were registered by extensions.
352 $services = MediaWikiServices::getInstance();
353 $names = $services->getServiceNames();
355 foreach ( $names as $name ) {
356 $this->assertTrue( $services->hasService( $name ) );
357 $service = $services->getService( $name );
358 $this->assertInternalType( 'object', $service );