1b5b23f347e44661eaaf5f3b32e4a6fdf3e6cdb1
[yaffs-website] / web / core / tests / Drupal / Tests / Component / DependencyInjection / ContainerTest.php
1 <?php
2
3 /**
4  * @file
5  * Contains \Drupal\Tests\Component\DependencyInjection\ContainerTest.
6  */
7
8 namespace Drupal\Tests\Component\DependencyInjection;
9
10 use Drupal\Component\Utility\Crypt;
11 use PHPUnit\Framework\TestCase;
12 use Symfony\Component\DependencyInjection\ContainerInterface;
13 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
14 use Symfony\Component\DependencyInjection\Exception\LogicException;
15 use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
16 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
17 use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
18 use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
19 use Prophecy\Argument;
20
21 /**
22  * @coversDefaultClass \Drupal\Component\DependencyInjection\Container
23  * @group DependencyInjection
24  */
25 class ContainerTest extends TestCase {
26
27   /**
28    * The tested container.
29    *
30    * @var \Symfony\Component\DependencyInjection\ContainerInterface
31    */
32   protected $container;
33
34   /**
35    * The container definition used for the test.
36    *
37    * @var array
38    */
39   protected $containerDefinition;
40
41   /**
42    * The container class to be tested.
43    *
44    * @var bool
45    */
46   protected $containerClass;
47
48   /**
49    * Whether the container uses the machine-optimized format or not.
50    *
51    * @var bool
52    */
53   protected $machineFormat;
54
55   /**
56    * {@inheritdoc}
57    */
58   protected function setUp() {
59     $this->machineFormat = TRUE;
60     $this->containerClass = '\Drupal\Component\DependencyInjection\Container';
61     $this->containerDefinition = $this->getMockContainerDefinition();
62     $this->container = new $this->containerClass($this->containerDefinition);
63   }
64
65   /**
66    * Tests that passing a non-supported format throws an InvalidArgumentException.
67    *
68    * @covers ::__construct
69    */
70   public function testConstruct() {
71     $container_definition = $this->getMockContainerDefinition();
72     $container_definition['machine_format'] = !$this->machineFormat;
73     if (method_exists($this, 'expectException')) {
74       $this->expectException(InvalidArgumentException::class);
75     }
76     else {
77       $this->setExpectedException(InvalidArgumentException::class);
78     }
79     $container = new $this->containerClass($container_definition);
80   }
81
82   /**
83    * Tests that Container::getParameter() works properly.
84    *
85    * @covers ::getParameter
86    */
87   public function testGetParameter() {
88     $this->assertEquals($this->containerDefinition['parameters']['some_config'], $this->container->getParameter('some_config'), 'Container parameter matches for %some_config%.');
89     $this->assertEquals($this->containerDefinition['parameters']['some_other_config'], $this->container->getParameter('some_other_config'), 'Container parameter matches for %some_other_config%.');
90   }
91
92   /**
93    * Tests that Container::getParameter() works properly for non-existing
94    * parameters.
95    *
96    * @covers ::getParameter
97    * @covers ::getParameterAlternatives
98    * @covers ::getAlternatives
99    */
100   public function testGetParameterIfNotFound() {
101     if (method_exists($this, 'expectException')) {
102       $this->expectException(ParameterNotFoundException::class);
103     }
104     else {
105       $this->setExpectedException(ParameterNotFoundException::class);
106     }
107     $this->container->getParameter('parameter_that_does_not_exist');
108   }
109
110   /**
111    * Tests that Container::getParameter() works properly for NULL parameters.
112    *
113    * @covers ::getParameter
114    */
115   public function testGetParameterIfNotFoundBecauseNull() {
116     if (method_exists($this, 'expectException')) {
117       $this->expectException(ParameterNotFoundException::class);
118     }
119     else {
120       $this->setExpectedException(ParameterNotFoundException::class);
121     }
122     $this->container->getParameter(NULL);
123   }
124
125   /**
126    * Tests that Container::hasParameter() works properly.
127    *
128    * @covers ::hasParameter
129    */
130   public function testHasParameter() {
131     $this->assertTrue($this->container->hasParameter('some_config'), 'Container parameters include %some_config%.');
132     $this->assertFalse($this->container->hasParameter('some_config_not_exists'), 'Container parameters do not include %some_config_not_exists%.');
133   }
134
135   /**
136    * Tests that Container::setParameter() in an unfrozen case works properly.
137    *
138    * @covers ::setParameter
139    */
140   public function testSetParameterWithUnfrozenContainer() {
141     $container_definition = $this->containerDefinition;
142     $container_definition['frozen'] = FALSE;
143     $this->container = new $this->containerClass($container_definition);
144     $this->container->setParameter('some_config', 'new_value');
145     $this->assertEquals('new_value', $this->container->getParameter('some_config'), 'Container parameters can be set.');
146   }
147
148   /**
149    * Tests that Container::setParameter() in a frozen case works properly.
150    *
151    * @covers ::setParameter
152    */
153   public function testSetParameterWithFrozenContainer() {
154     $this->container = new $this->containerClass($this->containerDefinition);
155     if (method_exists($this, 'expectException')) {
156       $this->expectException(LogicException::class);
157     }
158     else {
159       $this->setExpectedException(LogicException::class);
160     }
161     $this->container->setParameter('some_config', 'new_value');
162   }
163
164   /**
165    * Tests that Container::get() works properly.
166    *
167    * @covers ::get
168    * @covers ::createService
169    */
170   public function testGet() {
171     $container = $this->container->get('service_container');
172     $this->assertSame($this->container, $container, 'Container can be retrieved from itself.');
173
174     // Retrieve services of the container.
175     $other_service_class = $this->containerDefinition['services']['other.service']['class'];
176     $other_service = $this->container->get('other.service');
177     $this->assertInstanceOf($other_service_class, $other_service, 'other.service has the right class.');
178
179     $some_parameter = $this->containerDefinition['parameters']['some_config'];
180     $some_other_parameter = $this->containerDefinition['parameters']['some_other_config'];
181
182     $service = $this->container->get('service.provider');
183
184     $this->assertEquals($other_service, $service->getSomeOtherService(), '@other.service was injected via constructor.');
185     $this->assertEquals($some_parameter, $service->getSomeParameter(), '%some_config% was injected via constructor.');
186     $this->assertEquals($this->container, $service->getContainer(), 'Container was injected via setter injection.');
187     $this->assertEquals($some_other_parameter, $service->getSomeOtherParameter(), '%some_other_config% was injected via setter injection.');
188     $this->assertEquals($service->_someProperty, 'foo', 'Service has added properties.');
189   }
190
191   /**
192    * Tests that Container::get() for non-shared services works properly.
193    *
194    * @covers ::get
195    * @covers ::createService
196    */
197   public function testGetForNonSharedService() {
198     $service = $this->container->get('non_shared_service');
199     $service2 = $this->container->get('non_shared_service');
200
201     $this->assertNotSame($service, $service2, 'Non shared services are always re-instantiated.');
202   }
203
204   /**
205    * Tests that Container::get() works properly for class from parameters.
206    *
207    * @covers ::get
208    * @covers ::createService
209    */
210   public function testGetForClassFromParameter() {
211     $container_definition = $this->containerDefinition;
212     $container_definition['frozen'] = FALSE;
213     $container = new $this->containerClass($container_definition);
214
215     $other_service_class = $this->containerDefinition['parameters']['some_parameter_class'];
216     $other_service = $container->get('other.service_class_from_parameter');
217     $this->assertInstanceOf($other_service_class, $other_service, 'other.service_class_from_parameter has the right class.');
218   }
219
220   /**
221    * Tests that Container::set() works properly.
222    *
223    * @covers ::set
224    */
225   public function testSet() {
226     $this->assertNull($this->container->get('new_id', ContainerInterface::NULL_ON_INVALID_REFERENCE));
227     $mock_service = new MockService();
228     $this->container->set('new_id', $mock_service);
229
230     $this->assertSame($mock_service, $this->container->get('new_id'), 'A manual set service works as expected.');
231   }
232
233   /**
234    * Tests that Container::has() works properly.
235    *
236    * @covers ::has
237    */
238   public function testHas() {
239     $this->assertTrue($this->container->has('other.service'));
240     $this->assertFalse($this->container->has('another.service'));
241
242     // Set the service manually, ensure that its also respected.
243     $mock_service = new MockService();
244     $this->container->set('another.service', $mock_service);
245     $this->assertTrue($this->container->has('another.service'));
246   }
247
248   /**
249    * Tests that Container::has() for aliased services works properly.
250    *
251    * @covers ::has
252    */
253   public function testHasForAliasedService() {
254     $service = $this->container->has('service.provider');
255     $aliased_service = $this->container->has('service.provider_alias');
256     $this->assertSame($service, $aliased_service);
257   }
258
259   /**
260    * Tests that Container::get() for circular dependencies works properly.
261    * @covers ::get
262    * @covers ::createService
263    */
264   public function testGetForCircularServices() {
265     if (method_exists($this, 'expectException')) {
266       $this->expectException(ServiceCircularReferenceException::class);
267     }
268     else {
269       $this->setExpectedException(ServiceCircularReferenceException::class);
270     }
271     $this->container->get('circular_dependency');
272   }
273
274   /**
275    * Tests that Container::get() for non-existent services works properly.
276    *
277    * @covers ::get
278    * @covers ::createService
279    * @covers ::getAlternatives
280    * @covers ::getServiceAlternatives
281    */
282   public function testGetForNonExistantService() {
283     if (method_exists($this, 'expectException')) {
284       $this->expectException(ServiceNotFoundException::class);
285     }
286     else {
287       $this->setExpectedException(ServiceNotFoundException::class);
288     }
289     $this->container->get('service_not_exists');
290   }
291
292   /**
293    * Tests that Container::get() for a serialized definition works properly.
294    *
295    * @covers ::get
296    * @covers ::createService
297    */
298   public function testGetForSerializedServiceDefinition() {
299     $container_definition = $this->containerDefinition;
300     $container_definition['services']['other.service'] = serialize($container_definition['services']['other.service']);
301     $container = new $this->containerClass($container_definition);
302
303     // Retrieve services of the container.
304     $other_service_class = $this->containerDefinition['services']['other.service']['class'];
305     $other_service = $container->get('other.service');
306     $this->assertInstanceOf($other_service_class, $other_service, 'other.service has the right class.');
307
308     $service = $container->get('service.provider');
309     $this->assertEquals($other_service, $service->getSomeOtherService(), '@other.service was injected via constructor.');
310   }
311
312   /**
313    * Tests that Container::get() for non-existent parameters works properly.
314    *
315    * @covers ::get
316    * @covers ::createService
317    * @covers ::resolveServicesAndParameters
318    */
319   public function testGetForNonExistantParameterDependency() {
320     $service = $this->container->get('service_parameter_not_exists', ContainerInterface::NULL_ON_INVALID_REFERENCE);
321     $this->assertNull($service, 'Service is NULL.');
322   }
323
324   /**
325    * Tests Container::get() with an exception due to missing parameter on the second call.
326    *
327    * @covers ::get
328    * @covers ::createService
329    * @covers ::resolveServicesAndParameters
330    */
331   public function testGetForParameterDependencyWithExceptionOnSecondCall() {
332     $service = $this->container->get('service_parameter_not_exists', ContainerInterface::NULL_ON_INVALID_REFERENCE);
333     $this->assertNull($service, 'Service is NULL.');
334
335     // Reset the service.
336     $this->container->set('service_parameter_not_exists', NULL);
337     if (method_exists($this, 'expectException')) {
338       $this->expectException(InvalidArgumentException::class);
339     }
340     else {
341       $this->setExpectedException(InvalidArgumentException::class);
342     }
343     $this->container->get('service_parameter_not_exists');
344   }
345
346   /**
347    * Tests that Container::get() for non-existent parameters works properly.
348    *
349    * @covers ::get
350    * @covers ::createService
351    * @covers ::resolveServicesAndParameters
352    */
353   public function testGetForNonExistantParameterDependencyWithException() {
354     if (method_exists($this, 'expectException')) {
355       $this->expectException(InvalidArgumentException::class);
356     }
357     else {
358       $this->setExpectedException(InvalidArgumentException::class);
359     }
360     $this->container->get('service_parameter_not_exists');
361   }
362
363   /**
364    * Tests that Container::get() for non-existent dependencies works properly.
365    *
366    * @covers ::get
367    * @covers ::createService
368    * @covers ::resolveServicesAndParameters
369    */
370   public function testGetForNonExistantServiceDependency() {
371     $service = $this->container->get('service_dependency_not_exists', ContainerInterface::NULL_ON_INVALID_REFERENCE);
372     $this->assertNull($service, 'Service is NULL.');
373   }
374
375   /**
376    * Tests that Container::get() for non-existent dependencies works properly.
377    *
378    * @covers ::get
379    * @covers ::createService
380    * @covers ::resolveServicesAndParameters
381    * @covers ::getAlternatives
382    */
383   public function testGetForNonExistantServiceDependencyWithException() {
384     if (method_exists($this, 'expectException')) {
385       $this->expectException(ServiceNotFoundException::class);
386     }
387     else {
388       $this->setExpectedException(ServiceNotFoundException::class);
389     }
390     $this->container->get('service_dependency_not_exists');
391   }
392
393   /**
394    * Tests that Container::get() for non-existent services works properly.
395    *
396    * @covers ::get
397    * @covers ::createService
398    */
399   public function testGetForNonExistantServiceWhenUsingNull() {
400     $this->assertNull($this->container->get('service_not_exists', ContainerInterface::NULL_ON_INVALID_REFERENCE), 'Not found service does not throw exception.');
401   }
402
403   /**
404    * Tests that Container::get() for NULL service works properly.
405    * @covers ::get
406    * @covers ::createService
407    */
408   public function testGetForNonExistantNULLService() {
409     if (method_exists($this, 'expectException')) {
410       $this->expectException(ServiceNotFoundException::class);
411     }
412     else {
413       $this->setExpectedException(ServiceNotFoundException::class);
414     }
415     $this->container->get(NULL);
416   }
417
418   /**
419    * Tests multiple Container::get() calls for non-existing dependencies work.
420    *
421    * @covers ::get
422    * @covers ::createService
423    */
424   public function testGetForNonExistantServiceMultipleTimes() {
425     $container = new $this->containerClass();
426
427     $this->assertNull($container->get('service_not_exists', ContainerInterface::NULL_ON_INVALID_REFERENCE), 'Not found service does not throw exception.');
428     $this->assertNull($container->get('service_not_exists', ContainerInterface::NULL_ON_INVALID_REFERENCE), 'Not found service does not throw exception on second call.');
429   }
430
431   /**
432    * Tests multiple Container::get() calls with exception on the second time.
433    *
434    * @covers ::get
435    * @covers ::createService
436    * @covers ::getAlternatives
437    */
438   public function testGetForNonExistantServiceWithExceptionOnSecondCall() {
439     $this->assertNull($this->container->get('service_not_exists', ContainerInterface::NULL_ON_INVALID_REFERENCE), 'Not found service does nto throw exception.');
440     if (method_exists($this, 'expectException')) {
441       $this->expectException(ServiceNotFoundException::class);
442     }
443     else {
444       $this->setExpectedException(ServiceNotFoundException::class);
445     }
446     $this->container->get('service_not_exists');
447   }
448
449   /**
450    * Tests that Container::get() for aliased services works properly.
451    *
452    * @covers ::get
453    * @covers ::createService
454    */
455   public function testGetForAliasedService() {
456     $service = $this->container->get('service.provider');
457     $aliased_service = $this->container->get('service.provider_alias');
458     $this->assertSame($service, $aliased_service);
459   }
460
461   /**
462    * Tests that Container::get() for synthetic services works - if defined.
463    *
464    * @covers ::get
465    * @covers ::createService
466    */
467   public function testGetForSyntheticService() {
468     $synthetic_service = new \stdClass();
469     $this->container->set('synthetic', $synthetic_service);
470     $test_service = $this->container->get('synthetic');
471     $this->assertSame($synthetic_service, $test_service);
472   }
473
474   /**
475    * Tests that Container::get() for synthetic services throws an Exception if not defined.
476    *
477    * @covers ::get
478    * @covers ::createService
479    */
480   public function testGetForSyntheticServiceWithException() {
481     if (method_exists($this, 'expectException')) {
482       $this->expectException(RuntimeException::class);
483     }
484     else {
485       $this->setExpectedException(RuntimeException::class);
486     }
487     $this->container->get('synthetic');
488   }
489
490   /**
491    * Tests that Container::get() for services with file includes works.
492    *
493    * @covers ::get
494    * @covers ::createService
495    */
496   public function testGetWithFileInclude() {
497     $file_service = $this->container->get('container_test_file_service_test');
498     $this->assertTrue(function_exists('container_test_file_service_test_service_function'));
499     $this->assertEquals('Hello Container', container_test_file_service_test_service_function());
500   }
501
502   /**
503    * Tests that Container::get() for various arguments lengths works.
504    *
505    * @covers ::get
506    * @covers ::createService
507    * @covers ::resolveServicesAndParameters
508    */
509   public function testGetForInstantiationWithVariousArgumentLengths() {
510     $args = [];
511     for ($i = 0; $i < 12; $i++) {
512       $instantiation_service = $this->container->get('service_test_instantiation_' . $i);
513       $this->assertEquals($args, $instantiation_service->getArguments());
514       $args[] = 'arg_' . $i;
515     }
516   }
517
518   /**
519    * Tests that Container::get() for wrong factories works correctly.
520    *
521    * @covers ::get
522    * @covers ::createService
523    */
524   public function testGetForWrongFactory() {
525     if (method_exists($this, 'expectException')) {
526       $this->expectException(RuntimeException::class);
527     }
528     else {
529       $this->setExpectedException(RuntimeException::class);
530     }
531     $this->container->get('wrong_factory');
532   }
533
534   /**
535    * Tests Container::get() for factories via services (Symfony 2.7.0).
536    *
537    * @covers ::get
538    * @covers ::createService
539    */
540   public function testGetForFactoryService() {
541     $factory_service = $this->container->get('factory_service');
542     $factory_service_class = $this->container->getParameter('factory_service_class');
543     $this->assertInstanceOf($factory_service_class, $factory_service);
544   }
545
546   /**
547    * Tests that Container::get() for factories via class works (Symfony 2.7.0).
548    *
549    * @covers ::get
550    * @covers ::createService
551    */
552   public function testGetForFactoryClass() {
553     $service = $this->container->get('service.provider');
554     $factory_service = $this->container->get('factory_class');
555
556     $this->assertInstanceOf(get_class($service), $factory_service);
557     $this->assertEquals('bar', $factory_service->getSomeParameter(), 'Correct parameter was passed via the factory class instantiation.');
558     $this->assertEquals($this->container, $factory_service->getContainer(), 'Container was injected via setter injection.');
559   }
560
561   /**
562    * Tests that Container::get() for configurable services throws an Exception.
563    *
564    * @covers ::get
565    * @covers ::createService
566    */
567   public function testGetForConfiguratorWithException() {
568     if (method_exists($this, 'expectException')) {
569       $this->expectException(InvalidArgumentException::class);
570     }
571     else {
572       $this->setExpectedException(InvalidArgumentException::class);
573     }
574     $this->container->get('configurable_service_exception');
575   }
576
577   /**
578    * Tests that Container::get() for configurable services works.
579    *
580    * @covers ::get
581    * @covers ::createService
582    */
583   public function testGetForConfigurator() {
584     $container = $this->container;
585
586     // Setup a configurator.
587     $configurator = $this->prophesize('\Drupal\Tests\Component\DependencyInjection\MockConfiguratorInterface');
588     $configurator->configureService(Argument::type('object'))
589       ->shouldBeCalled(1)
590       ->will(function ($args) use ($container) {
591         $args[0]->setContainer($container);
592       });
593     $container->set('configurator', $configurator->reveal());
594
595     // Test that the configurator worked.
596     $service = $container->get('configurable_service');
597     $this->assertSame($container, $service->getContainer(), 'Container was injected via configurator.');
598   }
599
600   /**
601    * Tests that private services work correctly.
602    *
603    * @covers ::get
604    * @covers ::createService
605    * @covers ::resolveServicesAndParameters
606    */
607   public function testResolveServicesAndParametersForPrivateService() {
608     $service = $this->container->get('service_using_private');
609     $private_service = $service->getSomeOtherService();
610     $this->assertEquals($private_service->getSomeParameter(), 'really_private_lama', 'Private was found successfully.');
611
612     // Test that sharing the same private services works.
613     $service = $this->container->get('another_service_using_private');
614     $another_private_service = $service->getSomeOtherService();
615     $this->assertNotSame($private_service, $another_private_service, 'Private service is not shared.');
616     $this->assertEquals($private_service->getSomeParameter(), 'really_private_lama', 'Private was found successfully.');
617   }
618
619   /**
620    * Tests that private service sharing works correctly.
621    *
622    * @covers ::get
623    * @covers ::createService
624    * @covers ::resolveServicesAndParameters
625    */
626   public function testResolveServicesAndParametersForSharedPrivateService() {
627     $service = $this->container->get('service_using_shared_private');
628     $private_service = $service->getSomeOtherService();
629     $this->assertEquals($private_service->getSomeParameter(), 'really_private_lama', 'Private was found successfully.');
630
631     // Test that sharing the same private services works.
632     $service = $this->container->get('another_service_using_shared_private');
633     $same_private_service = $service->getSomeOtherService();
634     $this->assertSame($private_service, $same_private_service, 'Private service is shared.');
635     $this->assertEquals($private_service->getSomeParameter(), 'really_private_lama', 'Private was found successfully.');
636   }
637
638   /**
639    * Tests that services with an array of arguments work correctly.
640    *
641    * @covers ::get
642    * @covers ::createService
643    * @covers ::resolveServicesAndParameters
644    */
645   public function testResolveServicesAndParametersForArgumentsUsingDeepArray() {
646     $service = $this->container->get('service_using_array');
647     $other_service = $this->container->get('other.service');
648     $this->assertEquals($other_service, $service->getSomeOtherService(), '@other.service was injected via constructor.');
649   }
650
651   /**
652    * Tests that services that are optional work correctly.
653    *
654    * @covers ::get
655    * @covers ::createService
656    * @covers ::resolveServicesAndParameters
657    */
658   public function testResolveServicesAndParametersForOptionalServiceDependencies() {
659     $service = $this->container->get('service_with_optional_dependency');
660     $this->assertNull($service->getSomeOtherService(), 'other service was NULL was expected.');
661   }
662
663   /**
664    * Tests that an invalid argument throw an Exception.
665    *
666    * @covers ::get
667    * @covers ::createService
668    * @covers ::resolveServicesAndParameters
669    */
670   public function testResolveServicesAndParametersForInvalidArgument() {
671     if (method_exists($this, 'expectException')) {
672       $this->expectException(InvalidArgumentException::class);
673     }
674     else {
675       $this->setExpectedException(InvalidArgumentException::class);
676     }
677     $this->container->get('invalid_argument_service');
678   }
679
680   /**
681    * Tests that invalid arguments throw an Exception.
682    *
683    * @covers ::get
684    * @covers ::createService
685    * @covers ::resolveServicesAndParameters
686    */
687   public function testResolveServicesAndParametersForInvalidArguments() {
688     // In case the machine-optimized format is not used, we need to simulate the
689     // test failure.
690     if (method_exists($this, 'expectException')) {
691       $this->expectException(InvalidArgumentException::class);
692     }
693     else {
694       $this->setExpectedException(InvalidArgumentException::class);
695     }
696     if (!$this->machineFormat) {
697       throw new InvalidArgumentException('Simulating the test failure.');
698     }
699     $this->container->get('invalid_arguments_service');
700   }
701
702   /**
703    * Tests that a parameter that points to a service works correctly.
704    *
705    * @covers ::get
706    * @covers ::createService
707    * @covers ::resolveServicesAndParameters
708    */
709   public function testResolveServicesAndParametersForServiceInstantiatedFromParameter() {
710     $service = $this->container->get('service.provider');
711     $test_service = $this->container->get('service_with_parameter_service');
712     $this->assertSame($service, $test_service->getSomeOtherService(), 'Service was passed via parameter.');
713   }
714
715   /**
716    * Tests that Container::initialized works correctly.
717    *
718    * @covers ::initialized
719    */
720   public function testInitialized() {
721     $this->assertFalse($this->container->initialized('late.service'), 'Late service is not initialized.');
722     $this->container->get('late.service');
723     $this->assertTrue($this->container->initialized('late.service'), 'Late service is initialized after it was retrieved once.');
724   }
725
726   /**
727    * Tests that Container::initialized works correctly for aliases.
728    *
729    * @covers ::initialized
730    */
731   public function testInitializedForAliases() {
732     $this->assertFalse($this->container->initialized('late.service_alias'), 'Late service is not initialized.');
733     $this->container->get('late.service');
734     $this->assertTrue($this->container->initialized('late.service_alias'), 'Late service is initialized after it was retrieved once.');
735   }
736
737   /**
738    * Tests that Container::getServiceIds() works properly.
739    *
740    * @covers ::getServiceIds
741    */
742   public function testGetServiceIds() {
743     $service_definition_keys = array_keys($this->containerDefinition['services']);
744     $this->assertEquals($service_definition_keys, $this->container->getServiceIds(), 'Retrieved service IDs match definition.');
745
746     $mock_service = new MockService();
747     $this->container->set('bar', $mock_service);
748     $this->container->set('service.provider', $mock_service);
749     $service_definition_keys[] = 'bar';
750
751     $this->assertEquals($service_definition_keys, $this->container->getServiceIds(), 'Retrieved service IDs match definition after setting new services.');
752   }
753
754   /**
755    * Gets a mock container definition.
756    *
757    * @return array
758    *   Associated array with parameters and services.
759    */
760   protected function getMockContainerDefinition() {
761     $fake_service = new \stdClass();
762     $parameters = [];
763     $parameters['some_parameter_class'] = get_class($fake_service);
764     $parameters['some_private_config'] = 'really_private_lama';
765     $parameters['some_config'] = 'foo';
766     $parameters['some_other_config'] = 'lama';
767     $parameters['factory_service_class'] = get_class($fake_service);
768     // Also test alias resolving.
769     $parameters['service_from_parameter'] = $this->getServiceCall('service.provider_alias');
770
771     $services = [];
772     $services['service_container'] = [
773       'class' => '\Drupal\service_container\DependencyInjection\Container',
774     ];
775     $services['other.service'] = [
776       'class' => get_class($fake_service),
777     ];
778
779     $services['non_shared_service'] = [
780       'class' => get_class($fake_service),
781       'shared' => FALSE,
782     ];
783
784     $services['other.service_class_from_parameter'] = [
785       'class' => $this->getParameterCall('some_parameter_class'),
786     ];
787     $services['late.service'] = [
788       'class' => get_class($fake_service),
789     ];
790     $services['service.provider'] = [
791       'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
792       'arguments' => $this->getCollection([
793         $this->getServiceCall('other.service'),
794         $this->getParameterCall('some_config'),
795       ]),
796       'properties' => $this->getCollection(['_someProperty' => 'foo']),
797       'calls' => [
798         [
799           'setContainer',
800           $this->getCollection([
801             $this->getServiceCall('service_container'),
802           ]),
803         ],
804         [
805           'setOtherConfigParameter',
806           $this->getCollection([
807             $this->getParameterCall('some_other_config'),
808           ]),
809         ],
810       ],
811       'priority' => 0,
812     ];
813
814     // Test private services.
815     $private_service = [
816       'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
817       'arguments' => $this->getCollection([
818         $this->getServiceCall('other.service'),
819         $this->getParameterCall('some_private_config'),
820       ]),
821       'public' => FALSE,
822     ];
823
824     $services['service_using_private'] = [
825       'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
826       'arguments' => $this->getCollection([
827         $this->getPrivateServiceCall(NULL, $private_service),
828         $this->getParameterCall('some_config'),
829       ]),
830     ];
831     $services['another_service_using_private'] = [
832       'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
833       'arguments' => $this->getCollection([
834         $this->getPrivateServiceCall(NULL, $private_service),
835         $this->getParameterCall('some_config'),
836       ]),
837     ];
838
839     // Test shared private services.
840     $id = 'private_service_shared_1';
841
842     $services['service_using_shared_private'] = [
843       'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
844       'arguments' => $this->getCollection([
845         $this->getPrivateServiceCall($id, $private_service, TRUE),
846         $this->getParameterCall('some_config'),
847       ]),
848     ];
849     $services['another_service_using_shared_private'] = [
850       'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
851       'arguments' => $this->getCollection([
852         $this->getPrivateServiceCall($id, $private_service, TRUE),
853         $this->getParameterCall('some_config'),
854       ]),
855     ];
856
857     // Tests service with invalid argument.
858     $services['invalid_argument_service'] = [
859       'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
860       'arguments' => $this->getCollection([
861         // Test passing non-strings, too.
862         1,
863         (object) [
864           'type' => 'invalid',
865         ],
866       ]),
867     ];
868
869     $services['invalid_arguments_service'] = [
870       'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
871       'arguments' => (object) [
872         'type' => 'invalid',
873       ],
874     ];
875
876     // Test service that needs deep-traversal.
877     $services['service_using_array'] = [
878       'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
879       'arguments' => $this->getCollection([
880         $this->getCollection([
881           $this->getServiceCall('other.service'),
882         ]),
883         $this->getParameterCall('some_private_config'),
884       ]),
885     ];
886
887     $services['service_with_optional_dependency'] = [
888       'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
889       'arguments' => $this->getCollection([
890         $this->getServiceCall('service.does_not_exist', ContainerInterface::NULL_ON_INVALID_REFERENCE),
891         $this->getParameterCall('some_private_config'),
892       ]),
893
894     ];
895
896     $services['factory_service'] = [
897       'class' => '\Drupal\service_container\ServiceContainer\ControllerInterface',
898       'factory' => [
899         $this->getServiceCall('service.provider'),
900         'getFactoryMethod',
901       ],
902       'arguments' => $this->getCollection([
903         $this->getParameterCall('factory_service_class'),
904       ]),
905     ];
906     $services['factory_class'] = [
907       'class' => '\Drupal\service_container\ServiceContainer\ControllerInterface',
908       'factory' => '\Drupal\Tests\Component\DependencyInjection\MockService::getFactoryMethod',
909       'arguments' => [
910         '\Drupal\Tests\Component\DependencyInjection\MockService',
911         [NULL, 'bar'],
912       ],
913       'calls' => [
914         [
915           'setContainer',
916           $this->getCollection([
917             $this->getServiceCall('service_container'),
918           ]),
919         ],
920       ],
921     ];
922
923     $services['wrong_factory'] = [
924       'class' => '\Drupal\service_container\ServiceContainer\ControllerInterface',
925       'factory' => (object) ['I am not a factory, but I pretend to be.'],
926     ];
927
928     $services['circular_dependency'] = [
929       'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
930       'arguments' => $this->getCollection([
931         $this->getServiceCall('circular_dependency'),
932       ]),
933     ];
934     $services['synthetic'] = [
935       'synthetic' => TRUE,
936     ];
937     // The file could have been named as a .php file. The reason it is a .data
938     // file is that SimpleTest tries to load it. SimpleTest does not like such
939     // fixtures and hence we use a neutral name like .data.
940     $services['container_test_file_service_test'] = [
941       'class' => '\stdClass',
942       'file' => __DIR__ . '/Fixture/container_test_file_service_test_service_function.data',
943     ];
944
945     // Test multiple arguments.
946     $args = [];
947     for ($i = 0; $i < 12; $i++) {
948       $services['service_test_instantiation_' . $i] = [
949         'class' => '\Drupal\Tests\Component\DependencyInjection\MockInstantiationService',
950         // Also test a collection that does not need resolving.
951         'arguments' => $this->getCollection($args, FALSE),
952       ];
953       $args[] = 'arg_' . $i;
954     }
955
956     $services['service_parameter_not_exists'] = [
957       'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
958       'arguments' => $this->getCollection([
959         $this->getServiceCall('service.provider'),
960         $this->getParameterCall('not_exists'),
961       ]),
962     ];
963     $services['service_dependency_not_exists'] = [
964       'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
965       'arguments' => $this->getCollection([
966         $this->getServiceCall('service_not_exists'),
967         $this->getParameterCall('some_config'),
968       ]),
969     ];
970
971     $services['service_with_parameter_service'] = [
972       'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
973       'arguments' => $this->getCollection([
974         $this->getParameterCall('service_from_parameter'),
975         // Also test deep collections that don't need resolving.
976         $this->getCollection([
977           1,
978         ], FALSE),
979       ]),
980     ];
981
982     // To ensure getAlternatives() finds something.
983     $services['service_not_exists_similar'] = [
984       'synthetic' => TRUE,
985     ];
986
987     // Test configurator.
988     $services['configurator'] = [
989       'synthetic' => TRUE,
990     ];
991     $services['configurable_service'] = [
992       'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
993       'arguments' => [],
994       'configurator' => [
995         $this->getServiceCall('configurator'),
996         'configureService'
997       ],
998     ];
999     $services['configurable_service_exception'] = [
1000       'class' => '\Drupal\Tests\Component\DependencyInjection\MockService',
1001       'arguments' => [],
1002       'configurator' => 'configurator_service_test_does_not_exist',
1003     ];
1004
1005     $aliases = [];
1006     $aliases['service.provider_alias'] = 'service.provider';
1007     $aliases['late.service_alias'] = 'late.service';
1008
1009     return [
1010       'aliases' => $aliases,
1011       'parameters' => $parameters,
1012       'services' => $services,
1013       'frozen' => TRUE,
1014       'machine_format' => $this->machineFormat,
1015     ];
1016   }
1017
1018   /**
1019    * Helper function to return a service definition.
1020    */
1021   protected function getServiceCall($id, $invalid_behavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) {
1022     return (object) [
1023       'type' => 'service',
1024       'id' => $id,
1025       'invalidBehavior' => $invalid_behavior,
1026     ];
1027   }
1028
1029   /**
1030    * Helper function to return a service definition.
1031    */
1032   protected function getParameterCall($name) {
1033     return (object) [
1034       'type' => 'parameter',
1035       'name' => $name,
1036     ];
1037   }
1038
1039   /**
1040    * Helper function to return a private service definition.
1041    */
1042   protected function getPrivateServiceCall($id, $service_definition, $shared = FALSE) {
1043     if (!$id) {
1044       $hash = Crypt::hashBase64(serialize($service_definition));
1045       $id = 'private__' . $hash;
1046     }
1047     return (object) [
1048       'type' => 'private_service',
1049       'id' => $id,
1050       'value' => $service_definition,
1051       'shared' => $shared,
1052     ];
1053   }
1054
1055   /**
1056    * Helper function to return a machine-optimized collection.
1057    */
1058   protected function getCollection($collection, $resolve = TRUE) {
1059     return (object) [
1060       'type' => 'collection',
1061       'value' => $collection,
1062       'resolve' => $resolve,
1063     ];
1064   }
1065
1066 }
1067
1068 /**
1069  * Helper interface to test Container::get() with configurator.
1070  *
1071  * @group DependencyInjection
1072  */
1073 interface MockConfiguratorInterface {
1074
1075   /**
1076    * Configures a service.
1077    *
1078    * @param object $service
1079    *   The service to configure.
1080    */
1081   public function configureService($service);
1082
1083 }
1084
1085
1086 /**
1087  * Helper class to test Container::get() method for varying number of parameters.
1088  *
1089  * @group DependencyInjection
1090  */
1091 class MockInstantiationService {
1092
1093   /**
1094    * @var mixed[]
1095    */
1096   protected $arguments;
1097
1098   /**
1099    * Construct a mock instantiation service.
1100    */
1101   public function __construct() {
1102     $this->arguments = func_get_args();
1103   }
1104
1105   /**
1106    * Return arguments injected into the service.
1107    *
1108    * @return mixed[]
1109    *   Return the passed arguments.
1110    */
1111   public function getArguments() {
1112     return $this->arguments;
1113   }
1114
1115 }
1116
1117
1118 /**
1119  * Helper class to test Container::get() method.
1120  *
1121  * @group DependencyInjection
1122  */
1123 class MockService {
1124
1125   /**
1126    * @var \Symfony\Component\DependencyInjection\ContainerInterface
1127    */
1128   protected $container;
1129
1130   /**
1131    * @var object
1132    */
1133   protected $someOtherService;
1134
1135   /**
1136    * @var string
1137    */
1138   protected $someParameter;
1139
1140   /**
1141    * @var string
1142    */
1143   protected $someOtherParameter;
1144
1145   /**
1146    * Constructs a MockService object.
1147    *
1148    * @param object $some_other_service
1149    *   (optional) Another injected service.
1150    * @param string $some_parameter
1151    *   (optional) An injected parameter.
1152    */
1153   public function __construct($some_other_service = NULL, $some_parameter = NULL) {
1154     if (is_array($some_other_service)) {
1155       $some_other_service = $some_other_service[0];
1156     }
1157     $this->someOtherService = $some_other_service;
1158     $this->someParameter = $some_parameter;
1159   }
1160
1161   /**
1162    * Sets the container object.
1163    *
1164    * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
1165    *   The container to inject via setter injection.
1166    */
1167   public function setContainer(ContainerInterface $container) {
1168     $this->container = $container;
1169   }
1170
1171   /**
1172    * Gets the container object.
1173    *
1174    * @return \Symfony\Component\DependencyInjection\ContainerInterface
1175    *   The internally set container.
1176    */
1177   public function getContainer() {
1178     return $this->container;
1179   }
1180
1181   /**
1182    * Gets the someOtherService object.
1183    *
1184    * @return object
1185    *   The injected service.
1186    */
1187   public function getSomeOtherService() {
1188     return $this->someOtherService;
1189   }
1190
1191   /**
1192    * Gets the someParameter property.
1193    *
1194    * @return string
1195    *   The injected parameter.
1196    */
1197   public function getSomeParameter() {
1198     return $this->someParameter;
1199   }
1200
1201   /**
1202    * Sets the someOtherParameter property.
1203    *
1204    * @param string $some_other_parameter
1205    *   The setter injected parameter.
1206    */
1207   public function setOtherConfigParameter($some_other_parameter) {
1208     $this->someOtherParameter = $some_other_parameter;
1209   }
1210
1211   /**
1212    * Gets the someOtherParameter property.
1213    *
1214    * @return string
1215    *   The injected parameter.
1216    */
1217   public function getSomeOtherParameter() {
1218     return $this->someOtherParameter;
1219   }
1220
1221   /**
1222    * Provides a factory method to get a service.
1223    *
1224    * @param string $class
1225    *   The class name of the class to instantiate
1226    * @param array $arguments
1227    *   (optional) Arguments to pass to the new class.
1228    *
1229    * @return object
1230    *   The instantiated service object.
1231    */
1232   public static function getFactoryMethod($class, $arguments = []) {
1233     $r = new \ReflectionClass($class);
1234     $service = ($r->getConstructor() === NULL) ? $r->newInstance() : $r->newInstanceArgs($arguments);
1235
1236     return $service;
1237   }
1238
1239 }