Mercurial > hg > cmmr2012-drupal-site
diff core/tests/Drupal/Tests/Component/DependencyInjection/ContainerTest.php @ 0:c75dbcec494b
Initial commit from drush-created site
author | Chris Cannam |
---|---|
date | Thu, 05 Jul 2018 14:24:15 +0000 |
parents | |
children | a9cd425dd02b |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/tests/Drupal/Tests/Component/DependencyInjection/ContainerTest.php Thu Jul 05 14:24:15 2018 +0000 @@ -0,0 +1,1239 @@ +<?php + +/** + * @file + * Contains \Drupal\Tests\Component\DependencyInjection\ContainerTest. + */ + +namespace Drupal\Tests\Component\DependencyInjection; + +use Drupal\Component\Utility\Crypt; +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\LogicException; +use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; +use Prophecy\Argument; + +/** + * @coversDefaultClass \Drupal\Component\DependencyInjection\Container + * @group DependencyInjection + */ +class ContainerTest extends TestCase { + + /** + * The tested container. + * + * @var \Symfony\Component\DependencyInjection\ContainerInterface + */ + protected $container; + + /** + * The container definition used for the test. + * + * @var array + */ + protected $containerDefinition; + + /** + * The container class to be tested. + * + * @var bool + */ + protected $containerClass; + + /** + * Whether the container uses the machine-optimized format or not. + * + * @var bool + */ + protected $machineFormat; + + /** + * {@inheritdoc} + */ + protected function setUp() { + $this->machineFormat = TRUE; + $this->containerClass = '\Drupal\Component\DependencyInjection\Container'; + $this->containerDefinition = $this->getMockContainerDefinition(); + $this->container = new $this->containerClass($this->containerDefinition); + } + + /** + * Tests that passing a non-supported format throws an InvalidArgumentException. + * + * @covers ::__construct + */ + public function testConstruct() { + $container_definition = $this->getMockContainerDefinition(); + $container_definition['machine_format'] = !$this->machineFormat; + if (method_exists($this, 'expectException')) { + $this->expectException(InvalidArgumentException::class); + } + else { + $this->setExpectedException(InvalidArgumentException::class); + } + $container = new $this->containerClass($container_definition); + } + + /** + * Tests that Container::getParameter() works properly. + * + * @covers ::getParameter + */ + public function testGetParameter() { + $this->assertEquals($this->containerDefinition['parameters']['some_config'], $this->container->getParameter('some_config'), 'Container parameter matches for %some_config%.'); + $this->assertEquals($this->containerDefinition['parameters']['some_other_config'], $this->container->getParameter('some_other_config'), 'Container parameter matches for %some_other_config%.'); + } + + /** + * Tests that Container::getParameter() works properly for non-existing + * parameters. + * + * @covers ::getParameter + * @covers ::getParameterAlternatives + * @covers ::getAlternatives + */ + public function testGetParameterIfNotFound() { + if (method_exists($this, 'expectException')) { + $this->expectException(ParameterNotFoundException::class); + } + else { + $this->setExpectedException(ParameterNotFoundException::class); + } + $this->container->getParameter('parameter_that_does_not_exist'); + } + + /** + * Tests that Container::getParameter() works properly for NULL parameters. + * + * @covers ::getParameter + */ + public function testGetParameterIfNotFoundBecauseNull() { + if (method_exists($this, 'expectException')) { + $this->expectException(ParameterNotFoundException::class); + } + else { + $this->setExpectedException(ParameterNotFoundException::class); + } + $this->container->getParameter(NULL); + } + + /** + * Tests that Container::hasParameter() works properly. + * + * @covers ::hasParameter + */ + public function testHasParameter() { + $this->assertTrue($this->container->hasParameter('some_config'), 'Container parameters include %some_config%.'); + $this->assertFalse($this->container->hasParameter('some_config_not_exists'), 'Container parameters do not include %some_config_not_exists%.'); + } + + /** + * Tests that Container::setParameter() in an unfrozen case works properly. + * + * @covers ::setParameter + */ + public function testSetParameterWithUnfrozenContainer() { + $container_definition = $this->containerDefinition; + $container_definition['frozen'] = FALSE; + $this->container = new $this->containerClass($container_definition); + $this->container->setParameter('some_config', 'new_value'); + $this->assertEquals('new_value', $this->container->getParameter('some_config'), 'Container parameters can be set.'); + } + + /** + * Tests that Container::setParameter() in a frozen case works properly. + * + * @covers ::setParameter + */ + public function testSetParameterWithFrozenContainer() { + $this->container = new $this->containerClass($this->containerDefinition); + if (method_exists($this, 'expectException')) { + $this->expectException(LogicException::class); + } + else { + $this->setExpectedException(LogicException::class); + } + $this->container->setParameter('some_config', 'new_value'); + } + + /** + * Tests that Container::get() works properly. + * + * @covers ::get + * @covers ::createService + */ + public function testGet() { + $container = $this->container->get('service_container'); + $this->assertSame($this->container, $container, 'Container can be retrieved from itself.'); + + // Retrieve services of the container. + $other_service_class = $this->containerDefinition['services']['other.service']['class']; + $other_service = $this->container->get('other.service'); + $this->assertInstanceOf($other_service_class, $other_service, 'other.service has the right class.'); + + $some_parameter = $this->containerDefinition['parameters']['some_config']; + $some_other_parameter = $this->containerDefinition['parameters']['some_other_config']; + + $service = $this->container->get('service.provider'); + + $this->assertEquals($other_service, $service->getSomeOtherService(), '@other.service was injected via constructor.'); + $this->assertEquals($some_parameter, $service->getSomeParameter(), '%some_config% was injected via constructor.'); + $this->assertEquals($this->container, $service->getContainer(), 'Container was injected via setter injection.'); + $this->assertEquals($some_other_parameter, $service->getSomeOtherParameter(), '%some_other_config% was injected via setter injection.'); + $this->assertEquals($service->_someProperty, 'foo', 'Service has added properties.'); + } + + /** + * Tests that Container::get() for non-shared services works properly. + * + * @covers ::get + * @covers ::createService + */ + public function testGetForNonSharedService() { + $service = $this->container->get('non_shared_service'); + $service2 = $this->container->get('non_shared_service'); + + $this->assertNotSame($service, $service2, 'Non shared services are always re-instantiated.'); + } + + /** + * Tests that Container::get() works properly for class from parameters. + * + * @covers ::get + * @covers ::createService + */ + public function testGetForClassFromParameter() { + $container_definition = $this->containerDefinition; + $container_definition['frozen'] = FALSE; + $container = new $this->containerClass($container_definition); + + $other_service_class = $this->containerDefinition['parameters']['some_parameter_class']; + $other_service = $container->get('other.service_class_from_parameter'); + $this->assertInstanceOf($other_service_class, $other_service, 'other.service_class_from_parameter has the right class.'); + } + + /** + * Tests that Container::set() works properly. + * + * @covers ::set + */ + public function testSet() { + $this->assertNull($this->container->get('new_id', ContainerInterface::NULL_ON_INVALID_REFERENCE)); + $mock_service = new MockService(); + $this->container->set('new_id', $mock_service); + + $this->assertSame($mock_service, $this->container->get('new_id'), 'A manual set service works as expected.'); + } + + /** + * Tests that Container::has() works properly. + * + * @covers ::has + */ + public function testHas() { + $this->assertTrue($this->container->has('other.service')); + $this->assertFalse($this->container->has('another.service')); + + // Set the service manually, ensure that its also respected. + $mock_service = new MockService(); + $this->container->set('another.service', $mock_service); + $this->assertTrue($this->container->has('another.service')); + } + + /** + * Tests that Container::has() for aliased services works properly. + * + * @covers ::has + */ + public function testHasForAliasedService() { + $service = $this->container->has('service.provider'); + $aliased_service = $this->container->has('service.provider_alias'); + $this->assertSame($service, $aliased_service); + } + + /** + * Tests that Container::get() for circular dependencies works properly. + * @covers ::get + * @covers ::createService + */ + public function testGetForCircularServices() { + if (method_exists($this, 'expectException')) { + $this->expectException(ServiceCircularReferenceException::class); + } + else { + $this->setExpectedException(ServiceCircularReferenceException::class); + } + $this->container->get('circular_dependency'); + } + + /** + * Tests that Container::get() for non-existent services works properly. + * + * @covers ::get + * @covers ::createService + * @covers ::getAlternatives + * @covers ::getServiceAlternatives + */ + public function testGetForNonExistantService() { + if (method_exists($this, 'expectException')) { + $this->expectException(ServiceNotFoundException::class); + } + else { + $this->setExpectedException(ServiceNotFoundException::class); + } + $this->container->get('service_not_exists'); + } + + /** + * Tests that Container::get() for a serialized definition works properly. + * + * @covers ::get + * @covers ::createService + */ + public function testGetForSerializedServiceDefinition() { + $container_definition = $this->containerDefinition; + $container_definition['services']['other.service'] = serialize($container_definition['services']['other.service']); + $container = new $this->containerClass($container_definition); + + // Retrieve services of the container. + $other_service_class = $this->containerDefinition['services']['other.service']['class']; + $other_service = $container->get('other.service'); + $this->assertInstanceOf($other_service_class, $other_service, 'other.service has the right class.'); + + $service = $container->get('service.provider'); + $this->assertEquals($other_service, $service->getSomeOtherService(), '@other.service was injected via constructor.'); + } + + /** + * Tests that Container::get() for non-existent parameters works properly. + * + * @covers ::get + * @covers ::createService + * @covers ::resolveServicesAndParameters + */ + public function testGetForNonExistantParameterDependency() { + $service = $this->container->get('service_parameter_not_exists', ContainerInterface::NULL_ON_INVALID_REFERENCE); + $this->assertNull($service, 'Service is NULL.'); + } + + /** + * Tests Container::get() with an exception due to missing parameter on the second call. + * + * @covers ::get + * @covers ::createService + * @covers ::resolveServicesAndParameters + */ + public function testGetForParameterDependencyWithExceptionOnSecondCall() { + $service = $this->container->get('service_parameter_not_exists', ContainerInterface::NULL_ON_INVALID_REFERENCE); + $this->assertNull($service, 'Service is NULL.'); + + // Reset the service. + $this->container->set('service_parameter_not_exists', NULL); + if (method_exists($this, 'expectException')) { + $this->expectException(InvalidArgumentException::class); + } + else { + $this->setExpectedException(InvalidArgumentException::class); + } + $this->container->get('service_parameter_not_exists'); + } + + /** + * Tests that Container::get() for non-existent parameters works properly. + * + * @covers ::get + * @covers ::createService + * @covers ::resolveServicesAndParameters + */ + public function testGetForNonExistantParameterDependencyWithException() { + if (method_exists($this, 'expectException')) { + $this->expectException(InvalidArgumentException::class); + } + else { + $this->setExpectedException(InvalidArgumentException::class); + } + $this->container->get('service_parameter_not_exists'); + } + + /** + * Tests that Container::get() for non-existent dependencies works properly. + * + * @covers ::get + * @covers ::createService + * @covers ::resolveServicesAndParameters + */ + public function testGetForNonExistantServiceDependency() { + $service = $this->container->get('service_dependency_not_exists', ContainerInterface::NULL_ON_INVALID_REFERENCE); + $this->assertNull($service, 'Service is NULL.'); + } + + /** + * Tests that Container::get() for non-existent dependencies works properly. + * + * @covers ::get + * @covers ::createService + * @covers ::resolveServicesAndParameters + * @covers ::getAlternatives + */ + public function testGetForNonExistantServiceDependencyWithException() { + if (method_exists($this, 'expectException')) { + $this->expectException(ServiceNotFoundException::class); + } + else { + $this->setExpectedException(ServiceNotFoundException::class); + } + $this->container->get('service_dependency_not_exists'); + } + + /** + * Tests that Container::get() for non-existent services works properly. + * + * @covers ::get + * @covers ::createService + */ + public function testGetForNonExistantServiceWhenUsingNull() { + $this->assertNull($this->container->get('service_not_exists', ContainerInterface::NULL_ON_INVALID_REFERENCE), 'Not found service does not throw exception.'); + } + + /** + * Tests that Container::get() for NULL service works properly. + * @covers ::get + * @covers ::createService + */ + public function testGetForNonExistantNULLService() { + if (method_exists($this, 'expectException')) { + $this->expectException(ServiceNotFoundException::class); + } + else { + $this->setExpectedException(ServiceNotFoundException::class); + } + $this->container->get(NULL); + } + + /** + * Tests multiple Container::get() calls for non-existing dependencies work. + * + * @covers ::get + * @covers ::createService + */ + public function testGetForNonExistantServiceMultipleTimes() { + $container = new $this->containerClass(); + + $this->assertNull($container->get('service_not_exists', ContainerInterface::NULL_ON_INVALID_REFERENCE), 'Not found service does not throw exception.'); + $this->assertNull($container->get('service_not_exists', ContainerInterface::NULL_ON_INVALID_REFERENCE), 'Not found service does not throw exception on second call.'); + } + + /** + * Tests multiple Container::get() calls with exception on the second time. + * + * @covers ::get + * @covers ::createService + * @covers ::getAlternatives + */ + public function testGetForNonExistantServiceWithExceptionOnSecondCall() { + $this->assertNull($this->container->get('service_not_exists', ContainerInterface::NULL_ON_INVALID_REFERENCE), 'Not found service does nto throw exception.'); + if (method_exists($this, 'expectException')) { + $this->expectException(ServiceNotFoundException::class); + } + else { + $this->setExpectedException(ServiceNotFoundException::class); + } + $this->container->get('service_not_exists'); + } + + /** + * Tests that Container::get() for aliased services works properly. + * + * @covers ::get + * @covers ::createService + */ + public function testGetForAliasedService() { + $service = $this->container->get('service.provider'); + $aliased_service = $this->container->get('service.provider_alias'); + $this->assertSame($service, $aliased_service); + } + + /** + * Tests that Container::get() for synthetic services works - if defined. + * + * @covers ::get + * @covers ::createService + */ + public function testGetForSyntheticService() { + $synthetic_service = new \stdClass(); + $this->container->set('synthetic', $synthetic_service); + $test_service = $this->container->get('synthetic'); + $this->assertSame($synthetic_service, $test_service); + } + + /** + * Tests that Container::get() for synthetic services throws an Exception if not defined. + * + * @covers ::get + * @covers ::createService + */ + public function testGetForSyntheticServiceWithException() { + if (method_exists($this, 'expectException')) { + $this->expectException(RuntimeException::class); + } + else { + $this->setExpectedException(RuntimeException::class); + } + $this->container->get('synthetic'); + } + + /** + * Tests that Container::get() for services with file includes works. + * + * @covers ::get + * @covers ::createService + */ + public function testGetWithFileInclude() { + $file_service = $this->container->get('container_test_file_service_test'); + $this->assertTrue(function_exists('container_test_file_service_test_service_function')); + $this->assertEquals('Hello Container', container_test_file_service_test_service_function()); + } + + /** + * Tests that Container::get() for various arguments lengths works. + * + * @covers ::get + * @covers ::createService + * @covers ::resolveServicesAndParameters + */ + public function testGetForInstantiationWithVariousArgumentLengths() { + $args = []; + for ($i = 0; $i < 12; $i++) { + $instantiation_service = $this->container->get('service_test_instantiation_' . $i); + $this->assertEquals($args, $instantiation_service->getArguments()); + $args[] = 'arg_' . $i; + } + } + + /** + * Tests that Container::get() for wrong factories works correctly. + * + * @covers ::get + * @covers ::createService + */ + public function testGetForWrongFactory() { + if (method_exists($this, 'expectException')) { + $this->expectException(RuntimeException::class); + } + else { + $this->setExpectedException(RuntimeException::class); + } + $this->container->get('wrong_factory'); + } + + /** + * Tests Container::get() for factories via services (Symfony 2.7.0). + * + * @covers ::get + * @covers ::createService + */ + public function testGetForFactoryService() { + $factory_service = $this->container->get('factory_service'); + $factory_service_class = $this->container->getParameter('factory_service_class'); + $this->assertInstanceOf($factory_service_class, $factory_service); + } + + /** + * Tests that Container::get() for factories via class works (Symfony 2.7.0). + * + * @covers ::get + * @covers ::createService + */ + public function testGetForFactoryClass() { + $service = $this->container->get('service.provider'); + $factory_service = $this->container->get('factory_class'); + + $this->assertInstanceOf(get_class($service), $factory_service); + $this->assertEquals('bar', $factory_service->getSomeParameter(), 'Correct parameter was passed via the factory class instantiation.'); + $this->assertEquals($this->container, $factory_service->getContainer(), 'Container was injected via setter injection.'); + } + + /** + * Tests that Container::get() for configurable services throws an Exception. + * + * @covers ::get + * @covers ::createService + */ + public function testGetForConfiguratorWithException() { + if (method_exists($this, 'expectException')) { + $this->expectException(InvalidArgumentException::class); + } + else { + $this->setExpectedException(InvalidArgumentException::class); + } + $this->container->get('configurable_service_exception'); + } + + /** + * Tests that Container::get() for configurable services works. + * + * @covers ::get + * @covers ::createService + */ + public function testGetForConfigurator() { + $container = $this->container; + + // Setup a configurator. + $configurator = $this->prophesize('\Drupal\Tests\Component\DependencyInjection\MockConfiguratorInterface'); + $configurator->configureService(Argument::type('object')) + ->shouldBeCalled(1) + ->will(function ($args) use ($container) { + $args[0]->setContainer($container); + }); + $container->set('configurator', $configurator->reveal()); + + // Test that the configurator worked. + $service = $container->get('configurable_service'); + $this->assertSame($container, $service->getContainer(), 'Container was injected via configurator.'); + } + + /** + * Tests that private services work correctly. + * + * @covers ::get + * @covers ::createService + * @covers ::resolveServicesAndParameters + */ + public function testResolveServicesAndParametersForPrivateService() { + $service = $this->container->get('service_using_private'); + $private_service = $service->getSomeOtherService(); + $this->assertEquals($private_service->getSomeParameter(), 'really_private_lama', 'Private was found successfully.'); + + // Test that sharing the same private services works. + $service = $this->container->get('another_service_using_private'); + $another_private_service = $service->getSomeOtherService(); + $this->assertNotSame($private_service, $another_private_service, 'Private service is not shared.'); + $this->assertEquals($private_service->getSomeParameter(), 'really_private_lama', 'Private was found successfully.'); + } + + /** + * Tests that private service sharing works correctly. + * + * @covers ::get + * @covers ::createService + * @covers ::resolveServicesAndParameters + */ + public function testResolveServicesAndParametersForSharedPrivateService() { + $service = $this->container->get('service_using_shared_private'); + $private_service = $service->getSomeOtherService(); + $this->assertEquals($private_service->getSomeParameter(), 'really_private_lama', 'Private was found successfully.'); + + // Test that sharing the same private services works. + $service = $this->container->get('another_service_using_shared_private'); + $same_private_service = $service->getSomeOtherService(); + $this->assertSame($private_service, $same_private_service, 'Private service is shared.'); + $this->assertEquals($private_service->getSomeParameter(), 'really_private_lama', 'Private was found successfully.'); + } + + /** + * Tests that services with an array of arguments work correctly. + * + * @covers ::get + * @covers ::createService + * @covers ::resolveServicesAndParameters + */ + public function testResolveServicesAndParametersForArgumentsUsingDeepArray() { + $service = $this->container->get('service_using_array'); + $other_service = $this->container->get('other.service'); + $this->assertEquals($other_service, $service->getSomeOtherService(), '@other.service was injected via constructor.'); + } + + /** + * Tests that services that are optional work correctly. + * + * @covers ::get + * @covers ::createService + * @covers ::resolveServicesAndParameters + */ + public function testResolveServicesAndParametersForOptionalServiceDependencies() { + $service = $this->container->get('service_with_optional_dependency'); + $this->assertNull($service->getSomeOtherService(), 'other service was NULL was expected.'); + } + + /** + * Tests that an invalid argument throw an Exception. + * + * @covers ::get + * @covers ::createService + * @covers ::resolveServicesAndParameters + */ + public function testResolveServicesAndParametersForInvalidArgument() { + if (method_exists($this, 'expectException')) { + $this->expectException(InvalidArgumentException::class); + } + else { + $this->setExpectedException(InvalidArgumentException::class); + } + $this->container->get('invalid_argument_service'); + } + + /** + * Tests that invalid arguments throw an Exception. + * + * @covers ::get + * @covers ::createService + * @covers ::resolveServicesAndParameters + */ + public function testResolveServicesAndParametersForInvalidArguments() { + // In case the machine-optimized format is not used, we need to simulate the + // test failure. + if (method_exists($this, 'expectException')) { + $this->expectException(InvalidArgumentException::class); + } + else { + $this->setExpectedException(InvalidArgumentException::class); + } + if (!$this->machineFormat) { + throw new InvalidArgumentException('Simulating the test failure.'); + } + $this->container->get('invalid_arguments_service'); + } + + /** + * Tests that a parameter that points to a service works correctly. + * + * @covers ::get + * @covers ::createService + * @covers ::resolveServicesAndParameters + */ + public function testResolveServicesAndParametersForServiceInstantiatedFromParameter() { + $service = $this->container->get('service.provider'); + $test_service = $this->container->get('service_with_parameter_service'); + $this->assertSame($service, $test_service->getSomeOtherService(), 'Service was passed via parameter.'); + } + + /** + * Tests that Container::initialized works correctly. + * + * @covers ::initialized + */ + public function testInitialized() { + $this->assertFalse($this->container->initialized('late.service'), 'Late service is not initialized.'); + $this->container->get('late.service'); + $this->assertTrue($this->container->initialized('late.service'), 'Late service is initialized after it was retrieved once.'); + } + + /** + * Tests that Container::initialized works correctly for aliases. + * + * @covers ::initialized + */ + public function testInitializedForAliases() { + $this->assertFalse($this->container->initialized('late.service_alias'), 'Late service is not initialized.'); + $this->container->get('late.service'); + $this->assertTrue($this->container->initialized('late.service_alias'), 'Late service is initialized after it was retrieved once.'); + } + + /** + * Tests that Container::getServiceIds() works properly. + * + * @covers ::getServiceIds + */ + public function testGetServiceIds() { + $service_definition_keys = array_keys($this->containerDefinition['services']); + $this->assertEquals($service_definition_keys, $this->container->getServiceIds(), 'Retrieved service IDs match definition.'); + + $mock_service = new MockService(); + $this->container->set('bar', $mock_service); + $this->container->set('service.provider', $mock_service); + $service_definition_keys[] = 'bar'; + + $this->assertEquals($service_definition_keys, $this->container->getServiceIds(), 'Retrieved service IDs match definition after setting new services.'); + } + + /** + * Gets a mock container definition. + * + * @return array + * Associated array with parameters and services. + */ + protected function getMockContainerDefinition() { + $fake_service = new \stdClass(); + $parameters = []; + $parameters['some_parameter_class'] = get_class($fake_service); + $parameters['some_private_config'] = 'really_private_lama'; + $parameters['some_config'] = 'foo'; + $parameters['some_other_config'] = 'lama'; + $parameters['factory_service_class'] = get_class($fake_service); + // Also test alias resolving. + $parameters['service_from_parameter'] = $this->getServiceCall('service.provider_alias'); + + $services = []; + $services['service_container'] = [ + 'class' => '\Drupal\service_container\DependencyInjection\Container', + ]; + $services['other.service'] = [ + 'class' => get_class($fake_service), + ]; + + $services['non_shared_service'] = [ + 'class' => get_class($fake_service), + 'shared' => FALSE, + ]; + + $services['other.service_class_from_parameter'] = [ + 'class' => $this->getParameterCall('some_parameter_class'), + ]; + $services['late.service'] = [ + 'class' => get_class($fake_service), + ]; + $services['service.provider'] = [ + 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService', + 'arguments' => $this->getCollection([ + $this->getServiceCall('other.service'), + $this->getParameterCall('some_config'), + ]), + 'properties' => $this->getCollection(['_someProperty' => 'foo']), + 'calls' => [ + [ + 'setContainer', + $this->getCollection([ + $this->getServiceCall('service_container'), + ]), + ], + [ + 'setOtherConfigParameter', + $this->getCollection([ + $this->getParameterCall('some_other_config'), + ]), + ], + ], + 'priority' => 0, + ]; + + // Test private services. + $private_service = [ + 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService', + 'arguments' => $this->getCollection([ + $this->getServiceCall('other.service'), + $this->getParameterCall('some_private_config'), + ]), + 'public' => FALSE, + ]; + + $services['service_using_private'] = [ + 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService', + 'arguments' => $this->getCollection([ + $this->getPrivateServiceCall(NULL, $private_service), + $this->getParameterCall('some_config'), + ]), + ]; + $services['another_service_using_private'] = [ + 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService', + 'arguments' => $this->getCollection([ + $this->getPrivateServiceCall(NULL, $private_service), + $this->getParameterCall('some_config'), + ]), + ]; + + // Test shared private services. + $id = 'private_service_shared_1'; + + $services['service_using_shared_private'] = [ + 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService', + 'arguments' => $this->getCollection([ + $this->getPrivateServiceCall($id, $private_service, TRUE), + $this->getParameterCall('some_config'), + ]), + ]; + $services['another_service_using_shared_private'] = [ + 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService', + 'arguments' => $this->getCollection([ + $this->getPrivateServiceCall($id, $private_service, TRUE), + $this->getParameterCall('some_config'), + ]), + ]; + + // Tests service with invalid argument. + $services['invalid_argument_service'] = [ + 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService', + 'arguments' => $this->getCollection([ + // Test passing non-strings, too. + 1, + (object) [ + 'type' => 'invalid', + ], + ]), + ]; + + $services['invalid_arguments_service'] = [ + 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService', + 'arguments' => (object) [ + 'type' => 'invalid', + ], + ]; + + // Test service that needs deep-traversal. + $services['service_using_array'] = [ + 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService', + 'arguments' => $this->getCollection([ + $this->getCollection([ + $this->getServiceCall('other.service'), + ]), + $this->getParameterCall('some_private_config'), + ]), + ]; + + $services['service_with_optional_dependency'] = [ + 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService', + 'arguments' => $this->getCollection([ + $this->getServiceCall('service.does_not_exist', ContainerInterface::NULL_ON_INVALID_REFERENCE), + $this->getParameterCall('some_private_config'), + ]), + + ]; + + $services['factory_service'] = [ + 'class' => '\Drupal\service_container\ServiceContainer\ControllerInterface', + 'factory' => [ + $this->getServiceCall('service.provider'), + 'getFactoryMethod', + ], + 'arguments' => $this->getCollection([ + $this->getParameterCall('factory_service_class'), + ]), + ]; + $services['factory_class'] = [ + 'class' => '\Drupal\service_container\ServiceContainer\ControllerInterface', + 'factory' => '\Drupal\Tests\Component\DependencyInjection\MockService::getFactoryMethod', + 'arguments' => [ + '\Drupal\Tests\Component\DependencyInjection\MockService', + [NULL, 'bar'], + ], + 'calls' => [ + [ + 'setContainer', + $this->getCollection([ + $this->getServiceCall('service_container'), + ]), + ], + ], + ]; + + $services['wrong_factory'] = [ + 'class' => '\Drupal\service_container\ServiceContainer\ControllerInterface', + 'factory' => (object) ['I am not a factory, but I pretend to be.'], + ]; + + $services['circular_dependency'] = [ + 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService', + 'arguments' => $this->getCollection([ + $this->getServiceCall('circular_dependency'), + ]), + ]; + $services['synthetic'] = [ + 'synthetic' => TRUE, + ]; + // The file could have been named as a .php file. The reason it is a .data + // file is that SimpleTest tries to load it. SimpleTest does not like such + // fixtures and hence we use a neutral name like .data. + $services['container_test_file_service_test'] = [ + 'class' => '\stdClass', + 'file' => __DIR__ . '/Fixture/container_test_file_service_test_service_function.data', + ]; + + // Test multiple arguments. + $args = []; + for ($i = 0; $i < 12; $i++) { + $services['service_test_instantiation_' . $i] = [ + 'class' => '\Drupal\Tests\Component\DependencyInjection\MockInstantiationService', + // Also test a collection that does not need resolving. + 'arguments' => $this->getCollection($args, FALSE), + ]; + $args[] = 'arg_' . $i; + } + + $services['service_parameter_not_exists'] = [ + 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService', + 'arguments' => $this->getCollection([ + $this->getServiceCall('service.provider'), + $this->getParameterCall('not_exists'), + ]), + ]; + $services['service_dependency_not_exists'] = [ + 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService', + 'arguments' => $this->getCollection([ + $this->getServiceCall('service_not_exists'), + $this->getParameterCall('some_config'), + ]), + ]; + + $services['service_with_parameter_service'] = [ + 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService', + 'arguments' => $this->getCollection([ + $this->getParameterCall('service_from_parameter'), + // Also test deep collections that don't need resolving. + $this->getCollection([ + 1, + ], FALSE), + ]), + ]; + + // To ensure getAlternatives() finds something. + $services['service_not_exists_similar'] = [ + 'synthetic' => TRUE, + ]; + + // Test configurator. + $services['configurator'] = [ + 'synthetic' => TRUE, + ]; + $services['configurable_service'] = [ + 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService', + 'arguments' => [], + 'configurator' => [ + $this->getServiceCall('configurator'), + 'configureService' + ], + ]; + $services['configurable_service_exception'] = [ + 'class' => '\Drupal\Tests\Component\DependencyInjection\MockService', + 'arguments' => [], + 'configurator' => 'configurator_service_test_does_not_exist', + ]; + + $aliases = []; + $aliases['service.provider_alias'] = 'service.provider'; + $aliases['late.service_alias'] = 'late.service'; + + return [ + 'aliases' => $aliases, + 'parameters' => $parameters, + 'services' => $services, + 'frozen' => TRUE, + 'machine_format' => $this->machineFormat, + ]; + } + + /** + * Helper function to return a service definition. + */ + protected function getServiceCall($id, $invalid_behavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) { + return (object) [ + 'type' => 'service', + 'id' => $id, + 'invalidBehavior' => $invalid_behavior, + ]; + } + + /** + * Helper function to return a service definition. + */ + protected function getParameterCall($name) { + return (object) [ + 'type' => 'parameter', + 'name' => $name, + ]; + } + + /** + * Helper function to return a private service definition. + */ + protected function getPrivateServiceCall($id, $service_definition, $shared = FALSE) { + if (!$id) { + $hash = Crypt::hashBase64(serialize($service_definition)); + $id = 'private__' . $hash; + } + return (object) [ + 'type' => 'private_service', + 'id' => $id, + 'value' => $service_definition, + 'shared' => $shared, + ]; + } + + /** + * Helper function to return a machine-optimized collection. + */ + protected function getCollection($collection, $resolve = TRUE) { + return (object) [ + 'type' => 'collection', + 'value' => $collection, + 'resolve' => $resolve, + ]; + } + +} + +/** + * Helper interface to test Container::get() with configurator. + * + * @group DependencyInjection + */ +interface MockConfiguratorInterface { + + /** + * Configures a service. + * + * @param object $service + * The service to configure. + */ + public function configureService($service); + +} + + +/** + * Helper class to test Container::get() method for varying number of parameters. + * + * @group DependencyInjection + */ +class MockInstantiationService { + + /** + * @var mixed[] + */ + protected $arguments; + + /** + * Construct a mock instantiation service. + */ + public function __construct() { + $this->arguments = func_get_args(); + } + + /** + * Return arguments injected into the service. + * + * @return mixed[] + * Return the passed arguments. + */ + public function getArguments() { + return $this->arguments; + } + +} + + +/** + * Helper class to test Container::get() method. + * + * @group DependencyInjection + */ +class MockService { + + /** + * @var \Symfony\Component\DependencyInjection\ContainerInterface + */ + protected $container; + + /** + * @var object + */ + protected $someOtherService; + + /** + * @var string + */ + protected $someParameter; + + /** + * @var string + */ + protected $someOtherParameter; + + /** + * Constructs a MockService object. + * + * @param object $some_other_service + * (optional) Another injected service. + * @param string $some_parameter + * (optional) An injected parameter. + */ + public function __construct($some_other_service = NULL, $some_parameter = NULL) { + if (is_array($some_other_service)) { + $some_other_service = $some_other_service[0]; + } + $this->someOtherService = $some_other_service; + $this->someParameter = $some_parameter; + } + + /** + * Sets the container object. + * + * @param \Symfony\Component\DependencyInjection\ContainerInterface $container + * The container to inject via setter injection. + */ + public function setContainer(ContainerInterface $container) { + $this->container = $container; + } + + /** + * Gets the container object. + * + * @return \Symfony\Component\DependencyInjection\ContainerInterface + * The internally set container. + */ + public function getContainer() { + return $this->container; + } + + /** + * Gets the someOtherService object. + * + * @return object + * The injected service. + */ + public function getSomeOtherService() { + return $this->someOtherService; + } + + /** + * Gets the someParameter property. + * + * @return string + * The injected parameter. + */ + public function getSomeParameter() { + return $this->someParameter; + } + + /** + * Sets the someOtherParameter property. + * + * @param string $some_other_parameter + * The setter injected parameter. + */ + public function setOtherConfigParameter($some_other_parameter) { + $this->someOtherParameter = $some_other_parameter; + } + + /** + * Gets the someOtherParameter property. + * + * @return string + * The injected parameter. + */ + public function getSomeOtherParameter() { + return $this->someOtherParameter; + } + + /** + * Provides a factory method to get a service. + * + * @param string $class + * The class name of the class to instantiate + * @param array $arguments + * (optional) Arguments to pass to the new class. + * + * @return object + * The instantiated service object. + */ + public static function getFactoryMethod($class, $arguments = []) { + $r = new \ReflectionClass($class); + $service = ($r->getConstructor() === NULL) ? $r->newInstance() : $r->newInstanceArgs($arguments); + + return $service; + } + +}