MediaWiki  1.31.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  $newServices = $this->newServiceContainer();
190 
191  // define a service before importing, so we can later check that
192  // existing service instances survive importWiring()
193  $newServices->defineService( 'Car', function () {
194  return 'Car!';
195  } );
196 
197  // force instantiation
198  $newServices->getService( 'Car' );
199 
200  // Define another service, so we can later check that extra wiring
201  // is not lost.
202  $newServices->defineService( 'Xar', function () {
203  return 'Xar!';
204  } );
205 
206  // import wiring, but skip `Bar`
207  $newServices->importWiring( $services, [ 'Bar' ] );
208 
209  $this->assertNotContains( 'Bar', $newServices->getServiceNames(), 'Skip `Bar` service' );
210  $this->assertSame( 'Foo!', $newServices->getService( 'Foo' ) );
211 
212  // import all wiring, but preserve existing service instance
213  $newServices->importWiring( $services );
214 
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' );
219  }
220 
221  public function testLoadWiringFiles() {
222  $services = $this->newServiceContainer();
223 
224  $wiringFiles = [
225  __DIR__ . '/TestWiring1.php',
226  __DIR__ . '/TestWiring2.php',
227  ];
228 
229  $services->loadWiringFiles( $wiringFiles );
230 
231  $this->assertSame( 'Foo!', $services->getService( 'Foo' ) );
232  $this->assertSame( 'Bar!', $services->getService( 'Bar' ) );
233  }
234 
236  $services = $this->newServiceContainer();
237 
238  $wiringFiles = [
239  __DIR__ . '/TestWiring1.php',
240  __DIR__ . '/./TestWiring1.php',
241  ];
242 
243  // loading the same file twice should fail, because
244  $this->setExpectedException( MediaWiki\Services\ServiceAlreadyDefinedException::class );
245 
246  $services->loadWiringFiles( $wiringFiles );
247  }
248 
249  public function testRedefineService() {
250  $services = $this->newServiceContainer( [ 'Foo' ] );
251 
252  $theService1 = new stdClass();
253  $name = 'TestService92834576';
254 
255  $services->defineService( $name, function () {
256  PHPUnit_Framework_Assert::fail(
257  'The original instantiator function should not get called'
258  );
259  } );
260 
261  // redefine before instantiation
262  $services->redefineService(
263  $name,
264  function ( $actualLocator, $extra ) use ( $services, $theService1 ) {
265  PHPUnit_Framework_Assert::assertSame( $services, $actualLocator );
266  PHPUnit_Framework_Assert::assertSame( 'Foo', $extra );
267  return $theService1;
268  }
269  );
270 
271  // force instantiation, check result
272  $this->assertSame( $theService1, $services->getService( $name ) );
273  }
274 
275  public function testRedefineService_disabled() {
276  $services = $this->newServiceContainer( [ 'Foo' ] );
277 
278  $theService1 = new stdClass();
279  $name = 'TestService92834576';
280 
281  $services->defineService( $name, function () {
282  return 'Foo';
283  } );
284 
285  // disable the service. we should be able to redefine it anyway.
286  $services->disableService( $name );
287 
288  $services->redefineService( $name, function () use ( $theService1 ) {
289  return $theService1;
290  } );
291 
292  // force instantiation, check result
293  $this->assertSame( $theService1, $services->getService( $name ) );
294  }
295 
297  $services = $this->newServiceContainer();
298 
299  $theService = new stdClass();
300  $name = 'TestService92834576';
301 
302  $this->setExpectedException( MediaWiki\Services\NoSuchServiceException::class );
303 
304  $services->redefineService( $name, function () use ( $theService ) {
305  return $theService;
306  } );
307  }
308 
310  $services = $this->newServiceContainer( [ 'Foo' ] );
311 
312  $theService = new stdClass();
313  $name = 'TestService92834576';
314 
315  $services->defineService( $name, function () {
316  return 'Foo';
317  } );
318 
319  // create the service, so it can no longer be redefined
320  $services->getService( $name );
321 
322  $this->setExpectedException( MediaWiki\Services\CannotReplaceActiveServiceException::class );
323 
324  $services->redefineService( $name, function () use ( $theService ) {
325  return $theService;
326  } );
327  }
328 
329  public function testDisableService() {
330  $services = $this->newServiceContainer( [ 'Foo' ] );
331 
332  $destructible = $this->getMockBuilder( MediaWiki\Services\DestructibleService::class )
333  ->getMock();
334  $destructible->expects( $this->once() )
335  ->method( 'destroy' );
336 
337  $services->defineService( 'Foo', function () use ( $destructible ) {
338  return $destructible;
339  } );
340  $services->defineService( 'Bar', function () {
341  return new stdClass();
342  } );
343  $services->defineService( 'Qux', function () {
344  return new stdClass();
345  } );
346 
347  // instantiate Foo and Bar services
348  $services->getService( 'Foo' );
349  $services->getService( 'Bar' );
350 
351  // disable service, should call destroy() once.
352  $services->disableService( 'Foo' );
353 
354  // disabled service should still be listed
355  $this->assertContains( 'Foo', $services->getServiceNames() );
356 
357  // getting other services should still work
358  $services->getService( 'Bar' );
359 
360  // disable non-destructible service, and not-yet-instantiated service
361  $services->disableService( 'Bar' );
362  $services->disableService( 'Qux' );
363 
364  $this->assertNull( $services->peekService( 'Bar' ) );
365  $this->assertNull( $services->peekService( 'Qux' ) );
366 
367  // disabled service should still be listed
368  $this->assertContains( 'Bar', $services->getServiceNames() );
369  $this->assertContains( 'Qux', $services->getServiceNames() );
370 
371  $this->setExpectedException( MediaWiki\Services\ServiceDisabledException::class );
372  $services->getService( 'Qux' );
373  }
374 
376  $services = $this->newServiceContainer();
377 
378  $theService = new stdClass();
379  $name = 'TestService92834576';
380 
381  $this->setExpectedException( MediaWiki\Services\NoSuchServiceException::class );
382 
383  $services->redefineService( $name, function () use ( $theService ) {
384  return $theService;
385  } );
386  }
387 
388  public function testDestroy() {
389  $services = $this->newServiceContainer();
390 
391  $destructible = $this->getMockBuilder( MediaWiki\Services\DestructibleService::class )
392  ->getMock();
393  $destructible->expects( $this->once() )
394  ->method( 'destroy' );
395 
396  $services->defineService( 'Foo', function () use ( $destructible ) {
397  return $destructible;
398  } );
399 
400  $services->defineService( 'Bar', function () {
401  return new stdClass();
402  } );
403 
404  // create the service
405  $services->getService( 'Foo' );
406 
407  // destroy the container
408  $services->destroy();
409 
410  $this->setExpectedException( MediaWiki\Services\ContainerDisabledException::class );
411  $services->getService( 'Bar' );
412  }
413 
414 }
ServiceContainerTest\testDisableService
testDisableService()
Definition: ServiceContainerTest.php:329
ServiceContainerTest\testRedefineService_fail_in_use
testRedefineService_fail_in_use()
Definition: ServiceContainerTest.php:309
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
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\testGetServiceNames
testGetServiceNames()
Definition: ServiceContainerTest.php:18
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:302
ServiceContainerTest\testDestroy
testDestroy()
Definition: ServiceContainerTest.php:388
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:221
ServiceContainerTest\testDisableService_fail_undefined
testDisableService_fail_undefined()
Definition: ServiceContainerTest.php:375
MediaWiki
A helper class for throttling authentication attempts.
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:249
ServiceContainerTest\testLoadWiringFiles_fail_duplicate
testLoadWiringFiles_fail_duplicate()
Definition: ServiceContainerTest.php:235
$services
static configuration should be added through ResourceLoaderGetConfigVars instead can be used to get the real title 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:2220
ServiceContainerTest\testPeekService
testPeekService()
Definition: ServiceContainerTest.php:80
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:275
ServiceContainerTest\testRedefineService_fail_undefined
testRedefineService_fail_undefined()
Definition: ServiceContainerTest.php:296
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
ServiceContainerTest\newServiceContainer
newServiceContainer( $extraArgs=[])
Definition: ServiceContainerTest.php:14