2 use MediaWiki\Services\ServiceContainer
;
5 * @covers MediaWiki\Services\ServiceContainer
9 class ServiceContainerTest
extends PHPUnit\Framework\TestCase
{
11 use MediaWikiCoversValidator
;
12 use PHPUnit4And6Compat
;
14 private function newServiceContainer( $extraArgs = [] ) {
15 return new ServiceContainer( $extraArgs );
18 public function testGetServiceNames() {
19 $services = $this->newServiceContainer();
20 $names = $services->getServiceNames();
22 $this->assertInternalType( 'array', $names );
23 $this->assertEmpty( $names );
25 $name = 'TestService92834576';
26 $services->defineService( $name, function () {
30 $names = $services->getServiceNames();
31 $this->assertContains( $name, $names );
34 public function testHasService() {
35 $services = $this->newServiceContainer();
37 $name = 'TestService92834576';
38 $this->assertFalse( $services->hasService( $name ) );
40 $services->defineService( $name, function () {
44 $this->assertTrue( $services->hasService( $name ) );
47 public function testGetService() {
48 $services = $this->newServiceContainer( [ 'Foo' ] );
50 $theService = new stdClass();
51 $name = 'TestService92834576';
54 $services->defineService(
56 function ( $actualLocator, $extra ) use ( $services, $theService, &$count ) {
58 PHPUnit_Framework_Assert
::assertSame( $services, $actualLocator );
59 PHPUnit_Framework_Assert
::assertSame( $extra, 'Foo' );
64 $this->assertSame( $theService, $services->getService( $name ) );
66 $services->getService( $name );
67 $this->assertSame( 1, $count, 'instantiator should be called exactly once!' );
70 public function testGetService_fail_unknown() {
71 $services = $this->newServiceContainer();
73 $name = 'TestService92834576';
75 $this->setExpectedException( MediaWiki\Services\NoSuchServiceException
::class );
77 $services->getService( $name );
80 public function testPeekService() {
81 $services = $this->newServiceContainer();
83 $services->defineService(
86 return new stdClass();
90 $services->defineService(
93 return new stdClass();
97 // trigger instantiation of Foo
98 $services->getService( 'Foo' );
100 $this->assertInternalType(
102 $services->peekService( 'Foo' ),
103 'Peek should return the service object if it had been accessed before.'
107 $services->peekService( 'Bar' ),
108 'Peek should return null if the service was never accessed.'
112 public function testPeekService_fail_unknown() {
113 $services = $this->newServiceContainer();
115 $name = 'TestService92834576';
117 $this->setExpectedException( MediaWiki\Services\NoSuchServiceException
::class );
119 $services->peekService( $name );
122 public function testDefineService() {
123 $services = $this->newServiceContainer();
125 $theService = new stdClass();
126 $name = 'TestService92834576';
128 $services->defineService( $name, function ( $actualLocator ) use ( $services, $theService ) {
129 PHPUnit_Framework_Assert
::assertSame( $services, $actualLocator );
133 $this->assertTrue( $services->hasService( $name ) );
134 $this->assertSame( $theService, $services->getService( $name ) );
137 public function testDefineService_fail_duplicate() {
138 $services = $this->newServiceContainer();
140 $theService = new stdClass();
141 $name = 'TestService92834576';
143 $services->defineService( $name, function () use ( $theService ) {
147 $this->setExpectedException( MediaWiki\Services\ServiceAlreadyDefinedException
::class );
149 $services->defineService( $name, function () use ( $theService ) {
154 public function testApplyWiring() {
155 $services = $this->newServiceContainer();
158 'Foo' => function () {
161 'Bar' => function () {
166 $services->applyWiring( $wiring );
168 $this->assertSame( 'Foo!', $services->getService( 'Foo' ) );
169 $this->assertSame( 'Bar!', $services->getService( 'Bar' ) );
172 public function testImportWiring() {
173 $services = $this->newServiceContainer();
176 'Foo' => function () {
179 'Bar' => function () {
182 'Car' => function () {
187 $services->applyWiring( $wiring );
189 $newServices = $this->newServiceContainer();
191 // define a service before importing, so we can later check that
192 // existing service instances survive importWiring()
193 $newServices->defineService( 'Car', function () {
197 // force instantiation
198 $newServices->getService( 'Car' );
200 // Define another service, so we can later check that extra wiring
202 $newServices->defineService( 'Xar', function () {
206 // import wiring, but skip `Bar`
207 $newServices->importWiring( $services, [ 'Bar' ] );
209 $this->assertNotContains( 'Bar', $newServices->getServiceNames(), 'Skip `Bar` service' );
210 $this->assertSame( 'Foo!', $newServices->getService( 'Foo' ) );
212 // import all wiring, but preserve existing service instance
213 $newServices->importWiring( $services );
215 $this->assertContains( 'Bar', $newServices->getServiceNames(), 'Import all services' );
216 $this->assertSame( 'Bar!', $newServices->getService( 'Bar' ) );
217 $this->assertSame( 'Car!', $newServices->getService( 'Car' ), 'Use existing service instance' );
218 $this->assertSame( 'Xar!', $newServices->getService( 'Xar' ), 'Predefined services are kept' );
221 public function testLoadWiringFiles() {
222 $services = $this->newServiceContainer();
225 __DIR__
. '/TestWiring1.php',
226 __DIR__
. '/TestWiring2.php',
229 $services->loadWiringFiles( $wiringFiles );
231 $this->assertSame( 'Foo!', $services->getService( 'Foo' ) );
232 $this->assertSame( 'Bar!', $services->getService( 'Bar' ) );
235 public function testLoadWiringFiles_fail_duplicate() {
236 $services = $this->newServiceContainer();
239 __DIR__
. '/TestWiring1.php',
240 __DIR__
. '/./TestWiring1.php',
243 // loading the same file twice should fail, because
244 $this->setExpectedException( MediaWiki\Services\ServiceAlreadyDefinedException
::class );
246 $services->loadWiringFiles( $wiringFiles );
249 public function testRedefineService() {
250 $services = $this->newServiceContainer( [ 'Foo' ] );
252 $theService1 = new stdClass();
253 $name = 'TestService92834576';
255 $services->defineService( $name, function () {
256 PHPUnit_Framework_Assert
::fail(
257 'The original instantiator function should not get called'
261 // redefine before instantiation
262 $services->redefineService(
264 function ( $actualLocator, $extra ) use ( $services, $theService1 ) {
265 PHPUnit_Framework_Assert
::assertSame( $services, $actualLocator );
266 PHPUnit_Framework_Assert
::assertSame( 'Foo', $extra );
271 // force instantiation, check result
272 $this->assertSame( $theService1, $services->getService( $name ) );
275 public function testRedefineService_disabled() {
276 $services = $this->newServiceContainer( [ 'Foo' ] );
278 $theService1 = new stdClass();
279 $name = 'TestService92834576';
281 $services->defineService( $name, function () {
285 // disable the service. we should be able to redefine it anyway.
286 $services->disableService( $name );
288 $services->redefineService( $name, function () use ( $theService1 ) {
292 // force instantiation, check result
293 $this->assertSame( $theService1, $services->getService( $name ) );
296 public function testRedefineService_fail_undefined() {
297 $services = $this->newServiceContainer();
299 $theService = new stdClass();
300 $name = 'TestService92834576';
302 $this->setExpectedException( MediaWiki\Services\NoSuchServiceException
::class );
304 $services->redefineService( $name, function () use ( $theService ) {
309 public function testRedefineService_fail_in_use() {
310 $services = $this->newServiceContainer( [ 'Foo' ] );
312 $theService = new stdClass();
313 $name = 'TestService92834576';
315 $services->defineService( $name, function () {
319 // create the service, so it can no longer be redefined
320 $services->getService( $name );
322 $this->setExpectedException( MediaWiki\Services\CannotReplaceActiveServiceException
::class );
324 $services->redefineService( $name, function () use ( $theService ) {
329 public function testDisableService() {
330 $services = $this->newServiceContainer( [ 'Foo' ] );
332 $destructible = $this->getMockBuilder( MediaWiki\Services\DestructibleService
::class )
334 $destructible->expects( $this->once() )
335 ->method( 'destroy' );
337 $services->defineService( 'Foo', function () use ( $destructible ) {
338 return $destructible;
340 $services->defineService( 'Bar', function () {
341 return new stdClass();
343 $services->defineService( 'Qux', function () {
344 return new stdClass();
347 // instantiate Foo and Bar services
348 $services->getService( 'Foo' );
349 $services->getService( 'Bar' );
351 // disable service, should call destroy() once.
352 $services->disableService( 'Foo' );
354 // disabled service should still be listed
355 $this->assertContains( 'Foo', $services->getServiceNames() );
357 // getting other services should still work
358 $services->getService( 'Bar' );
360 // disable non-destructible service, and not-yet-instantiated service
361 $services->disableService( 'Bar' );
362 $services->disableService( 'Qux' );
364 $this->assertNull( $services->peekService( 'Bar' ) );
365 $this->assertNull( $services->peekService( 'Qux' ) );
367 // disabled service should still be listed
368 $this->assertContains( 'Bar', $services->getServiceNames() );
369 $this->assertContains( 'Qux', $services->getServiceNames() );
371 $this->setExpectedException( MediaWiki\Services\ServiceDisabledException
::class );
372 $services->getService( 'Qux' );
375 public function testDisableService_fail_undefined() {
376 $services = $this->newServiceContainer();
378 $theService = new stdClass();
379 $name = 'TestService92834576';
381 $this->setExpectedException( MediaWiki\Services\NoSuchServiceException
::class );
383 $services->redefineService( $name, function () use ( $theService ) {
388 public function testDestroy() {
389 $services = $this->newServiceContainer();
391 $destructible = $this->getMockBuilder( MediaWiki\Services\DestructibleService
::class )
393 $destructible->expects( $this->once() )
394 ->method( 'destroy' );
396 $services->defineService( 'Foo', function () use ( $destructible ) {
397 return $destructible;
400 $services->defineService( 'Bar', function () {
401 return new stdClass();
404 // create the service
405 $services->getService( 'Foo' );
407 // destroy the container
408 $services->destroy();
410 $this->setExpectedException( MediaWiki\Services\ContainerDisabledException
::class );
411 $services->getService( 'Bar' );