2 use MediaWiki\Services\ServiceContainer
;
5 * @covers MediaWiki\Services\ServiceContainer
9 class ServiceContainerTest
extends PHPUnit_Framework_TestCase
{
11 use MediaWikiCoversValidator
;
13 private function newServiceContainer( $extraArgs = [] ) {
14 return new ServiceContainer( $extraArgs );
17 public function testGetServiceNames() {
18 $services = $this->newServiceContainer();
19 $names = $services->getServiceNames();
21 $this->assertInternalType( 'array', $names );
22 $this->assertEmpty( $names );
24 $name = 'TestService92834576';
25 $services->defineService( $name, function () {
29 $names = $services->getServiceNames();
30 $this->assertContains( $name, $names );
33 public function testHasService() {
34 $services = $this->newServiceContainer();
36 $name = 'TestService92834576';
37 $this->assertFalse( $services->hasService( $name ) );
39 $services->defineService( $name, function () {
43 $this->assertTrue( $services->hasService( $name ) );
46 public function testGetService() {
47 $services = $this->newServiceContainer( [ 'Foo' ] );
49 $theService = new stdClass();
50 $name = 'TestService92834576';
53 $services->defineService(
55 function ( $actualLocator, $extra ) use ( $services, $theService, &$count ) {
57 PHPUnit_Framework_Assert
::assertSame( $services, $actualLocator );
58 PHPUnit_Framework_Assert
::assertSame( $extra, 'Foo' );
63 $this->assertSame( $theService, $services->getService( $name ) );
65 $services->getService( $name );
66 $this->assertSame( 1, $count, 'instantiator should be called exactly once!' );
69 public function testGetService_fail_unknown() {
70 $services = $this->newServiceContainer();
72 $name = 'TestService92834576';
74 $this->setExpectedException( 'MediaWiki\Services\NoSuchServiceException' );
76 $services->getService( $name );
79 public function testPeekService() {
80 $services = $this->newServiceContainer();
82 $services->defineService(
85 return new stdClass();
89 $services->defineService(
92 return new stdClass();
96 // trigger instantiation of Foo
97 $services->getService( 'Foo' );
99 $this->assertInternalType(
101 $services->peekService( 'Foo' ),
102 'Peek should return the service object if it had been accessed before.'
106 $services->peekService( 'Bar' ),
107 'Peek should return null if the service was never accessed.'
111 public function testPeekService_fail_unknown() {
112 $services = $this->newServiceContainer();
114 $name = 'TestService92834576';
116 $this->setExpectedException( 'MediaWiki\Services\NoSuchServiceException' );
118 $services->peekService( $name );
121 public function testDefineService() {
122 $services = $this->newServiceContainer();
124 $theService = new stdClass();
125 $name = 'TestService92834576';
127 $services->defineService( $name, function ( $actualLocator ) use ( $services, $theService ) {
128 PHPUnit_Framework_Assert
::assertSame( $services, $actualLocator );
132 $this->assertTrue( $services->hasService( $name ) );
133 $this->assertSame( $theService, $services->getService( $name ) );
136 public function testDefineService_fail_duplicate() {
137 $services = $this->newServiceContainer();
139 $theService = new stdClass();
140 $name = 'TestService92834576';
142 $services->defineService( $name, function () use ( $theService ) {
146 $this->setExpectedException( 'MediaWiki\Services\ServiceAlreadyDefinedException' );
148 $services->defineService( $name, function () use ( $theService ) {
153 public function testApplyWiring() {
154 $services = $this->newServiceContainer();
157 'Foo' => function () {
160 'Bar' => function () {
165 $services->applyWiring( $wiring );
167 $this->assertSame( 'Foo!', $services->getService( 'Foo' ) );
168 $this->assertSame( 'Bar!', $services->getService( 'Bar' ) );
171 public function testImportWiring() {
172 $services = $this->newServiceContainer();
175 'Foo' => function () {
178 'Bar' => function () {
181 'Car' => function () {
186 $services->applyWiring( $wiring );
188 $newServices = $this->newServiceContainer();
190 // define a service before importing, so we can later check that
191 // existing service instances survive importWiring()
192 $newServices->defineService( 'Car', function () {
196 // force instantiation
197 $newServices->getService( 'Car' );
199 // Define another service, so we can later check that extra wiring
201 $newServices->defineService( 'Xar', function () {
205 // import wiring, but skip `Bar`
206 $newServices->importWiring( $services, [ 'Bar' ] );
208 $this->assertNotContains( 'Bar', $newServices->getServiceNames(), 'Skip `Bar` service' );
209 $this->assertSame( 'Foo!', $newServices->getService( 'Foo' ) );
211 // import all wiring, but preserve existing service instance
212 $newServices->importWiring( $services );
214 $this->assertContains( 'Bar', $newServices->getServiceNames(), 'Import all services' );
215 $this->assertSame( 'Bar!', $newServices->getService( 'Bar' ) );
216 $this->assertSame( 'Car!', $newServices->getService( 'Car' ), 'Use existing service instance' );
217 $this->assertSame( 'Xar!', $newServices->getService( 'Xar' ), 'Predefined services are kept' );
220 public function testLoadWiringFiles() {
221 $services = $this->newServiceContainer();
224 __DIR__
. '/TestWiring1.php',
225 __DIR__
. '/TestWiring2.php',
228 $services->loadWiringFiles( $wiringFiles );
230 $this->assertSame( 'Foo!', $services->getService( 'Foo' ) );
231 $this->assertSame( 'Bar!', $services->getService( 'Bar' ) );
234 public function testLoadWiringFiles_fail_duplicate() {
235 $services = $this->newServiceContainer();
238 __DIR__
. '/TestWiring1.php',
239 __DIR__
. '/./TestWiring1.php',
242 // loading the same file twice should fail, because
243 $this->setExpectedException( 'MediaWiki\Services\ServiceAlreadyDefinedException' );
245 $services->loadWiringFiles( $wiringFiles );
248 public function testRedefineService() {
249 $services = $this->newServiceContainer( [ 'Foo' ] );
251 $theService1 = new stdClass();
252 $name = 'TestService92834576';
254 $services->defineService( $name, function () {
255 PHPUnit_Framework_Assert
::fail(
256 'The original instantiator function should not get called'
260 // redefine before instantiation
261 $services->redefineService(
263 function ( $actualLocator, $extra ) use ( $services, $theService1 ) {
264 PHPUnit_Framework_Assert
::assertSame( $services, $actualLocator );
265 PHPUnit_Framework_Assert
::assertSame( 'Foo', $extra );
270 // force instantiation, check result
271 $this->assertSame( $theService1, $services->getService( $name ) );
274 public function testRedefineService_disabled() {
275 $services = $this->newServiceContainer( [ 'Foo' ] );
277 $theService1 = new stdClass();
278 $name = 'TestService92834576';
280 $services->defineService( $name, function () {
284 // disable the service. we should be able to redefine it anyway.
285 $services->disableService( $name );
287 $services->redefineService( $name, function () use ( $theService1 ) {
291 // force instantiation, check result
292 $this->assertSame( $theService1, $services->getService( $name ) );
295 public function testRedefineService_fail_undefined() {
296 $services = $this->newServiceContainer();
298 $theService = new stdClass();
299 $name = 'TestService92834576';
301 $this->setExpectedException( 'MediaWiki\Services\NoSuchServiceException' );
303 $services->redefineService( $name, function () use ( $theService ) {
308 public function testRedefineService_fail_in_use() {
309 $services = $this->newServiceContainer( [ 'Foo' ] );
311 $theService = new stdClass();
312 $name = 'TestService92834576';
314 $services->defineService( $name, function () {
318 // create the service, so it can no longer be redefined
319 $services->getService( $name );
321 $this->setExpectedException( 'MediaWiki\Services\CannotReplaceActiveServiceException' );
323 $services->redefineService( $name, function () use ( $theService ) {
328 public function testDisableService() {
329 $services = $this->newServiceContainer( [ 'Foo' ] );
331 $destructible = $this->getMockBuilder( 'MediaWiki\Services\DestructibleService' )
333 $destructible->expects( $this->once() )
334 ->method( 'destroy' );
336 $services->defineService( 'Foo', function () use ( $destructible ) {
337 return $destructible;
339 $services->defineService( 'Bar', function () {
340 return new stdClass();
342 $services->defineService( 'Qux', function () {
343 return new stdClass();
346 // instantiate Foo and Bar services
347 $services->getService( 'Foo' );
348 $services->getService( 'Bar' );
350 // disable service, should call destroy() once.
351 $services->disableService( 'Foo' );
353 // disabled service should still be listed
354 $this->assertContains( 'Foo', $services->getServiceNames() );
356 // getting other services should still work
357 $services->getService( 'Bar' );
359 // disable non-destructible service, and not-yet-instantiated service
360 $services->disableService( 'Bar' );
361 $services->disableService( 'Qux' );
363 $this->assertNull( $services->peekService( 'Bar' ) );
364 $this->assertNull( $services->peekService( 'Qux' ) );
366 // disabled service should still be listed
367 $this->assertContains( 'Bar', $services->getServiceNames() );
368 $this->assertContains( 'Qux', $services->getServiceNames() );
370 $this->setExpectedException( 'MediaWiki\Services\ServiceDisabledException' );
371 $services->getService( 'Qux' );
374 public function testDisableService_fail_undefined() {
375 $services = $this->newServiceContainer();
377 $theService = new stdClass();
378 $name = 'TestService92834576';
380 $this->setExpectedException( 'MediaWiki\Services\NoSuchServiceException' );
382 $services->redefineService( $name, function () use ( $theService ) {
387 public function testDestroy() {
388 $services = $this->newServiceContainer();
390 $destructible = $this->getMockBuilder( 'MediaWiki\Services\DestructibleService' )
392 $destructible->expects( $this->once() )
393 ->method( 'destroy' );
395 $services->defineService( 'Foo', function () use ( $destructible ) {
396 return $destructible;
399 $services->defineService( 'Bar', function () {
400 return new stdClass();
403 // create the service
404 $services->getService( 'Foo' );
406 // destroy the container
407 $services->destroy();
409 $this->setExpectedException( 'MediaWiki\Services\ContainerDisabledException' );
410 $services->getService( 'Bar' );