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