MediaWiki  1.32.0
ServiceContainerTest.php
Go to the documentation of this file.
1 <?php
3 
9 class ServiceContainerTest extends PHPUnit\Framework\TestCase {
10 
11  use MediaWikiCoversValidator;
12  use PHPUnit4And6Compat;
13 
14  private function newServiceContainer( $extraArgs = [] ) {
15  return new ServiceContainer( $extraArgs );
16  }
17 
18  public function testGetServiceNames() {
19  $services = $this->newServiceContainer();
20  $names = $services->getServiceNames();
21 
22  $this->assertInternalType( 'array', $names );
23  $this->assertEmpty( $names );
24 
25  $name = 'TestService92834576';
26  $services->defineService( $name, function () {
27  return null;
28  } );
29 
30  $names = $services->getServiceNames();
31  $this->assertContains( $name, $names );
32  }
33 
34  public function testHasService() {
35  $services = $this->newServiceContainer();
36 
37  $name = 'TestService92834576';
38  $this->assertFalse( $services->hasService( $name ) );
39 
40  $services->defineService( $name, function () {
41  return null;
42  } );
43 
44  $this->assertTrue( $services->hasService( $name ) );
45  }
46 
47  public function testGetService() {
48  $services = $this->newServiceContainer( [ 'Foo' ] );
49 
50  $theService = new stdClass();
51  $name = 'TestService92834576';
52  $count = 0;
53 
54  $services->defineService(
55  $name,
56  function ( $actualLocator, $extra ) use ( $services, $theService, &$count ) {
57  $count++;
58  PHPUnit_Framework_Assert::assertSame( $services, $actualLocator );
59  PHPUnit_Framework_Assert::assertSame( $extra, 'Foo' );
60  return $theService;
61  }
62  );
63 
64  $this->assertSame( $theService, $services->getService( $name ) );
65 
66  $services->getService( $name );
67  $this->assertSame( 1, $count, 'instantiator should be called exactly once!' );
68  }
69 
70  public function testGetService_fail_unknown() {
71  $services = $this->newServiceContainer();
72 
73  $name = 'TestService92834576';
74 
75  $this->setExpectedException( MediaWiki\Services\NoSuchServiceException::class );
76 
77  $services->getService( $name );
78  }
79 
80  public function testPeekService() {
81  $services = $this->newServiceContainer();
82 
83  $services->defineService(
84  'Foo',
85  function () {
86  return new stdClass();
87  }
88  );
89 
90  $services->defineService(
91  'Bar',
92  function () {
93  return new stdClass();
94  }
95  );
96 
97  // trigger instantiation of Foo
98  $services->getService( 'Foo' );
99 
100  $this->assertInternalType(
101  'object',
102  $services->peekService( 'Foo' ),
103  'Peek should return the service object if it had been accessed before.'
104  );
105 
106  $this->assertNull(
107  $services->peekService( 'Bar' ),
108  'Peek should return null if the service was never accessed.'
109  );
110  }
111 
112  public function testPeekService_fail_unknown() {
113  $services = $this->newServiceContainer();
114 
115  $name = 'TestService92834576';
116 
117  $this->setExpectedException( MediaWiki\Services\NoSuchServiceException::class );
118 
119  $services->peekService( $name );
120  }
121 
122  public function testDefineService() {
123  $services = $this->newServiceContainer();
124 
125  $theService = new stdClass();
126  $name = 'TestService92834576';
127 
128  $services->defineService( $name, function ( $actualLocator ) use ( $services, $theService ) {
129  PHPUnit_Framework_Assert::assertSame( $services, $actualLocator );
130  return $theService;
131  } );
132 
133  $this->assertTrue( $services->hasService( $name ) );
134  $this->assertSame( $theService, $services->getService( $name ) );
135  }
136 
138  $services = $this->newServiceContainer();
139 
140  $theService = new stdClass();
141  $name = 'TestService92834576';
142 
143  $services->defineService( $name, function () use ( $theService ) {
144  return $theService;
145  } );
146 
147  $this->setExpectedException( MediaWiki\Services\ServiceAlreadyDefinedException::class );
148 
149  $services->defineService( $name, function () use ( $theService ) {
150  return $theService;
151  } );
152  }
153 
154  public function testApplyWiring() {
155  $services = $this->newServiceContainer();
156 
157  $wiring = [
158  'Foo' => function () {
159  return 'Foo!';
160  },
161  'Bar' => function () {
162  return 'Bar!';
163  },
164  ];
165 
166  $services->applyWiring( $wiring );
167 
168  $this->assertSame( 'Foo!', $services->getService( 'Foo' ) );
169  $this->assertSame( 'Bar!', $services->getService( 'Bar' ) );
170  }
171 
172  public function testImportWiring() {
173  $services = $this->newServiceContainer();
174 
175  $wiring = [
176  'Foo' => function () {
177  return 'Foo!';
178  },
179  'Bar' => function () {
180  return 'Bar!';
181  },
182  'Car' => function () {
183  return 'FUBAR!';
184  },
185  ];
186 
187  $services->applyWiring( $wiring );
188 
189  $services->addServiceManipulator( 'Foo', function ( $service ) {
190  return $service . '+X';
191  } );
192 
193  $services->addServiceManipulator( 'Car', function ( $service ) {
194  return $service . '+X';
195  } );
196 
197  $newServices = $this->newServiceContainer();
198 
199  // create a service with manipulator
200  $newServices->defineService( 'Foo', function () {
201  return 'Foo!';
202  } );
203 
204  $newServices->addServiceManipulator( 'Foo', function ( $service ) {
205  return $service . '+Y';
206  } );
207 
208  // create a service before importing, so we can later check that
209  // existing service instances survive importWiring()
210  $newServices->defineService( 'Car', function () {
211  return 'Car!';
212  } );
213 
214  // force instantiation
215  $newServices->getService( 'Car' );
216 
217  // Define another service, so we can later check that extra wiring
218  // is not lost.
219  $newServices->defineService( 'Xar', function () {
220  return 'Xar!';
221  } );
222 
223  // import wiring, but skip `Bar`
224  $newServices->importWiring( $services, [ 'Bar' ] );
225 
226  $this->assertNotContains( 'Bar', $newServices->getServiceNames(), 'Skip `Bar` service' );
227  $this->assertSame( 'Foo!+Y+X', $newServices->getService( 'Foo' ) );
228 
229  // import all wiring, but preserve existing service instance
230  $newServices->importWiring( $services );
231 
232  $this->assertContains( 'Bar', $newServices->getServiceNames(), 'Import all services' );
233  $this->assertSame( 'Bar!', $newServices->getService( 'Bar' ) );
234  $this->assertSame( 'Car!', $newServices->getService( 'Car' ), 'Use existing service instance' );
235  $this->assertSame( 'Xar!', $newServices->getService( 'Xar' ), 'Predefined services are kept' );
236  }
237 
238  public function testLoadWiringFiles() {
239  $services = $this->newServiceContainer();
240 
241  $wiringFiles = [
242  __DIR__ . '/TestWiring1.php',
243  __DIR__ . '/TestWiring2.php',
244  ];
245 
246  $services->loadWiringFiles( $wiringFiles );
247 
248  $this->assertSame( 'Foo!', $services->getService( 'Foo' ) );
249  $this->assertSame( 'Bar!', $services->getService( 'Bar' ) );
250  }
251 
253  $services = $this->newServiceContainer();
254 
255  $wiringFiles = [
256  __DIR__ . '/TestWiring1.php',
257  __DIR__ . '/./TestWiring1.php',
258  ];
259 
260  // loading the same file twice should fail, because
261  $this->setExpectedException( MediaWiki\Services\ServiceAlreadyDefinedException::class );
262 
263  $services->loadWiringFiles( $wiringFiles );
264  }
265 
266  public function testRedefineService() {
267  $services = $this->newServiceContainer( [ 'Foo' ] );
268 
269  $theService1 = new stdClass();
270  $name = 'TestService92834576';
271 
272  $services->defineService( $name, function () {
273  PHPUnit_Framework_Assert::fail(
274  'The original instantiator function should not get called'
275  );
276  } );
277 
278  // redefine before instantiation
279  $services->redefineService(
280  $name,
281  function ( $actualLocator, $extra ) use ( $services, $theService1 ) {
282  PHPUnit_Framework_Assert::assertSame( $services, $actualLocator );
283  PHPUnit_Framework_Assert::assertSame( 'Foo', $extra );
284  return $theService1;
285  }
286  );
287 
288  // force instantiation, check result
289  $this->assertSame( $theService1, $services->getService( $name ) );
290  }
291 
292  public function testRedefineService_disabled() {
293  $services = $this->newServiceContainer( [ 'Foo' ] );
294 
295  $theService1 = new stdClass();
296  $name = 'TestService92834576';
297 
298  $services->defineService( $name, function () {
299  return 'Foo';
300  } );
301 
302  // disable the service. we should be able to redefine it anyway.
303  $services->disableService( $name );
304 
305  $services->redefineService( $name, function () use ( $theService1 ) {
306  return $theService1;
307  } );
308 
309  // force instantiation, check result
310  $this->assertSame( $theService1, $services->getService( $name ) );
311  }
312 
314  $services = $this->newServiceContainer();
315 
316  $theService = new stdClass();
317  $name = 'TestService92834576';
318 
319  $this->setExpectedException( MediaWiki\Services\NoSuchServiceException::class );
320 
321  $services->redefineService( $name, function () use ( $theService ) {
322  return $theService;
323  } );
324  }
325 
327  $services = $this->newServiceContainer( [ 'Foo' ] );
328 
329  $theService = new stdClass();
330  $name = 'TestService92834576';
331 
332  $services->defineService( $name, function () {
333  return 'Foo';
334  } );
335 
336  // create the service, so it can no longer be redefined
337  $services->getService( $name );
338 
339  $this->setExpectedException( MediaWiki\Services\CannotReplaceActiveServiceException::class );
340 
341  $services->redefineService( $name, function () use ( $theService ) {
342  return $theService;
343  } );
344  }
345 
346  public function testAddServiceManipulator() {
347  $services = $this->newServiceContainer( [ 'Foo' ] );
348 
349  $theService1 = new stdClass();
350  $theService2 = new stdClass();
351  $name = 'TestService92834576';
352 
353  $services->defineService(
354  $name,
355  function ( $actualLocator, $extra ) use ( $services, $theService1 ) {
356  PHPUnit_Framework_Assert::assertSame( $services, $actualLocator );
357  PHPUnit_Framework_Assert::assertSame( 'Foo', $extra );
358  return $theService1;
359  }
360  );
361 
362  $services->addServiceManipulator(
363  $name,
364  function (
365  $theService, $actualLocator, $extra
366  ) use (
367  $services, $theService1, $theService2
368  ) {
369  PHPUnit_Framework_Assert::assertSame( $theService1, $theService );
370  PHPUnit_Framework_Assert::assertSame( $services, $actualLocator );
371  PHPUnit_Framework_Assert::assertSame( 'Foo', $extra );
372  return $theService2;
373  }
374  );
375 
376  // force instantiation, check result
377  $this->assertSame( $theService2, $services->getService( $name ) );
378  }
379 
381  $services = $this->newServiceContainer();
382 
383  $theService = new stdClass();
384  $name = 'TestService92834576';
385 
386  $this->setExpectedException( MediaWiki\Services\NoSuchServiceException::class );
387 
388  $services->addServiceManipulator( $name, function () use ( $theService ) {
389  return $theService;
390  } );
391  }
392 
394  $services = $this->newServiceContainer( [ 'Foo' ] );
395 
396  $theService = new stdClass();
397  $name = 'TestService92834576';
398 
399  $services->defineService( $name, function () use ( $theService ) {
400  return $theService;
401  } );
402 
403  // create the service, so it can no longer be redefined
404  $services->getService( $name );
405 
406  $this->setExpectedException( MediaWiki\Services\CannotReplaceActiveServiceException::class );
407 
408  $services->addServiceManipulator( $name, function () {
409  return 'Foo';
410  } );
411  }
412 
413  public function testDisableService() {
414  $services = $this->newServiceContainer( [ 'Foo' ] );
415 
416  $destructible = $this->getMockBuilder( MediaWiki\Services\DestructibleService::class )
417  ->getMock();
418  $destructible->expects( $this->once() )
419  ->method( 'destroy' );
420 
421  $services->defineService( 'Foo', function () use ( $destructible ) {
422  return $destructible;
423  } );
424  $services->defineService( 'Bar', function () {
425  return new stdClass();
426  } );
427  $services->defineService( 'Qux', function () {
428  return new stdClass();
429  } );
430 
431  // instantiate Foo and Bar services
432  $services->getService( 'Foo' );
433  $services->getService( 'Bar' );
434 
435  // disable service, should call destroy() once.
436  $services->disableService( 'Foo' );
437 
438  // disabled service should still be listed
439  $this->assertContains( 'Foo', $services->getServiceNames() );
440 
441  // getting other services should still work
442  $services->getService( 'Bar' );
443 
444  // disable non-destructible service, and not-yet-instantiated service
445  $services->disableService( 'Bar' );
446  $services->disableService( 'Qux' );
447 
448  $this->assertNull( $services->peekService( 'Bar' ) );
449  $this->assertNull( $services->peekService( 'Qux' ) );
450 
451  // disabled service should still be listed
452  $this->assertContains( 'Bar', $services->getServiceNames() );
453  $this->assertContains( 'Qux', $services->getServiceNames() );
454 
455  $this->setExpectedException( MediaWiki\Services\ServiceDisabledException::class );
456  $services->getService( 'Qux' );
457  }
458 
460  $services = $this->newServiceContainer();
461 
462  $theService = new stdClass();
463  $name = 'TestService92834576';
464 
465  $this->setExpectedException( MediaWiki\Services\NoSuchServiceException::class );
466 
467  $services->redefineService( $name, function () use ( $theService ) {
468  return $theService;
469  } );
470  }
471 
472  public function testDestroy() {
473  $services = $this->newServiceContainer();
474 
475  $destructible = $this->getMockBuilder( MediaWiki\Services\DestructibleService::class )
476  ->getMock();
477  $destructible->expects( $this->once() )
478  ->method( 'destroy' );
479 
480  $services->defineService( 'Foo', function () use ( $destructible ) {
481  return $destructible;
482  } );
483 
484  $services->defineService( 'Bar', function () {
485  return new stdClass();
486  } );
487 
488  // create the service
489  $services->getService( 'Foo' );
490 
491  // destroy the container
492  $services->destroy();
493 
494  $this->setExpectedException( MediaWiki\Services\ContainerDisabledException::class );
495  $services->getService( 'Bar' );
496  }
497 
498 }
ServiceContainerTest\testDisableService
testDisableService()
Definition: ServiceContainerTest.php:413
ServiceContainerTest\testRedefineService_fail_in_use
testRedefineService_fail_in_use()
Definition: ServiceContainerTest.php:326
ServiceContainerTest\testApplyWiring
testApplyWiring()
Definition: ServiceContainerTest.php:154
ServiceContainerTest\testDefineService
testDefineService()
Definition: ServiceContainerTest.php:122
MediaWiki\Services\ServiceContainer
ServiceContainer provides a generic service to manage named services using lazy instantiation based o...
Definition: ServiceContainer.php:46
ServiceContainerTest\testAddServiceManipulator
testAddServiceManipulator()
Definition: ServiceContainerTest.php:346
ServiceContainerTest\testAddServiceManipulator_fail_in_use
testAddServiceManipulator_fail_in_use()
Definition: ServiceContainerTest.php:393
ServiceContainerTest\testAddServiceManipulator_fail_undefined
testAddServiceManipulator_fail_undefined()
Definition: ServiceContainerTest.php:380
ServiceContainerTest\testGetServiceNames
testGetServiceNames()
Definition: ServiceContainerTest.php:18
ServiceContainerTest\testDestroy
testDestroy()
Definition: ServiceContainerTest.php:472
php
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
ServiceContainerTest\testLoadWiringFiles
testLoadWiringFiles()
Definition: ServiceContainerTest.php:238
ServiceContainerTest\testDisableService_fail_undefined
testDisableService_fail_undefined()
Definition: ServiceContainerTest.php:459
MediaWiki
A helper class for throttling authentication attempts.
use
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
Definition: MIT-LICENSE.txt:10
ServiceContainerTest
MediaWiki\Services\ServiceContainer.
Definition: ServiceContainerTest.php:9
ServiceContainerTest\testDefineService_fail_duplicate
testDefineService_fail_duplicate()
Definition: ServiceContainerTest.php:137
ServiceContainerTest\testRedefineService
testRedefineService()
Definition: ServiceContainerTest.php:266
ServiceContainerTest\testLoadWiringFiles_fail_duplicate
testLoadWiringFiles_fail_duplicate()
Definition: ServiceContainerTest.php:252
ServiceContainerTest\testPeekService
testPeekService()
Definition: ServiceContainerTest.php:80
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:302
ServiceContainerTest\testGetService_fail_unknown
testGetService_fail_unknown()
Definition: ServiceContainerTest.php:70
ServiceContainerTest\testHasService
testHasService()
Definition: ServiceContainerTest.php:34
ServiceContainerTest\testPeekService_fail_unknown
testPeekService_fail_unknown()
Definition: ServiceContainerTest.php:112
ServiceContainerTest\testRedefineService_disabled
testRedefineService_disabled()
Definition: ServiceContainerTest.php:292
ServiceContainerTest\testRedefineService_fail_undefined
testRedefineService_fail_undefined()
Definition: ServiceContainerTest.php:313
ServiceContainerTest\testImportWiring
testImportWiring()
Definition: ServiceContainerTest.php:172
ServiceContainerTest\testGetService
testGetService()
Definition: ServiceContainerTest.php:47
class
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:52
$services
static configuration should be added through ResourceLoaderGetConfigVars instead can be used to get the real title e g db for database replication lag or jobqueue for job queue size converted to pseudo seconds It is possible to add more fields and they will be returned to the user in the API response after the basic globals have been set but before ordinary actions take place or wrap services the preferred way to define a new service is the $wgServiceWiringFiles array $services
Definition: hooks.txt:2270
ServiceContainerTest\newServiceContainer
newServiceContainer( $extraArgs=[])
Definition: ServiceContainerTest.php:14