RevisionTest: Make @covers tags absolute
[lhc/web/wiklou.git] / tests / phpunit / includes / MediaWikiServicesTest.php
1 <?php
2 use MediaWiki\Interwiki\InterwikiLookup;
3 use MediaWiki\Linker\LinkRenderer;
4 use MediaWiki\Linker\LinkRendererFactory;
5 use MediaWiki\MediaWikiServices;
6 use MediaWiki\Services\DestructibleService;
7 use MediaWiki\Services\SalvageableService;
8 use MediaWiki\Services\ServiceDisabledException;
9 use MediaWiki\Shell\CommandFactory;
10 use MediaWiki\Storage\BlobStore;
11 use MediaWiki\Storage\RevisionStore;
12
13 /**
14 * @covers MediaWiki\MediaWikiServices
15 *
16 * @group MediaWiki
17 */
18 class MediaWikiServicesTest extends MediaWikiTestCase {
19
20 /**
21 * @return Config
22 */
23 private function newTestConfig() {
24 $globalConfig = new GlobalVarConfig();
25
26 $testConfig = new HashConfig();
27 $testConfig->set( 'ServiceWiringFiles', $globalConfig->get( 'ServiceWiringFiles' ) );
28 $testConfig->set( 'ConfigRegistry', $globalConfig->get( 'ConfigRegistry' ) );
29
30 return $testConfig;
31 }
32
33 /**
34 * @return MediaWikiServices
35 */
36 private function newMediaWikiServices( Config $config = null ) {
37 if ( $config === null ) {
38 $config = $this->newTestConfig();
39 }
40
41 $instance = new MediaWikiServices( $config );
42
43 // Load the default wiring from the specified files.
44 $wiringFiles = $config->get( 'ServiceWiringFiles' );
45 $instance->loadWiringFiles( $wiringFiles );
46
47 return $instance;
48 }
49
50 public function testGetInstance() {
51 $services = MediaWikiServices::getInstance();
52 $this->assertInstanceOf( 'MediaWiki\\MediaWikiServices', $services );
53 }
54
55 public function testForceGlobalInstance() {
56 $newServices = $this->newMediaWikiServices();
57 $oldServices = MediaWikiServices::forceGlobalInstance( $newServices );
58
59 $this->assertInstanceOf( 'MediaWiki\\MediaWikiServices', $oldServices );
60 $this->assertNotSame( $oldServices, $newServices );
61
62 $theServices = MediaWikiServices::getInstance();
63 $this->assertSame( $theServices, $newServices );
64
65 MediaWikiServices::forceGlobalInstance( $oldServices );
66
67 $theServices = MediaWikiServices::getInstance();
68 $this->assertSame( $theServices, $oldServices );
69 }
70
71 public function testResetGlobalInstance() {
72 $newServices = $this->newMediaWikiServices();
73 $oldServices = MediaWikiServices::forceGlobalInstance( $newServices );
74
75 $service1 = $this->createMock( SalvageableService::class );
76 $service1->expects( $this->never() )
77 ->method( 'salvage' );
78
79 $newServices->defineService(
80 'Test',
81 function () use ( $service1 ) {
82 return $service1;
83 }
84 );
85
86 // force instantiation
87 $newServices->getService( 'Test' );
88
89 MediaWikiServices::resetGlobalInstance( $this->newTestConfig() );
90 $theServices = MediaWikiServices::getInstance();
91
92 $this->assertSame(
93 $service1,
94 $theServices->getService( 'Test' ),
95 'service definition should survive reset'
96 );
97
98 $this->assertNotSame( $theServices, $newServices );
99 $this->assertNotSame( $theServices, $oldServices );
100
101 MediaWikiServices::forceGlobalInstance( $oldServices );
102 }
103
104 public function testResetGlobalInstance_quick() {
105 $newServices = $this->newMediaWikiServices();
106 $oldServices = MediaWikiServices::forceGlobalInstance( $newServices );
107
108 $service1 = $this->createMock( SalvageableService::class );
109 $service1->expects( $this->never() )
110 ->method( 'salvage' );
111
112 $service2 = $this->createMock( SalvageableService::class );
113 $service2->expects( $this->once() )
114 ->method( 'salvage' )
115 ->with( $service1 );
116
117 // sequence of values the instantiator will return
118 $instantiatorReturnValues = [
119 $service1,
120 $service2,
121 ];
122
123 $newServices->defineService(
124 'Test',
125 function () use ( &$instantiatorReturnValues ) {
126 return array_shift( $instantiatorReturnValues );
127 }
128 );
129
130 // force instantiation
131 $newServices->getService( 'Test' );
132
133 MediaWikiServices::resetGlobalInstance( $this->newTestConfig(), 'quick' );
134 $theServices = MediaWikiServices::getInstance();
135
136 $this->assertSame( $service2, $theServices->getService( 'Test' ) );
137
138 $this->assertNotSame( $theServices, $newServices );
139 $this->assertNotSame( $theServices, $oldServices );
140
141 MediaWikiServices::forceGlobalInstance( $oldServices );
142 }
143
144 public function testDisableStorageBackend() {
145 $newServices = $this->newMediaWikiServices();
146 $oldServices = MediaWikiServices::forceGlobalInstance( $newServices );
147
148 $lbFactory = $this->getMockBuilder( 'LBFactorySimple' )
149 ->disableOriginalConstructor()
150 ->getMock();
151
152 $newServices->redefineService(
153 'DBLoadBalancerFactory',
154 function () use ( $lbFactory ) {
155 return $lbFactory;
156 }
157 );
158
159 // force the service to become active, so we can check that it does get destroyed
160 $newServices->getService( 'DBLoadBalancerFactory' );
161
162 MediaWikiServices::disableStorageBackend(); // should destroy DBLoadBalancerFactory
163
164 try {
165 MediaWikiServices::getInstance()->getService( 'DBLoadBalancerFactory' );
166 $this->fail( 'DBLoadBalancerFactory should have been disabled' );
167 }
168 catch ( ServiceDisabledException $ex ) {
169 // ok, as expected
170 } catch ( Throwable $ex ) {
171 $this->fail( 'ServiceDisabledException expected, caught ' . get_class( $ex ) );
172 }
173
174 MediaWikiServices::forceGlobalInstance( $oldServices );
175 $newServices->destroy();
176 }
177
178 public function testResetChildProcessServices() {
179 $newServices = $this->newMediaWikiServices();
180 $oldServices = MediaWikiServices::forceGlobalInstance( $newServices );
181
182 $service1 = $this->createMock( DestructibleService::class );
183 $service1->expects( $this->once() )
184 ->method( 'destroy' );
185
186 $service2 = $this->createMock( DestructibleService::class );
187 $service2->expects( $this->never() )
188 ->method( 'destroy' );
189
190 // sequence of values the instantiator will return
191 $instantiatorReturnValues = [
192 $service1,
193 $service2,
194 ];
195
196 $newServices->defineService(
197 'Test',
198 function () use ( &$instantiatorReturnValues ) {
199 return array_shift( $instantiatorReturnValues );
200 }
201 );
202
203 // force the service to become active, so we can check that it does get destroyed
204 $oldTestService = $newServices->getService( 'Test' );
205
206 MediaWikiServices::resetChildProcessServices();
207 $finalServices = MediaWikiServices::getInstance();
208
209 $newTestService = $finalServices->getService( 'Test' );
210 $this->assertNotSame( $oldTestService, $newTestService );
211
212 MediaWikiServices::forceGlobalInstance( $oldServices );
213 }
214
215 public function testResetServiceForTesting() {
216 $services = $this->newMediaWikiServices();
217 $serviceCounter = 0;
218
219 $services->defineService(
220 'Test',
221 function () use ( &$serviceCounter ) {
222 $serviceCounter++;
223 $service = $this->createMock( 'MediaWiki\Services\DestructibleService' );
224 $service->expects( $this->once() )->method( 'destroy' );
225 return $service;
226 }
227 );
228
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.' );
232
233 $oldInstance = $services->getService( 'Test' );
234 $this->assertEquals( 1, $serviceCounter, 'A service instance should exit now.' );
235
236 // The old instance should be detached, and destroy() called.
237 $services->resetServiceForTesting( 'Test' );
238 $newInstance = $services->getService( 'Test' );
239
240 $this->assertNotSame( $oldInstance, $newInstance );
241
242 // Satisfy the expectation that destroy() is called also for the second service instance.
243 $newInstance->destroy();
244 }
245
246 public function testResetServiceForTesting_noDestroy() {
247 $services = $this->newMediaWikiServices();
248
249 $services->defineService(
250 'Test',
251 function () {
252 $service = $this->createMock( 'MediaWiki\Services\DestructibleService' );
253 $service->expects( $this->never() )->method( 'destroy' );
254 return $service;
255 }
256 );
257
258 $oldInstance = $services->getService( 'Test' );
259
260 // The old instance should be detached, but destroy() not called.
261 $services->resetServiceForTesting( 'Test', false );
262 $newInstance = $services->getService( 'Test' );
263
264 $this->assertNotSame( $oldInstance, $newInstance );
265 }
266
267 public function provideGetters() {
268 $getServiceCases = $this->provideGetService();
269 $getterCases = [];
270
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
275 continue;
276 }
277 list( $service, $class ) = $case;
278 $getterCases[$name] = [
279 'get' . $service,
280 $class,
281 ];
282 }
283
284 return $getterCases;
285 }
286
287 /**
288 * @dataProvider provideGetters
289 */
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 );
295 }
296
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().
301 return [
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', IBufferingStatsdDataFactory::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', Wikimedia\Rdbms\LBFactory::class ],
314 'DBLoadBalancer' => [ 'DBLoadBalancer', 'LoadBalancer' ],
315 'WatchedItemStore' => [ 'WatchedItemStore', WatchedItemStore::class ],
316 'WatchedItemQueryService' => [ 'WatchedItemQueryService', WatchedItemQueryService::class ],
317 'CryptRand' => [ 'CryptRand', CryptRand::class ],
318 'CryptHKDF' => [ 'CryptHKDF', CryptHKDF::class ],
319 'MediaHandlerFactory' => [ 'MediaHandlerFactory', MediaHandlerFactory::class ],
320 'Parser' => [ 'Parser', Parser::class ],
321 'ParserCache' => [ 'ParserCache', ParserCache::class ],
322 'GenderCache' => [ 'GenderCache', GenderCache::class ],
323 'LinkCache' => [ 'LinkCache', LinkCache::class ],
324 'LinkRenderer' => [ 'LinkRenderer', LinkRenderer::class ],
325 'LinkRendererFactory' => [ 'LinkRendererFactory', LinkRendererFactory::class ],
326 '_MediaWikiTitleCodec' => [ '_MediaWikiTitleCodec', MediaWikiTitleCodec::class ],
327 'MimeAnalyzer' => [ 'MimeAnalyzer', MimeAnalyzer::class ],
328 'TitleFormatter' => [ 'TitleFormatter', TitleFormatter::class ],
329 'TitleParser' => [ 'TitleParser', TitleParser::class ],
330 'ProxyLookup' => [ 'ProxyLookup', ProxyLookup::class ],
331 'MainObjectStash' => [ 'MainObjectStash', BagOStuff::class ],
332 'MainWANObjectCache' => [ 'MainWANObjectCache', WANObjectCache::class ],
333 'LocalServerObjectCache' => [ 'LocalServerObjectCache', BagOStuff::class ],
334 'VirtualRESTServiceClient' => [ 'VirtualRESTServiceClient', VirtualRESTServiceClient::class ],
335 'ShellCommandFactory' => [ 'ShellCommandFactory', CommandFactory::class ],
336 'BlobStore' => [ 'BlobStore', BlobStore::class ],
337 'RevisionStore' => [ 'RevisionStore', RevisionStore::class ],
338 ];
339 }
340
341 /**
342 * @dataProvider provideGetService
343 */
344 public function testGetService( $name, $type ) {
345 // Test against the default instance, since the dummy will not know the default services.
346 $services = MediaWikiServices::getInstance();
347
348 $service = $services->getService( $name );
349 $this->assertInstanceOf( $type, $service );
350 }
351
352 public function testDefaultServiceInstantiation() {
353 // Check all services in the default instance, not a dummy instance!
354 // Note that we instantiate all services here, including any that
355 // were registered by extensions.
356 $services = MediaWikiServices::getInstance();
357 $names = $services->getServiceNames();
358
359 foreach ( $names as $name ) {
360 $this->assertTrue( $services->hasService( $name ) );
361 $service = $services->getService( $name );
362 $this->assertInternalType( 'object', $service );
363 }
364 }
365
366 }