Merge "Use STRAIGHT_JOIN on updateCollation.php per jcrespo"
[lhc/web/wiklou.git] / tests / phpunit / includes / MediaWikiServicesTest.php
1 <?php
2 use Liuggio\StatsdClient\Factory\StatsdDataFactory;
3 use MediaWiki\MediaWikiServices;
4 use MediaWiki\Services\ServiceDisabledException;
5
6 /**
7 * @covers MediaWiki\MediaWikiServices
8 *
9 * @group MediaWiki
10 */
11 class MediaWikiServicesTest extends PHPUnit_Framework_TestCase {
12
13 /**
14 * @return Config
15 */
16 private function newTestConfig() {
17 $globalConfig = new GlobalVarConfig();
18
19 $testConfig = new HashConfig();
20 $testConfig->set( 'ServiceWiringFiles', $globalConfig->get( 'ServiceWiringFiles' ) );
21 $testConfig->set( 'ConfigRegistry', $globalConfig->get( 'ConfigRegistry' ) );
22
23 return $testConfig;
24 }
25
26 /**
27 * @return MediaWikiServices
28 */
29 private function newMediaWikiServices( Config $config = null ) {
30 if ( $config === null ) {
31 $config = $this->newTestConfig();
32 }
33
34 $instance = new MediaWikiServices( $config );
35
36 // Load the default wiring from the specified files.
37 $wiringFiles = $config->get( 'ServiceWiringFiles' );
38 $instance->loadWiringFiles( $wiringFiles );
39
40 return $instance;
41 }
42
43 public function testGetInstance() {
44 $services = MediaWikiServices::getInstance();
45 $this->assertInstanceOf( 'MediaWiki\\MediaWikiServices', $services );
46 }
47
48 public function testForceGlobalInstance() {
49 $newServices = $this->newMediaWikiServices();
50 $oldServices = MediaWikiServices::forceGlobalInstance( $newServices );
51
52 $this->assertInstanceOf( 'MediaWiki\\MediaWikiServices', $oldServices );
53 $this->assertNotSame( $oldServices, $newServices );
54
55 $theServices = MediaWikiServices::getInstance();
56 $this->assertSame( $theServices, $newServices );
57
58 MediaWikiServices::forceGlobalInstance( $oldServices );
59
60 $theServices = MediaWikiServices::getInstance();
61 $this->assertSame( $theServices, $oldServices );
62 }
63
64 public function testResetGlobalInstance() {
65 $newServices = $this->newMediaWikiServices();
66 $oldServices = MediaWikiServices::forceGlobalInstance( $newServices );
67
68 MediaWikiServices::resetGlobalInstance( $this->newTestConfig() );
69 $theServices = MediaWikiServices::getInstance();
70
71 $this->assertNotSame( $theServices, $newServices );
72 $this->assertNotSame( $theServices, $oldServices );
73
74 MediaWikiServices::forceGlobalInstance( $oldServices );
75 }
76
77 public function testDisableStorageBackend() {
78 $newServices = $this->newMediaWikiServices();
79 $oldServices = MediaWikiServices::forceGlobalInstance( $newServices );
80
81 $lbFactory = $this->getMockBuilder( 'LBFactorySimple' )
82 ->disableOriginalConstructor()
83 ->getMock();
84
85 $lbFactory->expects( $this->once() )
86 ->method( 'destroy' );
87
88 $newServices->redefineService(
89 'DBLoadBalancerFactory',
90 function() use ( $lbFactory ) {
91 return $lbFactory;
92 }
93 );
94
95 // force the service to become active, so we can check that it does get destroyed
96 $newServices->getService( 'DBLoadBalancerFactory' );
97
98 MediaWikiServices::disableStorageBackend(); // should destroy DBLoadBalancerFactory
99
100 try {
101 MediaWikiServices::getInstance()->getService( 'DBLoadBalancerFactory' );
102 $this->fail( 'DBLoadBalancerFactory shoudl have been disabled' );
103 }
104 catch ( ServiceDisabledException $ex ) {
105 // ok, as expected
106 }
107 catch ( Throwable $ex ) {
108 $this->fail( 'ServiceDisabledException expected, caught ' . get_class( $ex ) );
109 }
110
111 MediaWikiServices::forceGlobalInstance( $oldServices );
112 }
113
114 public function testResetChildProcessServices() {
115 $newServices = $this->newMediaWikiServices();
116 $oldServices = MediaWikiServices::forceGlobalInstance( $newServices );
117
118 $lbFactory = $this->getMockBuilder( 'LBFactorySimple' )
119 ->disableOriginalConstructor()
120 ->getMock();
121
122 $lbFactory->expects( $this->once() )
123 ->method( 'destroy' );
124
125 $newServices->redefineService(
126 'DBLoadBalancerFactory',
127 function() use ( $lbFactory ) {
128 return $lbFactory;
129 }
130 );
131
132 // force the service to become active, so we can check that it does get destroyed
133 $oldLBFactory = $newServices->getService( 'DBLoadBalancerFactory' );
134
135 MediaWikiServices::resetChildProcessServices();
136 $finalServices = MediaWikiServices::getInstance();
137
138 $newLBFactory = $finalServices->getService( 'DBLoadBalancerFactory' );
139
140 $this->assertNotSame( $oldLBFactory, $newLBFactory );
141
142 MediaWikiServices::forceGlobalInstance( $oldServices );
143 }
144
145 public function testResetServiceForTesting() {
146 $services = $this->newMediaWikiServices();
147 $serviceCounter = 0;
148
149 $services->defineService(
150 'Test',
151 function() use ( &$serviceCounter ) {
152 $serviceCounter++;
153 $service = $this->getMock( 'MediaWiki\Services\DestructibleService' );
154 $service->expects( $this->once() )->method( 'destroy' );
155 return $service;
156 }
157 );
158
159 // This should do nothing. In particular, it should not create a service instance.
160 $services->resetServiceForTesting( 'Test' );
161 $this->assertEquals( 0, $serviceCounter, 'No service instance should be created yet.' );
162
163 $oldInstance = $services->getService( 'Test' );
164 $this->assertEquals( 1, $serviceCounter, 'A service instance should exit now.' );
165
166 // The old instance should be detached, and destroy() called.
167 $services->resetServiceForTesting( 'Test' );
168 $newInstance = $services->getService( 'Test' );
169
170 $this->assertNotSame( $oldInstance, $newInstance );
171
172 // Satisfy the expectation that destroy() is called also for the second service instance.
173 $newInstance->destroy();
174 }
175
176 public function testResetServiceForTesting_noDestroy() {
177 $services = $this->newMediaWikiServices();
178
179 $services->defineService(
180 'Test',
181 function() {
182 $service = $this->getMock( 'MediaWiki\Services\DestructibleService' );
183 $service->expects( $this->never() )->method( 'destroy' );
184 return $service;
185 }
186 );
187
188 $oldInstance = $services->getService( 'Test' );
189
190 // The old instance should be detached, but destroy() not called.
191 $services->resetServiceForTesting( 'Test', false );
192 $newInstance = $services->getService( 'Test' );
193
194 $this->assertNotSame( $oldInstance, $newInstance );
195 }
196
197 public function provideGetters() {
198 $getServiceCases = $this->provideGetService();
199 $getterCases = [];
200
201 // All getters should be named just like the service, with "get" added.
202 foreach ( $getServiceCases as $name => $case ) {
203 list( $service, $class ) = $case;
204 $getterCases[$name] = [
205 'get' . $service,
206 $class,
207 ];
208 }
209
210 return $getterCases;
211 }
212
213 /**
214 * @dataProvider provideGetters
215 */
216 public function testGetters( $getter, $type ) {
217 // Test against the default instance, since the dummy will not know the default services.
218 $services = MediaWikiServices::getInstance();
219 $service = $services->$getter();
220 $this->assertInstanceOf( $type, $service );
221 }
222
223 public function provideGetService() {
224 // NOTE: This should list all service getters defined in ServiceWiring.php.
225 // NOTE: For every test case defined here there should be a corresponding
226 // test case defined in provideGetters().
227 return [
228 'BootstrapConfig' => [ 'BootstrapConfig', Config::class ],
229 'ConfigFactory' => [ 'ConfigFactory', ConfigFactory::class ],
230 'MainConfig' => [ 'MainConfig', Config::class ],
231 'SiteStore' => [ 'SiteStore', SiteStore::class ],
232 'SiteLookup' => [ 'SiteLookup', SiteLookup::class ],
233 'StatsdDataFactory' => [ 'StatsdDataFactory', StatsdDataFactory::class ],
234 'EventRelayerGroup' => [ 'EventRelayerGroup', EventRelayerGroup::class ],
235 'SearchEngineFactory' => [ 'SearchEngineFactory', SearchEngineFactory::class ],
236 'SearchEngineConfig' => [ 'SearchEngineConfig', SearchEngineConfig::class ],
237 'SkinFactory' => [ 'SkinFactory', SkinFactory::class ],
238 'DBLoadBalancerFactory' => [ 'DBLoadBalancerFactory', 'LBFactory' ],
239 'DBLoadBalancer' => [ 'DBLoadBalancer', 'LoadBalancer' ],
240 'WatchedItemStore' => [ 'WatchedItemStore', WatchedItemStore::class ],
241 'GenderCache' => [ 'GenderCache', GenderCache::class ],
242 ];
243 }
244
245 /**
246 * @dataProvider provideGetService
247 */
248 public function testGetService( $name, $type ) {
249 // Test against the default instance, since the dummy will not know the default services.
250 $services = MediaWikiServices::getInstance();
251
252 $service = $services->getService( $name );
253 $this->assertInstanceOf( $type, $service );
254 }
255
256 public function testDefaultServiceInstantiation() {
257 // Check all services in the default instance, not a dummy instance!
258 // Note that we instantiate all services here, including any that
259 // were registered by extensions.
260 $services = MediaWikiServices::getInstance();
261 $names = $services->getServiceNames();
262
263 foreach ( $names as $name ) {
264 $this->assertTrue( $services->hasService( $name ) );
265 $service = $services->getService( $name );
266 $this->assertInternalType( 'object', $service );
267 }
268 }
269
270 }