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