├── .github └── pull_request_template.md ├── .gitignore ├── .php_cs ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── composer.json ├── depfile.yml ├── infection.json.dist ├── phpunit.xml.dist ├── src ├── HttpKernel │ ├── Exception.php │ ├── Exception │ │ ├── ClassNotFoundException.php │ │ └── KernelNotSupportedException.php │ ├── KernelBuilder.php │ ├── KernelConfiguration.php │ └── TestKernel.php └── PHPUnit │ ├── ConfigurableKernel.php │ └── TestKernel.php ├── tests ├── HttpKernel │ ├── Fixtures │ │ ├── BarBundle │ │ │ └── BarBundle.php │ │ ├── CustomConfiguration.php │ │ ├── CustomKernel.php │ │ ├── DummyKernel.php │ │ └── FooBundle │ │ │ ├── DependencyInjection │ │ │ ├── Configuration.php │ │ │ └── FooExtension.php │ │ │ ├── Foo.php │ │ │ ├── FooBundle.php │ │ │ └── Resources │ │ │ └── config │ │ │ └── services.xml │ ├── KernelBuilderTest.php │ ├── KernelConfigurationTest.php │ └── TestKernelTest.php └── PHPUnit │ ├── ConfigurableKernelTest.php │ ├── Fixtures │ ├── CustomKernel.php │ └── FooBundle │ │ ├── DependencyInjection │ │ ├── Configuration.php │ │ └── FooExtension.php │ │ ├── Foo.php │ │ ├── FooBundle.php │ │ └── Resources │ │ └── config │ │ └── services.xml │ └── TestKernelTest.php └── tools └── .gitignore /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.php_cs.cache 2 | /build/ 3 | /composer.lock 4 | /vendor/ 5 | -------------------------------------------------------------------------------- /.php_cs: -------------------------------------------------------------------------------- 1 | in(['src', 'tests']) 5 | ; 6 | 7 | return PhpCsFixer\Config::create() 8 | ->setRules([ 9 | '@PSR2' => true, 10 | 'array_syntax' => ['syntax' => 'short'], 11 | 'blank_line_before_statement' => true, 12 | 'declare_strict_types' => true, 13 | 'native_function_invocation' => true, 14 | 'no_empty_comment' => true, 15 | 'no_empty_phpdoc' => true, 16 | 'no_empty_statement' => true, 17 | 'no_extra_blank_lines' => true, 18 | 'no_extra_consecutive_blank_lines' => true, 19 | 'no_leading_import_slash' => true, 20 | 'no_leading_namespace_whitespace' => true, 21 | 'no_unused_imports' => true, 22 | 'no_useless_else' => true, 23 | 'ordered_class_elements' => true, 24 | 'ordered_imports' => true, 25 | 'phpdoc_add_missing_param_annotation' => ['only_untyped' => true], 26 | 'protected_to_private' => true, 27 | 'strict_comparison' => true, 28 | 'ternary_operator_spaces' => true, 29 | 'ternary_to_null_coalescing' => true, 30 | 'yoda_style' => true, 31 | ]) 32 | ->setFinder($finder) 33 | ; 34 | 35 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | matrix: 4 | include: 5 | - php: 7.1 6 | - php: 7.2 7 | - php: 7.3 8 | env: deps=low 9 | fast_finish: true 10 | 11 | before_install: 12 | - phpenv config-rm xdebug.ini || echo "XDebug is not enabled" 13 | 14 | install: 15 | - if [[ $deps = low ]]; then make update-min; else make install; fi 16 | 17 | script: 18 | - if [[ $deps = low ]]; then make test-min; else make test; fi 19 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at oss@zalas.pl. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When contributing to this repository send a new pull request. 4 | If your change is big or complex, or you simply want to suggest an improvement, 5 | please discuss the change you wish to make via an issue. 6 | 7 | Please note we have a [code of conduct](CODE_OF_CONDUCT.md). Please follow it in all your interactions with the project. 8 | 9 | ## Pull Request Process 10 | 11 | * Provide good commit messages describing what you've done. 12 | * Provide tests for any code you write. 13 | * Make sure all tests are passing with `make test`. 14 | * Run infection to see if you haven't covered some important behaviour. 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Jakub Zalas 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | default: build 2 | 3 | build: install test 4 | .PHONY: build 5 | 6 | install: 7 | composer install 8 | .PHONY: install 9 | 10 | update: 11 | composer update 12 | .PHONY: update 13 | 14 | update-min: 15 | composer update --prefer-stable --prefer-lowest 16 | .PHONY: update-min 17 | 18 | update-no-dev: 19 | composer update --prefer-stable --no-dev 20 | .PHONY: update-no-dev 21 | 22 | test: vendor cs deptrac phpunit infection 23 | .PHONY: test 24 | 25 | test-min: update-min cs deptrac phpunit infection 26 | .PHONY: test-min 27 | 28 | cs: tools/php-cs-fixer 29 | PHP_CS_FIXER_IGNORE_ENV=1 tools/php-cs-fixer --dry-run --allow-risky=yes --no-interaction --ansi fix 30 | .PHONY: cs 31 | 32 | cs-fix: tools/php-cs-fixer 33 | PHP_CS_FIXER_IGNORE_ENV=1 tools/php-cs-fixer --allow-risky=yes --no-interaction --ansi fix 34 | .PHONY: cs-fix 35 | 36 | deptrac: tools/deptrac 37 | tools/deptrac --no-interaction --ansi --formatter-graphviz-display=0 38 | .PHONY: deptrac 39 | 40 | infection: tools/infection tools/infection.pubkey 41 | phpdbg -qrr ./tools/infection --no-interaction --formatter=progress --min-msi=63 --min-covered-msi=63 --only-covered --ansi 42 | .PHONY: infection 43 | 44 | phpunit: tools/phpunit 45 | tools/phpunit 46 | .PHONY: phpunit 47 | 48 | tools: tools/php-cs-fixer tools/deptrac tools/infection tools/box 49 | .PHONY: tools 50 | 51 | clean: 52 | rm -rf build 53 | .PHONY: clean 54 | 55 | vendor: install 56 | 57 | vendor/bin/phpunit: install 58 | 59 | tools/phpunit: vendor/bin/phpunit 60 | ln -sf ../vendor/bin/phpunit tools/phpunit 61 | 62 | tools/php-cs-fixer: 63 | curl -Ls http://cs.sensiolabs.org/download/php-cs-fixer-v2.phar -o tools/php-cs-fixer && chmod +x tools/php-cs-fixer 64 | 65 | tools/deptrac: 66 | curl -Ls http://get.sensiolabs.de/deptrac.phar -o tools/deptrac && chmod +x tools/deptrac 67 | 68 | tools/infection: tools/infection.pubkey 69 | curl -Ls https://github.com/infection/infection/releases/download/0.9.0-beta.2/infection.phar -o tools/infection && chmod +x tools/infection 70 | 71 | tools/infection.pubkey: 72 | curl -Ls https://github.com/infection/infection/releases/download/0.9.0-beta.2/infection.phar.pubkey -o tools/infection.pubkey 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bundle Test 2 | 3 | [![Build Status](https://travis-ci.org/jakzal/bundle-test.svg?branch=master)](https://travis-ci.org/jakzal/bundle-test) 4 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/jakzal/bundle-test/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/jakzal/bundle-test/?branch=master) 5 | 6 | Tools for testing Symfony Bundles. 7 | 8 | **This is work in progress**. 9 | 10 | ## Introduction 11 | 12 | Symfony made it easy to test bundles with a Kernel booted thanks to the `KernelTestCase` and the `MicrokernelTrait`. 13 | However, there's no convenient way to recreate scenarios of bundles configured in various ways. 14 | One solution would be to create several test kernels with different configurations. This is boring, tedious and in most 15 | cases not maintainable. Also, changing the configuration on the fly, per test case, would mean having to deal 16 | with manual cache clearing before every test case is executed. 17 | 18 | The Symfony2Extension Behat extension "suffers" from similar issues. "suffers" is put in quotes as those tools 19 | were simply designed for different use cases. 20 | 21 | This library offers an alternative - a configurable test kernel that automatically puts each configuration in its own scope. 22 | Its aim is to make writing integration tests for Symfony Bundles easier and more high level. 23 | 24 | ## PHPUnit 25 | 26 | ```php 27 | namespace Acme\Tests; 28 | 29 | use Acme\FooBundle; 30 | use PHPUnit\Framework\TestCase; 31 | use Symfony\Bundle\FrameworkBundle\FrameworkBundle; 32 | use Zalas\BundleTest\PHPUnit\ConfigurableKernel; 33 | use Zalas\BundleTest\PHPUnit\ContainerAssertions; 34 | 35 | class FooBundleTest extends TestCase 36 | { 37 | use ConfigurableKernel; 38 | use ContainerAssertions; 39 | 40 | public function testTheFooServiceIsDisabledByDefault() 41 | { 42 | $this->givenBundlesAreEnabled([new FrameworkBundle(), new FooBundle()]); 43 | 44 | $kernel = self::bootKernel(); 45 | 46 | $this->assertServiceIsNotDefined('foo', $kernel->getContainer()); 47 | } 48 | 49 | public function testTheFooServiceIsEnabledInConfiguration() 50 | { 51 | $this->givenBundlesAreEnabled([new FrameworkBundle(), new FooBundle()]); 52 | $this->givenBundleConfiguration('foo', ['enabled' => true]); 53 | 54 | $kernel = self::bootKernel(); 55 | 56 | $this->assertServiceIsDefined('foo', Foo::class, $kernel->getContainer()); 57 | } 58 | } 59 | ``` 60 | 61 | ### ConfigurableKernel 62 | 63 | The `Zalas\BundleTest\PHPUnit\ConfigurableKernel` trait provides a convenient way to configure and then boot the 64 | Symfony kernel in tests. 65 | 66 | Here's a full example of available methods: 67 | 68 | ```php 69 | namespace Acme\Tests; 70 | 71 | use PHPUnit\Framework\TestCase; 72 | use Zalas\BundleTest\PHPUnit\ConfigurableKernel; 73 | 74 | class FooTest extends TestCase 75 | { 76 | use ConfigurableKernel; 77 | 78 | public function testItEnablesTheFooService() 79 | { 80 | $this->givenEnvironment('foo') 81 | ->givenDebugIsEnabled() 82 | ->givenBundleIsEnabled([new FooBundle()]) 83 | ->givenBundleConfiguration('foo', ['enabled' => true, 'bar' => 'baz']) 84 | ->givenPublicServiceId('foo') 85 | ->givenKernel(CustomKernel::class) 86 | ->givenTempDir('/tmp'); 87 | 88 | $kernel = self::bootKernel(); 89 | 90 | $this->assertTrue($kernel->getContainer()->has('foo')); 91 | } 92 | } 93 | ``` 94 | 95 | A new kernel configuration is generated for each set of given* calls. If another test case uses the same 96 | given* calls the kernel cache will be reused. Otherwise a new one's generated. 97 | 98 | ## Behat 99 | 100 | ```php 101 | use Acme\FooBundle; 102 | use Behat\Behat\Context; 103 | use Symfony\Bundle\FrameworkBundle\FrameworkBundle; 104 | 105 | class FooContext implements Context 106 | { 107 | private $applicationRunner; 108 | 109 | public function __construct() 110 | { 111 | $this->applicationRunner = new ApplicationRunner(); 112 | $this->applicationRunner->enableBundle(new FrameworkBundle()); 113 | } 114 | 115 | /** 116 | * @Given the :bundle bundle is enabled 117 | */ 118 | public function givenTheBundleIsEnabled(string $bundle) 119 | { 120 | $class = 'Foo\\'.$bundle; 121 | if (!class_exists($class)) { 122 | throw new \LogicException(sprintf('The bundle "%s" does not exist.', $class)); 123 | } 124 | 125 | $this->applicationRunner->enableBundle(new $class()); 126 | } 127 | 128 | /** 129 | * @When the :event event is dispatched with :userId 130 | */ 131 | public function whenTheEventIsDispatchedWithUserId(string $event, string $userId) 132 | { 133 | $supportedEvents = [ 134 | 'user.verified' => 'Foo\Event\UserVerifiedEvent', 135 | ]; 136 | 137 | if (!isset($supportedEvents[$event])) { 138 | throw new \LogicException(sprintf('The event "%s" is not recognised.', $event)); 139 | } 140 | 141 | $eventClass = $supportedEvents[$event]; 142 | 143 | $this->applicationRunner->dispatchEvent($event, new $eventClass($userId)); 144 | } 145 | 146 | /** 147 | * @Then the :userId user should enabled 148 | */ 149 | public function thenTheUserShouldBeEnabled(string $userId) 150 | { 151 | $user = $this->applicationRunner->getService('user.repository')->find($userId); 152 | 153 | if (!$user->isEnabled()) { 154 | throw new \LogicException(sprintf('The user %s should be enabled', $userId)); 155 | } 156 | } 157 | } 158 | ``` 159 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zalas/bundle-test", 3 | "description": "Tools for testing Symfony Bundles", 4 | "type": "library", 5 | "require": { 6 | "php": "^7.1", 7 | "symfony/http-kernel": "^3.4 || ^4.0", 8 | "symfony/dependency-injection": "^3.4 || ^4.0", 9 | "symfony/config": "^3.4 || ^4.0" 10 | }, 11 | "require-dev": { 12 | "phpunit/phpunit": "^7.0", 13 | "zalas/phpunit-globals": "^1.0" 14 | }, 15 | "autoload": { 16 | "psr-4": { "Zalas\\BundleTest\\": "src/" } 17 | }, 18 | "autoload-dev": { 19 | "psr-4": { "Zalas\\BundleTest\\Tests\\": "tests/" } 20 | }, 21 | "license": "MIT", 22 | "authors": [ 23 | { 24 | "name": "Jakub Zalas", 25 | "email": "jakub@zalas.pl" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /depfile.yml: -------------------------------------------------------------------------------- 1 | paths: 2 | - ./src 3 | exclude_files: ~ 4 | layers: 5 | - name: HttpKernel 6 | collectors: 7 | - type: className 8 | regex: ^Zalas\\BundleTest\\HttpKernel\\.* 9 | - name: PHPUnit 10 | collectors: 11 | - type: className 12 | regex: ^Zalas\\BundleTest\\PHPUnit\\.* 13 | - name: Symfony Config 14 | collectors: 15 | - type: className 16 | regex: ^Symfony\\Component\\Config\\.* 17 | - name: Symfony DependencyInjection 18 | collectors: 19 | - type: className 20 | regex: ^Symfony\\Component\\DependencyInjection\\.* 21 | - name: Symfony HttpKernel 22 | collectors: 23 | - type: className 24 | regex: ^Symfony\\Component\\HttpKernel\\.* 25 | - name: PHPUnit Framework 26 | collectors: 27 | - type: className 28 | regex: ^PHPUnit\\Framework\\.* 29 | - name: Other Vendors 30 | collectors: 31 | - type: bool 32 | must: 33 | # must be outside of global namespace 34 | - type: className 35 | regex: '[\\]+' 36 | must_not: 37 | # must not be one of the known vendors 38 | - type: className 39 | regex: ^Zalas\\BundleTest\\.* 40 | - type: className 41 | regex: ^PHPUnit\\Framework\\.* 42 | - type: className 43 | regex: ^Symfony\\Component\\Config\\.* 44 | - type: className 45 | regex: ^Symfony\\Component\\DependencyInjection\\.* 46 | - type: className 47 | regex: ^Symfony\\Component\\HttpKernel\\.* 48 | ruleset: 49 | HttpKernel: 50 | - Symfony Config 51 | - Symfony DependencyInjection 52 | - Symfony HttpKernel 53 | PHPUnit: 54 | - HttpKernel 55 | - PHPUnit Framework 56 | - Symfony DependencyInjection 57 | - Symfony HttpKernel 58 | -------------------------------------------------------------------------------- /infection.json.dist: -------------------------------------------------------------------------------- 1 | { 2 | "timeout": 2, 3 | "source": { 4 | "directories": [ 5 | "src" 6 | ] 7 | }, 8 | "logs": { 9 | "text": "build/infection-log.txt" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | tests 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | src 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/HttpKernel/Exception.php: -------------------------------------------------------------------------------- 1 | configuration = new KernelConfiguration($namespace); 26 | } 27 | 28 | public function createKernel(): KernelInterface 29 | { 30 | return new $this->kernelClass($this->configuration); 31 | } 32 | 33 | public function bootKernel(): KernelInterface 34 | { 35 | $kernel = $this->createKernel(); 36 | $kernel->boot(); 37 | 38 | return $kernel; 39 | } 40 | 41 | public function withEnvironment(string $environment): self 42 | { 43 | $this->configuration = $this->configuration->withEnvironment($environment); 44 | 45 | return $this; 46 | } 47 | 48 | public function withDebug(bool $debug): self 49 | { 50 | $this->configuration = $this->configuration->withDebug($debug); 51 | 52 | return $this; 53 | } 54 | 55 | public function withBundles(array $bundles): self 56 | { 57 | $this->configuration = $this->configuration->withBundles($bundles); 58 | 59 | return $this; 60 | } 61 | 62 | public function withBundle(BundleInterface $bundle): self 63 | { 64 | $this->configuration = $this->configuration->withBundle($bundle); 65 | 66 | return $this; 67 | } 68 | 69 | public function withBundleConfiguration(string $extensionName, array $configuration): self 70 | { 71 | $this->configuration = $this->configuration->withBundleConfiguration($extensionName, $configuration); 72 | 73 | return $this; 74 | } 75 | 76 | public function withPublicService(string $serviceId): self 77 | { 78 | $this->configuration = $this->configuration->withPublicServiceId($serviceId); 79 | 80 | return $this; 81 | } 82 | 83 | public function withPublicServices(array $serviceIds): self 84 | { 85 | $this->configuration = $this->configuration->withPublicServiceIds($serviceIds); 86 | 87 | return $this; 88 | } 89 | 90 | public function withTempDir(string $tempDir): self 91 | { 92 | $this->configuration = $this->configuration->withTempDir($tempDir); 93 | 94 | return $this; 95 | } 96 | 97 | public function withKernelClass(string $kernelClass): self 98 | { 99 | $this->guardKernelClass($kernelClass); 100 | 101 | $this->kernelClass = $kernelClass; 102 | 103 | return $this; 104 | } 105 | 106 | private function guardKernelClass(string $kernelClass): void 107 | { 108 | if (!\class_exists($kernelClass)) { 109 | throw new ClassNotFoundException($kernelClass); 110 | } 111 | 112 | if (TestKernel::class !== $kernelClass && !\in_array(TestKernel::class, \class_parents($kernelClass))) { 113 | throw new KernelNotSupportedException($kernelClass, TestKernel::class); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/HttpKernel/KernelConfiguration.php: -------------------------------------------------------------------------------- 1 | namespace = $namespace; 51 | } 52 | 53 | public function withEnvironment(string $environment): self 54 | { 55 | $config = clone $this; 56 | $config->environment = $environment; 57 | 58 | return $config; 59 | } 60 | 61 | public function withDebug(bool $debug): self 62 | { 63 | $config = clone $this; 64 | $config->debug = $debug; 65 | 66 | return $config; 67 | } 68 | 69 | public function withBundle(BundleInterface $bundle): self 70 | { 71 | $config = clone $this; 72 | $config->bundles[] = $bundle; 73 | 74 | return $config; 75 | } 76 | 77 | /** 78 | * @param BundleInterface[] $bundles 79 | */ 80 | public function withBundles(array $bundles): self 81 | { 82 | return \array_reduce($bundles, function (self $config, BundleInterface $bundle) { 83 | return $config->withBundle($bundle); 84 | }, $this); 85 | } 86 | 87 | public function withBundleConfiguration(string $extensionName, array $configuration): self 88 | { 89 | $config = clone $this; 90 | $config->bundleConfigurations[$extensionName] = \array_merge_recursive( 91 | $config->bundleConfigurations[$extensionName] ?? [], 92 | $configuration 93 | ); 94 | 95 | return $config; 96 | } 97 | 98 | public function withTempDir(string $tempDir): self 99 | { 100 | $config = clone $this; 101 | $config->tempDir = $tempDir; 102 | 103 | return $config; 104 | } 105 | 106 | public function withPublicServiceId(string $serviceId): self 107 | { 108 | $config = clone $this; 109 | $config->publicServiceIds[] = $serviceId; 110 | 111 | return $config; 112 | } 113 | 114 | /** 115 | * @param string[] $serviceIds 116 | */ 117 | public function withPublicServiceIds(array $serviceIds): self 118 | { 119 | return \array_reduce($serviceIds, function (self $config, string $serviceId) { 120 | return $config->withPublicServiceId($serviceId); 121 | }, $this); 122 | } 123 | 124 | /** 125 | * Computes an unique identifier of the current configuration. 126 | * 127 | * Cache will be scoped (so also refreshed) based on this value. 128 | */ 129 | public function getHash(): string 130 | { 131 | return \sha1(\serialize([ 132 | $this->getEnvironment(), 133 | $this->isDebug(), 134 | \array_map(function (BundleInterface $bundle) { 135 | return \get_class($bundle); 136 | }, $this->getBundles()), 137 | $this->getAllBundleConfigurations(), 138 | $this->tempDir, 139 | $this->namespace, 140 | $this->getPublicServiceIds(), 141 | ])); 142 | } 143 | 144 | public function getEnvironment(): string 145 | { 146 | return $this->environment; 147 | } 148 | 149 | public function isDebug(): bool 150 | { 151 | return $this->debug; 152 | } 153 | 154 | /** 155 | * @return BundleInterface[] 156 | */ 157 | public function getBundles(): array 158 | { 159 | return $this->bundles; 160 | } 161 | 162 | public function getAllBundleConfigurations(): array 163 | { 164 | return $this->bundleConfigurations; 165 | } 166 | 167 | public function getTempDir(): string 168 | { 169 | return \sprintf('%s/%s/%s', $this->tempDir ?? \sys_get_temp_dir(), $this->namespace, $this->getHash()); 170 | } 171 | 172 | final public function getCacheDir(): string 173 | { 174 | return \sprintf('%s/var/cache/%s', $this->getTempDir(), $this->environment); 175 | } 176 | 177 | final public function getLogDir(): string 178 | { 179 | return \sprintf('%s/var/log', $this->getTempDir()); 180 | } 181 | 182 | /** 183 | * @return string[] 184 | */ 185 | public function getPublicServiceIds(): array 186 | { 187 | return $this->publicServiceIds; 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/HttpKernel/TestKernel.php: -------------------------------------------------------------------------------- 1 | getEnvironment(), $configuration->isDebug()); 22 | 23 | $this->configuration = $configuration; 24 | } 25 | 26 | /** 27 | * @return BundleInterface[] 28 | */ 29 | public function registerBundles() 30 | { 31 | return $this->configuration->getBundles(); 32 | } 33 | 34 | public function getCacheDir() 35 | { 36 | return $this->configuration->getCacheDir(); 37 | } 38 | 39 | public function getLogDir() 40 | { 41 | return $this->configuration->getLogDir(); 42 | } 43 | 44 | public function registerContainerConfiguration(LoaderInterface $loader) 45 | { 46 | $loader->load(function (ContainerBuilder $container) { 47 | $this->loadExtensionConfigurations($container); 48 | }); 49 | $loader->load(function (ContainerBuilder $container) { 50 | $this->makeServicesPublic($container); 51 | }); 52 | } 53 | 54 | private function loadExtensionConfigurations(ContainerBuilder $container) 55 | { 56 | foreach ($this->configuration->getAllBundleConfigurations() as $extension => $configuration) { 57 | $container->loadFromExtension($extension, $configuration); 58 | } 59 | } 60 | 61 | private function makeServicesPublic(ContainerBuilder $container) 62 | { 63 | $container->addCompilerPass( 64 | new class($this->configuration) implements CompilerPassInterface { 65 | private $configuration; 66 | 67 | public function __construct(KernelConfiguration $configuration) 68 | { 69 | $this->configuration = $configuration; 70 | } 71 | 72 | public function process(ContainerBuilder $container) 73 | { 74 | foreach ($this->configuration->getPublicServiceIds() as $serviceId) { 75 | if ($container->hasDefinition($serviceId)) { 76 | $container->getDefinition($serviceId)->setPublic(true); 77 | } 78 | if ($container->hasAlias($serviceId)) { 79 | $container->getAlias($serviceId)->setPublic(true); 80 | } 81 | } 82 | } 83 | } 84 | ); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/PHPUnit/ConfigurableKernel.php: -------------------------------------------------------------------------------- 1 | getTestNamespace()); 27 | static::$kernelBuilder->withKernelClass(static::getKernelClass()); 28 | 29 | if (null !== $environment = $_ENV['APP_ENV'] ?? $_SERVER['APP_ENV'] ?? null) { 30 | static::$kernelBuilder->withEnvironment((string) $environment); 31 | } 32 | 33 | if (null !== $debug = $_ENV['APP_DEBUG'] ?? $_SERVER['APP_DEBUG'] ?? null) { 34 | static::$kernelBuilder->withDebug((bool) $debug); 35 | } 36 | } 37 | 38 | protected static function createKernelBuilder(array $options): KernelBuilder 39 | { 40 | if (null !== $environment = $options['environment'] ?? null) { 41 | static::$kernelBuilder->withEnvironment((string) $environment); 42 | } 43 | 44 | if (null !== $isDebug = $options['debug'] ?? null) { 45 | static::$kernelBuilder->withDebug((bool) $isDebug); 46 | } 47 | 48 | if (null !== $kernelClass = $options['kernel_class'] ?? null) { 49 | static::$kernelBuilder->withKernelClass((string) $kernelClass); 50 | } 51 | 52 | return static::$kernelBuilder; 53 | } 54 | 55 | /** 56 | * Scopes the current test case in the working directory (temp directory). 57 | */ 58 | protected function getTestNamespace(): string 59 | { 60 | return \str_replace('\\', '', __CLASS__); 61 | } 62 | 63 | protected function givenEnvironment(string $environment): self 64 | { 65 | $this->ensureKernelNotBooted(); 66 | 67 | static::$kernelBuilder->withEnvironment($environment); 68 | 69 | return $this; 70 | } 71 | 72 | protected function givenDebugIsEnabled(): self 73 | { 74 | $this->ensureKernelNotBooted(); 75 | 76 | static::$kernelBuilder->withDebug(true); 77 | 78 | return $this; 79 | } 80 | 81 | protected function givenDebugIsDisabled(): self 82 | { 83 | $this->ensureKernelNotBooted(); 84 | 85 | static::$kernelBuilder->withDebug(false); 86 | 87 | return $this; 88 | } 89 | 90 | protected function givenKernel(string $kernelClass): self 91 | { 92 | $this->ensureKernelNotBooted(); 93 | 94 | static::$kernelBuilder->withKernelClass($kernelClass); 95 | 96 | return $this; 97 | } 98 | 99 | protected function givenBundleConfiguration($extensionName, array $configuration): self 100 | { 101 | $this->ensureKernelNotBooted(); 102 | 103 | static::$kernelBuilder->withBundleConfiguration($extensionName, $configuration); 104 | 105 | return $this; 106 | } 107 | 108 | protected function givenPublicServiceId(string $serviceId): self 109 | { 110 | $this->ensureKernelNotBooted(); 111 | 112 | static::$kernelBuilder->withPublicService($serviceId); 113 | 114 | return $this; 115 | } 116 | 117 | protected function givenPublicServiceIds(array $serviceIds): self 118 | { 119 | $this->ensureKernelNotBooted(); 120 | 121 | static::$kernelBuilder->withPublicServices($serviceIds); 122 | 123 | return $this; 124 | } 125 | 126 | /** 127 | * @param BundleInterface[] $bundles 128 | */ 129 | protected function givenEnabledBundles(array $bundles): self 130 | { 131 | $this->ensureKernelNotBooted(); 132 | 133 | static::$kernelBuilder->withBundles($bundles); 134 | 135 | return $this; 136 | } 137 | 138 | protected function givenEnabledBundle(BundleInterface $bundle): self 139 | { 140 | $this->ensureKernelNotBooted(); 141 | 142 | static::$kernelBuilder->withBundle($bundle); 143 | 144 | return $this; 145 | } 146 | 147 | protected function givenTempDir(string $tempDir): self 148 | { 149 | $this->ensureKernelNotBooted(); 150 | 151 | static::$kernelBuilder->withTempDir($tempDir); 152 | 153 | return $this; 154 | } 155 | 156 | private function ensureKernelNotBooted(): void 157 | { 158 | if (null !== static::$kernel) { 159 | throw new \LogicException('Configuration cannot be changed once kernel is booted.'); 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/PHPUnit/TestKernel.php: -------------------------------------------------------------------------------- 1 | bootKernel(); 42 | } 43 | 44 | /** 45 | * Creates a test kernel. 46 | * 47 | * Compared to the original `KernelTestCase` this method offers an additional `kernel_class` option due to a more 48 | * dynamic nature of this trait. 49 | * 50 | * Options: 51 | * 52 | * * environment 53 | * * debug 54 | * * kernel_class 55 | */ 56 | protected static function createKernel(array $options = []): KernelInterface 57 | { 58 | return self::createKernelBuilder($options)->createKernel(); 59 | } 60 | 61 | protected static function createKernelBuilder(array $options): KernelBuilder 62 | { 63 | $builder = new KernelBuilder(); 64 | $builder->withKernelClass($options['kernel_class'] ?? static::getKernelClass()); 65 | $builder->withEnvironment($options['environment'] ?? $_ENV['APP_ENV'] ?? $_SERVER['APP_ENV'] ?? 'test'); 66 | $builder->withDebug((bool) ($options['debug'] ?? $_ENV['APP_DEBUG'] ?? $_SERVER['APP_DEBUG'] ?? true)); 67 | 68 | return $builder; 69 | } 70 | 71 | /** 72 | * @after 73 | */ 74 | protected static function ensureKernelShutdown(): void 75 | { 76 | if (null !== static::$kernel) { 77 | $container = static::$kernel->getContainer(); 78 | static::$kernel->shutdown(); 79 | if ($container instanceof ResettableContainerInterface) { 80 | $container->reset(); 81 | } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /tests/HttpKernel/Fixtures/BarBundle/BarBundle.php: -------------------------------------------------------------------------------- 1 | root('zalas_foo'); 15 | 16 | // canBeEnabled() is broken in older Symfony versions 17 | $rootNode 18 | ->addDefaultsIfNotSet() 19 | ->treatFalseLike(['enabled' => false]) 20 | ->treatTrueLike(['enabled' => true]) 21 | ->treatNullLike(['enabled' => true]) 22 | ->beforeNormalization() 23 | ->ifArray() 24 | ->then(function ($v) { 25 | $v['enabled'] = $v['enabled'] ?? !empty($v); 26 | 27 | return $v; 28 | }) 29 | ->end() 30 | ->children() 31 | ->booleanNode('enabled') 32 | ->defaultFalse(); 33 | 34 | return $treeBuilder; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/HttpKernel/Fixtures/FooBundle/DependencyInjection/FooExtension.php: -------------------------------------------------------------------------------- 1 | processConfiguration(new Configuration(), $configs); 16 | 17 | if ($config['enabled']) { 18 | $loader = new XmlFileLoader($container, new FileLocator(\dirname(__DIR__).'/Resources/config')); 19 | $loader->load('services.xml'); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/HttpKernel/Fixtures/FooBundle/Foo.php: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /tests/HttpKernel/KernelBuilderTest.php: -------------------------------------------------------------------------------- 1 | expectException(ClassNotFoundException::class); 22 | 23 | $builder = new KernelBuilder(); 24 | $builder->withKernelClass('Foo\\Bar\\Baz'); 25 | } 26 | 27 | public function test_it_throws_an_exception_if_class_is_not_a_test_kernel() 28 | { 29 | $this->expectException(KernelNotSupportedException::class); 30 | 31 | $builder = new KernelBuilder(); 32 | $builder->withKernelClass(DummyKernel::class); 33 | } 34 | 35 | public function test_bootKernel_creates_the_test_kernel_with_defaults() 36 | { 37 | $builder = new KernelBuilder(); 38 | 39 | $kernel = $builder->bootKernel(); 40 | 41 | $this->assertInstanceOf(TestKernel::class, $kernel); 42 | $this->assertSame('test', $kernel->getEnvironment()); 43 | $this->assertTrue($kernel->isDebug()); 44 | } 45 | 46 | public function test_it_creates_the_configured_kernel() 47 | { 48 | $builder = new KernelBuilder(); 49 | $builder->withKernelClass(CustomKernel::class); 50 | 51 | $kernel = $builder->bootKernel(); 52 | 53 | $this->assertInstanceOf(CustomKernel::class, $kernel); 54 | $this->assertSame('test', $kernel->getEnvironment()); 55 | $this->assertTrue($kernel->isDebug()); 56 | } 57 | 58 | public function test_bootKernel_creates_the_test_kernel_for_given_environment_and_debug_flag() 59 | { 60 | $builder = new KernelBuilder(); 61 | $builder->withEnvironment('foo'); 62 | $builder->withDebug(false); 63 | 64 | $kernel = $builder->bootKernel(); 65 | 66 | $this->assertSame('foo', $kernel->getEnvironment()); 67 | $this->assertFalse($kernel->isDebug()); 68 | } 69 | 70 | public function test_bootKernel_boots_the_created_kernel() 71 | { 72 | $builder = new KernelBuilder(); 73 | 74 | $kernel = $builder->bootKernel(); 75 | 76 | $this->assertNotNull($kernel->getContainer()); 77 | } 78 | 79 | public function test_createKernel_creates_the_test_kernel_with_defaults() 80 | { 81 | $builder = new KernelBuilder(); 82 | 83 | $kernel = $builder->createKernel(); 84 | 85 | $this->assertInstanceOf(TestKernel::class, $kernel); 86 | $this->assertSame('test', $kernel->getEnvironment()); 87 | $this->assertTrue($kernel->isDebug()); 88 | } 89 | 90 | public function test_createKernel_creates_the_test_kernel_for_given_environment_and_debug_flag() 91 | { 92 | $builder = new KernelBuilder(); 93 | $builder->withKernelClass(CustomKernel::class); 94 | $builder->withEnvironment('foo'); 95 | $builder->withDebug(false); 96 | 97 | $kernel = $builder->createKernel(); 98 | 99 | $this->assertInstanceOf(CustomKernel::class, $kernel); 100 | $this->assertSame('foo', $kernel->getEnvironment()); 101 | $this->assertFalse($kernel->isDebug()); 102 | } 103 | 104 | public function test_it_enables_bundles() 105 | { 106 | $builder = new KernelBuilder(); 107 | $builder->withBundles([new FooBundle()]); 108 | 109 | $kernel = $builder->bootKernel(); 110 | $bundles = $kernel->getBundles(); 111 | 112 | $this->assertCount(1, $bundles, 'One bundle was registered.'); 113 | $this->assertArrayHasKey('FooBundle', $bundles, 'The "FooBundle" was registered.'); 114 | $this->assertInstanceOf(FooBundle::class, $bundles['FooBundle'], 'The "FooBundle" was registered.'); 115 | } 116 | 117 | public function test_it_enables_a_bundle() 118 | { 119 | $builder = new KernelBuilder(); 120 | $builder->withBundle(new FooBundle()); 121 | 122 | $kernel = $builder->bootKernel(); 123 | $bundles = $kernel->getBundles(); 124 | 125 | $this->assertCount(1, $bundles, 'One bundle was registered.'); 126 | $this->assertArrayHasKey('FooBundle', $bundles, 'The "FooBundle" was registered.'); 127 | $this->assertInstanceOf(FooBundle::class, $bundles['FooBundle'], 'The "FooBundle" was registered.'); 128 | } 129 | 130 | public function test_it_configures_the_bundle() 131 | { 132 | $builder = new KernelBuilder(); 133 | $builder->withBundle(new FooBundle()); 134 | $builder->withBundleConfiguration('foo', ['enabled' => true]); 135 | $builder->withPublicService(Foo::class); 136 | 137 | $kernel = $builder->bootKernel(); 138 | 139 | $this->assertTrue($kernel->getContainer()->has(Foo::class)); 140 | } 141 | 142 | public function test_it_exposes_private_services_as_public() 143 | { 144 | $builder = new KernelBuilder(); 145 | $builder->withBundle(new FooBundle()); 146 | $builder->withBundleConfiguration('foo', ['enabled' => true]); 147 | $builder->withPublicServices([Foo::class]); 148 | 149 | $kernel = $builder->bootKernel(); 150 | 151 | $this->assertTrue($kernel->getContainer()->has(Foo::class)); 152 | } 153 | 154 | public function test_it_changes_the_temp_dir() 155 | { 156 | $builder = new KernelBuilder(); 157 | $builder->withTempDir($tempDir = \sys_get_temp_dir().'/KernelBuilderTest'); 158 | 159 | $kernel = $builder->bootKernel(); 160 | 161 | $this->assertStringStartsWith($tempDir, $kernel->getCacheDir()); 162 | } 163 | 164 | public function test_it_uses_the_default_namespace_if_not_set() 165 | { 166 | $builder = new KernelBuilder(); 167 | 168 | $kernel = $builder->bootKernel(); 169 | 170 | $this->assertContains(KernelConfiguration::DEFAULT_NAMESPACE, $kernel->getCacheDir()); 171 | } 172 | 173 | public function test_it_changes_the_namespace() 174 | { 175 | $builder = new KernelBuilder('FooBarBaz'); 176 | 177 | $kernel = $builder->bootKernel(); 178 | 179 | $this->assertContains('FooBarBaz', $kernel->getCacheDir()); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /tests/HttpKernel/KernelConfigurationTest.php: -------------------------------------------------------------------------------- 1 | withEnvironment('foo'); 17 | 18 | $this->assertNotSame($config, $newConfig); 19 | $this->assertSame(KernelConfiguration::DEFAULT_ENVIRONMENT, $config->getEnvironment()); 20 | $this->assertSame('foo', $newConfig->getEnvironment()); 21 | } 22 | 23 | public function test_a_new_object_is_created_when_debug_flag_is_changed() 24 | { 25 | $config = new KernelConfiguration(); 26 | $newConfig = $config->withDebug(false); 27 | 28 | $this->assertNotSame($config, $newConfig); 29 | $this->assertTrue($config->isDebug()); 30 | $this->assertFalse($newConfig->isDebug()); 31 | } 32 | 33 | public function test_a_new_object_is_created_when_a_bundle_is_added() 34 | { 35 | $fooBundle = new FooBundle(); 36 | 37 | $config = new KernelConfiguration(); 38 | $newConfig = $config->withBundle($fooBundle); 39 | 40 | $this->assertNotSame($config, $newConfig); 41 | $this->assertSame([], $config->getBundles()); 42 | $this->assertSame([$fooBundle], $newConfig->getBundles()); 43 | } 44 | 45 | public function test_a_new_object_is_created_when_multiple_bundles_are_added() 46 | { 47 | $fooBundle = new FooBundle(); 48 | $barBundle = new BarBundle(); 49 | 50 | $config = new KernelConfiguration(); 51 | $newConfig = $config->withBundles([$fooBundle, $barBundle]); 52 | 53 | $this->assertNotSame($config, $newConfig); 54 | $this->assertSame([], $config->getBundles()); 55 | $this->assertSame([$fooBundle, $barBundle], $newConfig->getBundles()); 56 | } 57 | 58 | public function test_bundles_are_added_to_the_previously_added_ones() 59 | { 60 | $fooBundle = new FooBundle(); 61 | $barBundle = new BarBundle(); 62 | 63 | $config = (new KernelConfiguration()) 64 | ->withBundle($fooBundle) 65 | ->withBundle($barBundle); 66 | $otherConfig = (new KernelConfiguration()) 67 | ->withBundles([$fooBundle]) 68 | ->withBundles([$barBundle]); 69 | 70 | $this->assertSame([$fooBundle, $barBundle], $config->getBundles()); 71 | $this->assertSame([$fooBundle, $barBundle], $otherConfig->getBundles()); 72 | } 73 | 74 | public function test_a_new_object_is_created_when_a_bundle_is_configured() 75 | { 76 | $config = new KernelConfiguration(); 77 | $newConfig = (new KernelConfiguration()) 78 | ->withBundleConfiguration('foo', ['enabled' => true]) 79 | ->withBundleConfiguration('bar', ['enabled' => false]); 80 | 81 | $this->assertNotSame($config, $newConfig); 82 | $this->assertSame([], $config->getAllBundleConfigurations()); 83 | $this->assertSame(['foo' => ['enabled' => true], 'bar' => ['enabled' => false]], $newConfig->getAllBundleConfigurations()); 84 | } 85 | 86 | public function test_bundle_configurations_are_appended_to_the_previously_added_ones() 87 | { 88 | $config = (new KernelConfiguration()) 89 | ->withBundleConfiguration('foo', ['enabled' => true, 'foos' => 'foo1']) 90 | ->withBundleConfiguration('foo', ['foos' => 'foo2']); 91 | 92 | $this->assertSame(['foo' => ['enabled' => true, 'foos' => ['foo1', 'foo2']]], $config->getAllBundleConfigurations()); 93 | } 94 | 95 | public function test_a_new_object_is_created_if_the_temp_dir_is_changed() 96 | { 97 | $config = new KernelConfiguration(); 98 | $newConfig = $config->withTempDir('/tmp'); 99 | 100 | $this->assertNotSame($config, $newConfig); 101 | } 102 | 103 | public function test_the_temp_dir_is_scoped_by_the_configuration_hash() 104 | { 105 | $config = new KernelConfiguration(); 106 | $newConfig = $config->withTempDir('/tmp'); 107 | 108 | $tempDir = \sys_get_temp_dir().'/'.KernelConfiguration::DEFAULT_NAMESPACE.'/'.$config->getHash(); 109 | $newTempDir = '/tmp/'.KernelConfiguration::DEFAULT_NAMESPACE.'/'.$newConfig->getHash(); 110 | 111 | $this->assertSame($tempDir, $config->getTempDir()); 112 | $this->assertSame($newTempDir, $newConfig->getTempDir()); 113 | } 114 | 115 | public function test_the_namespace_can_be_customised() 116 | { 117 | $config = (new KernelConfiguration('Foo'))->withTempDir('/tmp'); 118 | 119 | $this->assertSame('/tmp/Foo/'.$config->getHash(), $config->getTempDir()); 120 | } 121 | 122 | public function test_the_cache_dir_is_in_the_temp_dir() 123 | { 124 | $config = (new KernelConfiguration('Foo'))->withEnvironment('foo')->withTempDir('/tmp'); 125 | $cacheDir = \sprintf('/tmp/Foo/%s/var/cache/foo', $config->getHash()); 126 | 127 | $this->assertSame($cacheDir, $config->getCacheDir()); 128 | } 129 | 130 | public function test_the_log_dir_is_in_the_temp_dir() 131 | { 132 | $config = (new KernelConfiguration('Foo'))->withEnvironment('foo')->withTempDir('/tmp'); 133 | $logDir = \sprintf('/tmp/Foo/%s/var/log', $config->getHash()); 134 | 135 | $this->assertSame($logDir, $config->getLogDir()); 136 | } 137 | 138 | public function test_a_new_object_is_created_if_a_public_service_is_configured() 139 | { 140 | $config = new KernelConfiguration(); 141 | $newConfig = $config->withPublicServiceId('foo.bar'); 142 | 143 | $this->assertNotSame($config, $newConfig); 144 | $this->assertEmpty($config->getPublicServiceIds()); 145 | $this->assertSame(['foo.bar'], $newConfig->getPublicServiceIds()); 146 | } 147 | 148 | public function test_a_new_object_is_created_if_public_services_are_configured() 149 | { 150 | $config = new KernelConfiguration(); 151 | $newConfig = $config->withPublicServiceIds(['foo.bar']); 152 | 153 | $this->assertNotSame($config, $newConfig); 154 | $this->assertEmpty($config->getPublicServiceIds()); 155 | $this->assertSame(['foo.bar'], $newConfig->getPublicServiceIds()); 156 | } 157 | 158 | public function test_public_service_id_configurations_are_appended_to_the_previously_added_ones() 159 | { 160 | $config = (new KernelConfiguration()) 161 | ->withPublicServiceId('foo.bar1') 162 | ->withPublicServiceId('foo.bar2'); 163 | 164 | $this->assertSame(['foo.bar1', 'foo.bar2'], $config->getPublicServiceIds()); 165 | } 166 | 167 | public function test_the_hash_is_the_same_for_same_environments() 168 | { 169 | $config1 = new KernelConfiguration(); 170 | $config2 = $config1->withEnvironment(KernelConfiguration::DEFAULT_ENVIRONMENT); 171 | 172 | $this->assertSameHash($config1, $config2); 173 | } 174 | 175 | public function test_the_hash_is_unique_for_different_environments() 176 | { 177 | $config1 = (new KernelConfiguration())->withEnvironment('test'); 178 | $config2 = $config1->withEnvironment('foo'); 179 | 180 | $this->assertNotSameHash($config1, $config2); 181 | } 182 | 183 | public function test_the_hash_is_the_same_for_the_same_debug_flag() 184 | { 185 | $config1 = (new KernelConfiguration())->withDebug(false); 186 | $config2 = (new KernelConfiguration())->withDebug(false); 187 | 188 | $this->assertSameHash($config1, $config2); 189 | } 190 | 191 | public function test_the_hash_is_different_for_different_debug_flags() 192 | { 193 | $config1 = (new KernelConfiguration())->withDebug(true); 194 | $config2 = (new KernelConfiguration())->withDebug(false); 195 | 196 | $this->assertNotSameHash($config1, $config2); 197 | } 198 | 199 | public function test_the_hash_is_the_same_for_the_same_collection_of_bundles() 200 | { 201 | $config1 = (new KernelConfiguration())->withBundle(new FooBundle()); 202 | $config2 = (new KernelConfiguration())->withBundle(new FooBundle()); 203 | 204 | $this->assertSameHash($config1, $config2); 205 | } 206 | 207 | public function test_the_hash_is_different_for_different_collections_of_bundles() 208 | { 209 | $config1 = (new KernelConfiguration())->withBundle(new FooBundle()); 210 | $config2 = (new KernelConfiguration())->withBundles([new FooBundle(), new BarBundle()]); 211 | 212 | $this->assertNotSameHash($config1, $config2); 213 | } 214 | 215 | public function test_the_hash_is_the_same_for_same_bundle_configurations() 216 | { 217 | $config1 = (new KernelConfiguration())->withBundleConfiguration('foo', ['enabled' => true]); 218 | $config2 = (new KernelConfiguration())->withBundleConfiguration('foo', ['enabled' => true]); 219 | 220 | $this->assertSameHash($config1, $config2); 221 | } 222 | 223 | public function test_the_hash_is_different_for_different_bundle_configurations() 224 | { 225 | $config1 = (new KernelConfiguration())->withBundleConfiguration('foo', ['enabled' => true]); 226 | $config2 = (new KernelConfiguration())->withBundleConfiguration('foo', ['enabled' => false]); 227 | 228 | $this->assertNotSameHash($config1, $config2); 229 | } 230 | 231 | public function test_the_hash_is_unique_per_namespace() 232 | { 233 | $this->assertSameHash(new KernelConfiguration('foo1'), new KernelConfiguration('foo1')); 234 | $this->assertNotSameHash(new KernelConfiguration('foo1'), new KernelConfiguration('foo2')); 235 | } 236 | 237 | public function test_the_hash_is_unique_per_temp_dir() 238 | { 239 | $this->assertSameHash((new KernelConfiguration())->withTempDir('/tmp'), (new KernelConfiguration())->withTempDir('/tmp')); 240 | $this->assertNotSameHash((new KernelConfiguration())->withTempDir('/tmp'), new KernelConfiguration()); 241 | } 242 | 243 | public function test_the_hash_is_unique_per_public_service_id_collection() 244 | { 245 | $this->assertSameHash((new KernelConfiguration())->withPublicServiceId('foo.bar'), (new KernelConfiguration())->withPublicServiceId('foo.bar')); 246 | $this->assertNotSameHash((new KernelConfiguration())->withPublicServiceId('foo.bar1'), (new KernelConfiguration())->withPublicServiceId('foo.bar2')); 247 | } 248 | 249 | private function assertSameHash(KernelConfiguration $conf1, KernelConfiguration $conf2) 250 | { 251 | $this->assertSame($conf1->getHash(), $conf2->getHash()); 252 | } 253 | 254 | private function assertNotSameHash(KernelConfiguration $conf1, KernelConfiguration $conf2) 255 | { 256 | $this->assertNotEquals($conf1->getHash(), $conf2->getHash()); 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /tests/HttpKernel/TestKernelTest.php: -------------------------------------------------------------------------------- 1 | withEnvironment('foo') 20 | ->withDebug(false) 21 | ); 22 | 23 | $this->assertInstanceOf(KernelInterface::class, $kernel); 24 | $this->assertSame('foo', $kernel->getEnvironment()); 25 | $this->assertFalse($kernel->isDebug()); 26 | } 27 | 28 | public function test_bundles_are_read_from_the_kernel_configuration() 29 | { 30 | $fooBundle = new FooBundle(); 31 | 32 | $kernel = new TestKernel( 33 | (new KernelConfiguration()) 34 | ->withBundle($fooBundle) 35 | ); 36 | 37 | $this->assertSame([$fooBundle], $kernel->registerBundles()); 38 | } 39 | 40 | public function test_cache_and_log_dirs_are_read_from_the_kernel_configuration() 41 | { 42 | $config = new KernelConfiguration(); 43 | $kernel = new TestKernel($config); 44 | 45 | $this->assertSame($config->getCacheDir(), $kernel->getCacheDir()); 46 | $this->assertSame($config->getLogDir(), $kernel->getLogDir()); 47 | } 48 | 49 | public function test_bundle_is_disabled() 50 | { 51 | $kernel = new TestKernel( 52 | (new KernelConfiguration('ZalasTestKernelTest')) 53 | ->withBundle(new FooBundle()) 54 | ->withBundleConfiguration('foo', ['enabled' => false]) 55 | ); 56 | $kernel->boot(); 57 | 58 | $this->assertFalse($kernel->getContainer()->has('foo.foo')); 59 | } 60 | 61 | public function test_bundle_extension_configuration_is_loaded() 62 | { 63 | $kernel = new TestKernel( 64 | (new KernelConfiguration('ZalasTestKernelTest')) 65 | ->withBundle(new FooBundle()) 66 | ->withBundleConfiguration('foo', ['enabled' => true]) 67 | ->withPublicServiceId(Foo::class) 68 | ); 69 | $kernel->boot(); 70 | 71 | $this->assertTrue($kernel->getContainer()->has(Foo::class)); 72 | } 73 | 74 | public function test_services_remain_private_unless_configured() 75 | { 76 | $kernel = new TestKernel( 77 | (new KernelConfiguration('ZalasTestKernelTest')) 78 | ->withBundle(new FooBundle()) 79 | ->withBundleConfiguration('foo', ['enabled' => true]) 80 | ); 81 | $kernel->boot(); 82 | 83 | $this->assertFalse($kernel->getContainer()->has(Foo::class)); 84 | } 85 | 86 | public function test_service_aliases_are_also_made_public() 87 | { 88 | $kernel = new TestKernel( 89 | (new KernelConfiguration('ZalasTestKernelTest')) 90 | ->withBundle(new FooBundle()) 91 | ->withBundleConfiguration('foo', ['enabled' => true]) 92 | ->withPublicServiceId('foo.foo') 93 | ); 94 | $kernel->boot(); 95 | 96 | $this->assertTrue($kernel->getContainer()->has('foo.foo')); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /tests/PHPUnit/ConfigurableKernelTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf(TestKernel::class, $kernel); 22 | } 23 | 24 | public function test_it_configures_the_kernel_environment() 25 | { 26 | $this->givenEnvironment('foo'); 27 | 28 | $kernel = self::bootKernel(); 29 | 30 | $this->assertSame('foo', $kernel->getEnvironment()); 31 | } 32 | 33 | public function test_it_enables_the_kernel_debug_flag() 34 | { 35 | $this->givenDebugIsEnabled(); 36 | 37 | $kernel = self::bootKernel(); 38 | 39 | $this->assertTrue($kernel->isDebug()); 40 | } 41 | 42 | public function test_it_disables_the_kernel_debug_flag() 43 | { 44 | $this->givenDebugIsDisabled(); 45 | 46 | $kernel = self::bootKernel(); 47 | 48 | $this->assertFalse($kernel->isDebug()); 49 | } 50 | 51 | public function test_it_configures_the_kernel_class() 52 | { 53 | $this->givenKernel(CustomKernel::class); 54 | 55 | $kernel = self::bootKernel(); 56 | 57 | $this->assertInstanceOf(CustomKernel::class, $kernel); 58 | } 59 | 60 | public function test_options_passed_while_configuring_the_kernel_take_precedence_over_previously_configured_ones() 61 | { 62 | $this->givenEnvironment('foo'); 63 | $this->givenDebugIsDisabled(); 64 | 65 | $kernel = self::bootKernel([ 66 | 'environment' => 'bar', 67 | 'debug' => true, 68 | 'kernel_class' => CustomKernel::class, 69 | ]); 70 | 71 | $this->assertInstanceOf(CustomKernel::class, $kernel); 72 | $this->assertSame('bar', $kernel->getEnvironment()); 73 | $this->assertTrue($kernel->isDebug()); 74 | } 75 | 76 | /** 77 | * @env APP_ENV=bar 78 | * @env APP_DEBUG=0 79 | * @env KERNEL_CLASS=Zalas\BundleTest\Tests\PHPUnit\Fixtures\CustomKernel 80 | */ 81 | public function test_environment_variables_are_read_if_kernel_was_not_configured_explicitly() 82 | { 83 | $kernel = self::bootKernel(); 84 | 85 | $this->assertInstanceOf(CustomKernel::class, $kernel); 86 | $this->assertSame('bar', $kernel->getEnvironment()); 87 | $this->assertFalse($kernel->isDebug()); 88 | } 89 | 90 | /** 91 | * @server APP_ENV=bar 92 | * @server APP_DEBUG=0 93 | * @server KERNEL_CLASS=Zalas\BundleTest\Tests\PHPUnit\Fixtures\CustomKernel 94 | */ 95 | public function test_server_variables_are_read_if_kernel_was_not_configured_explicitly() 96 | { 97 | $kernel = self::bootKernel(); 98 | 99 | $this->assertInstanceOf(CustomKernel::class, $kernel); 100 | $this->assertSame('bar', $kernel->getEnvironment()); 101 | $this->assertFalse($kernel->isDebug()); 102 | } 103 | 104 | /** 105 | * @env APP_ENV=bar 106 | * @env APP_DEBUG=1 107 | * @env KERNEL_CLASS=Zalas\BundleTest\HttpKernel\TestKernel 108 | */ 109 | public function test_environment_variables_are_overridden_by_custom_configuration() 110 | { 111 | $this->givenEnvironment('foo'); 112 | $this->givenDebugIsDisabled(); 113 | $this->givenKernel(CustomKernel::class); 114 | 115 | $kernel = self::bootKernel(); 116 | 117 | $this->assertInstanceOf(CustomKernel::class, $kernel); 118 | $this->assertSame('foo', $kernel->getEnvironment()); 119 | $this->assertFalse($kernel->isDebug()); 120 | } 121 | 122 | /** 123 | * @server APP_ENV=bar 124 | * @server APP_DEBUG=1 125 | * @server KERNEL_CLASS=Zalas\BundleTest\HttpKernel\TestKernel 126 | */ 127 | public function test_server_variables_are_overridden_by_custom_configuration() 128 | { 129 | $this->givenEnvironment('foo'); 130 | $this->givenDebugIsDisabled(); 131 | $this->givenKernel(CustomKernel::class); 132 | 133 | $kernel = self::bootKernel(); 134 | 135 | $this->assertInstanceOf(CustomKernel::class, $kernel); 136 | $this->assertSame('foo', $kernel->getEnvironment()); 137 | $this->assertFalse($kernel->isDebug()); 138 | } 139 | 140 | public function test_it_enables_bundles() 141 | { 142 | $this->givenEnabledBundles([new FooBundle()]); 143 | 144 | $kernel = self::bootKernel(); 145 | $bundles = $kernel->getBundles(); 146 | 147 | $this->assertCount(1, $bundles, 'One bundle was registered.'); 148 | $this->assertArrayHasKey('FooBundle', $bundles, 'The "FooBundle" was registered.'); 149 | $this->assertInstanceOf(FooBundle::class, $bundles['FooBundle'], 'The "FooBundle" was registered.'); 150 | } 151 | 152 | public function test_it_enables_a_bundle() 153 | { 154 | $this->givenEnabledBundle(new FooBundle()); 155 | 156 | $kernel = self::bootKernel(); 157 | $bundles = $kernel->getBundles(); 158 | 159 | $this->assertCount(1, $bundles, 'One bundle was registered.'); 160 | $this->assertArrayHasKey('FooBundle', $bundles, 'The "FooBundle" was registered.'); 161 | $this->assertInstanceOf(FooBundle::class, $bundles['FooBundle'], 'The "FooBundle" was registered.'); 162 | } 163 | 164 | public function test_it_configures_the_bundle() 165 | { 166 | $this->givenEnabledBundle(new FooBundle()); 167 | $this->givenBundleConfiguration('foo', ['enabled' => true]); 168 | $this->givenPublicServiceId(Foo::class); 169 | 170 | $kernel = self::bootKernel(); 171 | 172 | $this->assertTrue($kernel->getContainer()->has(Foo::class)); 173 | } 174 | 175 | public function test_it_exposes_private_services_as_public() 176 | { 177 | $this->givenEnabledBundle(new FooBundle()); 178 | $this->givenBundleConfiguration('foo', ['enabled' => true]); 179 | $this->givenPublicServiceIds([Foo::class]); 180 | 181 | $kernel = self::bootKernel(); 182 | 183 | $this->assertTrue($kernel->getContainer()->has(Foo::class)); 184 | } 185 | 186 | public function test_it_changes_the_temp_dir() 187 | { 188 | $this->givenTempDir($tempDir = \sys_get_temp_dir().'/ConfigurableKernelTest'); 189 | 190 | $kernel = self::bootKernel(); 191 | 192 | $this->assertStringStartsWith($tempDir, $kernel->getCacheDir()); 193 | } 194 | 195 | public function test_that_namespace_is_defined_by_default() 196 | { 197 | $kernel = self::bootKernel(); 198 | 199 | $this->assertContains('ConfigurableKernelTest', $kernel->getCacheDir()); 200 | } 201 | 202 | public function test_environment_cannot_be_changed_once_kernel_is_booted() 203 | { 204 | $this->expectException(\LogicException::class); 205 | 206 | self::bootKernel(); 207 | 208 | $this->givenEnvironment('foo'); 209 | } 210 | 211 | public function test_debug_cannot_be_disabled_once_kernel_is_booted() 212 | { 213 | $this->expectException(\LogicException::class); 214 | 215 | self::bootKernel(); 216 | 217 | $this->givenDebugIsDisabled(); 218 | } 219 | 220 | public function test_debug_cannot_be_enabled_once_kernel_is_booted() 221 | { 222 | $this->expectException(\LogicException::class); 223 | 224 | self::bootKernel(); 225 | 226 | $this->givenDebugIsEnabled(); 227 | } 228 | 229 | public function test_kernel_class_cannot_be_changed_once_kernel_is_booted() 230 | { 231 | $this->expectException(\LogicException::class); 232 | 233 | self::bootKernel(); 234 | 235 | $this->givenKernel(CustomKernel::class); 236 | } 237 | 238 | public function test_bundle_configuration_cannot_be_changed_once_kernel_is_booted() 239 | { 240 | $this->expectException(\LogicException::class); 241 | 242 | self::bootKernel(); 243 | 244 | $this->givenBundleConfiguration('foo', ['enabled' => true]); 245 | } 246 | 247 | public function test_public_service_cannot_be_added_once_kernel_is_booted() 248 | { 249 | $this->expectException(\LogicException::class); 250 | 251 | self::bootKernel(); 252 | 253 | $this->givenPublicServiceId('foo'); 254 | } 255 | 256 | public function test_public_services_cannot_be_added_once_kernel_is_booted() 257 | { 258 | $this->expectException(\LogicException::class); 259 | 260 | self::bootKernel(); 261 | 262 | $this->givenPublicServiceIds(['foo']); 263 | } 264 | 265 | public function test_bundle_cannot_be_enabled_once_kernel_is_booted() 266 | { 267 | $this->expectException(\LogicException::class); 268 | 269 | self::bootKernel(); 270 | 271 | $this->givenEnabledBundle(new FooBundle()); 272 | } 273 | 274 | public function test_bundles_cannot_be_enabled_once_kernel_is_booted() 275 | { 276 | $this->expectException(\LogicException::class); 277 | 278 | self::bootKernel(); 279 | 280 | $this->givenEnabledBundles([new FooBundle()]); 281 | } 282 | 283 | public function test_temp_dir_cannot_be_changed_once_kernel_is_booted() 284 | { 285 | $this->expectException(\LogicException::class); 286 | 287 | self::bootKernel(); 288 | 289 | $this->givenTempDir('/tmp'); 290 | } 291 | } 292 | -------------------------------------------------------------------------------- /tests/PHPUnit/Fixtures/CustomKernel.php: -------------------------------------------------------------------------------- 1 | root('zalas_foo'); 15 | 16 | // canBeEnabled() is broken in older Symfony versions 17 | $rootNode 18 | ->addDefaultsIfNotSet() 19 | ->treatFalseLike(['enabled' => false]) 20 | ->treatTrueLike(['enabled' => true]) 21 | ->treatNullLike(['enabled' => true]) 22 | ->beforeNormalization() 23 | ->ifArray() 24 | ->then(function ($v) { 25 | $v['enabled'] = $v['enabled'] ?? !empty($v); 26 | 27 | return $v; 28 | }) 29 | ->end() 30 | ->children() 31 | ->booleanNode('enabled') 32 | ->defaultFalse(); 33 | 34 | return $treeBuilder; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/PHPUnit/Fixtures/FooBundle/DependencyInjection/FooExtension.php: -------------------------------------------------------------------------------- 1 | processConfiguration(new Configuration(), $configs); 16 | 17 | if ($config['enabled']) { 18 | $loader = new XmlFileLoader($container, new FileLocator(\dirname(__DIR__).'/Resources/config')); 19 | $loader->load('services.xml'); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/PHPUnit/Fixtures/FooBundle/Foo.php: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /tests/PHPUnit/TestKernelTest.php: -------------------------------------------------------------------------------- 1 | assertSame('test', $kernel->getEnvironment()); 19 | $this->assertTrue($kernel->isDebug()); 20 | } 21 | 22 | public function test_bootKernel_creates_the_test_kernel_for_given_environment_and_debug_flag() 23 | { 24 | $kernel = self::bootKernel([ 25 | 'environment' => 'foo', 26 | 'debug' => false, 27 | ]); 28 | 29 | $this->assertSame('foo', $kernel->getEnvironment()); 30 | $this->assertFalse($kernel->isDebug()); 31 | } 32 | 33 | public function test_bootKernel_gives_access_to_the_previously_booted_kernel() 34 | { 35 | $kernel = self::bootKernel(); 36 | 37 | $this->assertSame($kernel, self::$kernel); 38 | } 39 | 40 | public function test_bootKernel_boots_the_created_kernel() 41 | { 42 | $kernel = self::bootKernel(); 43 | 44 | $this->assertNotNull($kernel->getContainer()); 45 | } 46 | 47 | public function test_bootKernel_ensures_the_kernel_was_shut_down() 48 | { 49 | $kernel1 = self::bootKernel(); 50 | $kernel2 = self::bootKernel(); 51 | 52 | $this->assertNull($kernel1->getContainer()); 53 | $this->assertNotNull($kernel2->getContainer()); 54 | } 55 | 56 | public function test_ensureKernelShutdown_shuts_down_the_kernel() 57 | { 58 | $kernel = self::bootKernel(); 59 | 60 | self::ensureKernelShutdown(); 61 | 62 | $this->assertNull($kernel->getContainer()); 63 | } 64 | 65 | public function test_ensureKernelShutdown_resets_the_container() 66 | { 67 | $kernel = self::bootKernel(); 68 | 69 | $container = $kernel->getContainer(); 70 | $container->set('foo.bar', new \stdClass()); 71 | 72 | self::ensureKernelShutdown(); 73 | 74 | $this->assertFalse($container->has('foo.bar')); 75 | } 76 | 77 | public function test_createKernel_creates_the_test_kernel_with_defaults() 78 | { 79 | $kernel = self::createKernel(); 80 | 81 | $this->assertSame('test', $kernel->getEnvironment()); 82 | $this->assertTrue($kernel->isDebug()); 83 | } 84 | 85 | public function test_createKernel_creates_the_test_kernel_for_given_environment_and_debug_flag() 86 | { 87 | $kernel = self::createKernel([ 88 | 'environment' => 'foo', 89 | 'debug' => false, 90 | 'kernel_class' => CustomKernel::class, 91 | ]); 92 | 93 | $this->assertInstanceOf(CustomKernel::class, $kernel); 94 | $this->assertSame('foo', $kernel->getEnvironment()); 95 | $this->assertFalse($kernel->isDebug()); 96 | } 97 | 98 | /** 99 | * @env APP_ENV=foo 100 | * @env APP_DEBUG=0 101 | * @env KERNEL_CLASS=Zalas\BundleTest\Tests\PHPUnit\Fixtures\CustomKernel 102 | */ 103 | public function test_createKernel_creates_the_kernel_based_on_environment_variables() 104 | { 105 | $kernel = self::createKernel(); 106 | 107 | $this->assertInstanceOf(CustomKernel::class, $kernel); 108 | $this->assertSame('foo', $kernel->getEnvironment()); 109 | $this->assertFalse($kernel->isDebug()); 110 | } 111 | 112 | /** 113 | * @server APP_ENV=foo 114 | * @server APP_DEBUG=0 115 | * @server KERNEL_CLASS=Zalas\BundleTest\Tests\PHPUnit\Fixtures\CustomKernel 116 | */ 117 | public function test_createKernel_creates_the_kernel_based_on_server_variables() 118 | { 119 | $kernel = self::createKernel(); 120 | 121 | $this->assertInstanceOf(CustomKernel::class, $kernel); 122 | $this->assertSame('foo', $kernel->getEnvironment()); 123 | $this->assertFalse($kernel->isDebug()); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /tools/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore --------------------------------------------------------------------------------