]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - tests/phpunit/includes/MediaWikiServicesTest.php
MediaWiki 1.30.2-scripts
[autoinstallsdev/mediawiki.git] / tests / phpunit / includes / MediaWikiServicesTest.php
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;
11 use MediaWiki\Shell\CommandFactory;
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                 ];
337         }
338
339         /**
340          * @dataProvider provideGetService
341          */
342         public function testGetService( $name, $type ) {
343                 // Test against the default instance, since the dummy will not know the default services.
344                 $services = MediaWikiServices::getInstance();
345
346                 $service = $services->getService( $name );
347                 $this->assertInstanceOf( $type, $service );
348         }
349
350         public function testDefaultServiceInstantiation() {
351                 // Check all services in the default instance, not a dummy instance!
352                 // Note that we instantiate all services here, including any that
353                 // were registered by extensions.
354                 $services = MediaWikiServices::getInstance();
355                 $names = $services->getServiceNames();
356
357                 foreach ( $names as $name ) {
358                         $this->assertTrue( $services->hasService( $name ) );
359                         $service = $services->getService( $name );
360                         $this->assertInternalType( 'object', $service );
361                 }
362         }
363
364 }