From 11095f21809bbf8b956d54f0d671a3bd7d0e8899 Mon Sep 17 00:00:00 2001 From: Simon Podlipsky Date: Mon, 11 Jan 2021 09:57:15 +0100 Subject: [PATCH] Establish maintenance --- .gitattributes | 16 +- .github/workflows/coding-standards.yml | 34 ++ .github/workflows/continuous-integration.yml | 75 ++++ .github/workflows/static-analysis.yml | 59 +++ .gitignore | 9 +- .travis.yml | 33 -- license.md => LICENCE | 0 readme.md => README.md | 26 +- build.xml | 115 ------ build/.gitignore | 2 - build/coveralls.yml | 3 - build/cs-ruleset.xml | 22 -- composer.json | 54 ++- phpcs.xml.dist | 14 + phpstan-baseline.neon | 7 + phpstan.neon.dist | 8 + phpunit.xml.dist | 21 ++ psalm-baseline.xml | 11 + psalm.xml.dist | 23 ++ src/BlueScreen/BlueScreenFactory.php | 27 +- .../ConsoleBlueScreenErrorListener.php | 175 +++++---- .../ControllerBlueScreenExceptionListener.php | 84 ++--- src/DependencyInjection/Configuration.php | 205 +++++------ .../Exception/TwigBundleRequired.php | 16 + .../TracyBlueScreenExtension.php | 228 ++++++------ .../config/console_listener.yml | 14 +- .../config/controller_listener.yml | 8 +- src/DependencyInjection/config/services.yml | 12 +- .../TwigBundleRequiredException.php | 15 - src/TracyBlueScreenBundle.php | 9 +- ...ConsoleBlueScreenExceptionListenerTest.php | 228 ++++++------ ...trollerBlueScreenExceptionListenerTest.php | 49 +-- .../TracyBlueScreenExtensionConsoleTest.php | 307 +++++++++------- ...TracyBlueScreenExtensionControllerTest.php | 213 ++++++----- .../TracyBlueScreenExtensionTest.php | 339 ++++++++++-------- tests/bootstrap.php | 2 +- tests/phpunit.xml | 33 -- 37 files changed, 1325 insertions(+), 1171 deletions(-) create mode 100644 .github/workflows/coding-standards.yml create mode 100644 .github/workflows/continuous-integration.yml create mode 100644 .github/workflows/static-analysis.yml delete mode 100644 .travis.yml rename license.md => LICENCE (100%) rename readme.md => README.md (78%) delete mode 100644 build.xml delete mode 100644 build/.gitignore delete mode 100644 build/coveralls.yml delete mode 100644 build/cs-ruleset.xml create mode 100644 phpcs.xml.dist create mode 100644 phpstan-baseline.neon create mode 100644 phpstan.neon.dist create mode 100644 phpunit.xml.dist create mode 100644 psalm-baseline.xml create mode 100644 psalm.xml.dist create mode 100644 src/DependencyInjection/Exception/TwigBundleRequired.php delete mode 100644 src/DependencyInjection/exceptions/TwigBundleRequiredException.php delete mode 100644 tests/phpunit.xml diff --git a/.gitattributes b/.gitattributes index e77abda..ed17d00 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,12 @@ -.gitattributes export-ignore -.gitignore export-ignore -/.travis.yml export-ignore -/build export-ignore -/build.xml export-ignore +# Set the default behavior, in case people don't have core.autocrlf set. +* text eol=lf +/.gitattributes export-ignore +/.gitignore export-ignore +/.github export-ignore +/phpcs.xml.dist export-ignore +/phpstan.neon.dist export-ignore +/phpstan-baseline.neon export-ignore +/phpunit.xml.dist export-ignore +/psalm.xml.dist export-ignore +/psalm-baseline.xml export-ignore /tests export-ignore diff --git a/.github/workflows/coding-standards.yml b/.github/workflows/coding-standards.yml new file mode 100644 index 0000000..6007fbe --- /dev/null +++ b/.github/workflows/coding-standards.yml @@ -0,0 +1,34 @@ +name: "Coding Standards" + +on: + pull_request: + push: + branches: + - "master" + +jobs: + coding-standards: + name: "Coding Standards" + runs-on: "ubuntu-20.04" + + strategy: + matrix: + php-version: + - "7.3" + + steps: + - name: "Checkout" + uses: "actions/checkout@v2" + + - name: "Install PHP" + uses: "shivammathur/setup-php@v2" + with: + coverage: "none" + php-version: "${{ matrix.php-version }}" + tools: "cs2pr" + + - name: "Install dependencies with Composer" + uses: "ramsey/composer-install@v1" + + - name: "Run PHP_CodeSniffer" + run: "vendor/bin/phpcs -q --no-colors --report=checkstyle | cs2pr" diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml new file mode 100644 index 0000000..124570d --- /dev/null +++ b/.github/workflows/continuous-integration.yml @@ -0,0 +1,75 @@ +name: "CI" + +on: + pull_request: + push: + branches: + - "master" + schedule: + - cron: "42 3 * * *" + +jobs: + phpunit: + name: "PHPUnit" + runs-on: "ubuntu-20.04" + + strategy: + matrix: + php-version: + - "7.3" + - "7.4" + - "8.0" + dependencies: + - "highest" + include: + - dependencies: "lowest" + php-version: "7.3" + + steps: + - name: "Checkout" + uses: "actions/checkout@v2" + with: + fetch-depth: 2 + + - name: "Install PHP" + uses: "shivammathur/setup-php@v2" + with: + php-version: "${{ matrix.php-version }}" + coverage: "pcov" + ini-values: "zend.assertions=1" + + - name: "Install dependencies with Composer" + uses: "ramsey/composer-install@v1" + with: + dependency-versions: "${{ matrix.dependencies }}" + + - name: "Run PHPUnit" + run: "vendor/bin/phpunit --coverage-clover=coverage.xml" + + - name: "Upload coverage file" + uses: "actions/upload-artifact@v2" + with: + name: "phpunit-${{ matrix.deps }}-${{ matrix.php-version }}.coverage" + path: "coverage.xml" + + upload_coverage: + name: "Upload coverage to Codecov" + runs-on: "ubuntu-20.04" + needs: + - "phpunit" + + steps: + - name: "Checkout" + uses: "actions/checkout@v2" + with: + fetch-depth: 2 + + - name: "Download coverage files" + uses: "actions/download-artifact@v2" + with: + path: "reports" + + - name: "Upload to Codecov" + uses: "codecov/codecov-action@v1" + with: + directory: reports diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml new file mode 100644 index 0000000..48298be --- /dev/null +++ b/.github/workflows/static-analysis.yml @@ -0,0 +1,59 @@ +name: "Static Analysis" + +on: + pull_request: + push: + branches: + - "master" + +jobs: + static-analysis-phpstan: + name: "Static Analysis with PHPStan" + runs-on: "ubuntu-20.04" + + strategy: + matrix: + php-version: + - "7.3" + + steps: + - name: "Checkout code" + uses: "actions/checkout@v2" + + - name: "Install PHP" + uses: "shivammathur/setup-php@v2" + with: + coverage: "none" + php-version: "${{ matrix.php-version }}" + tools: "cs2pr" + + - name: "Install dependencies with Composer" + uses: "ramsey/composer-install@v1" + + - name: "Run a static analysis with phpstan/phpstan" + run: "vendor/bin/phpstan analyse --error-format=checkstyle | cs2pr" + + static-analysis-psalm: + name: "Static Analysis with Psalm" + runs-on: "ubuntu-20.04" + + strategy: + matrix: + php-version: + - "7.3" + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Psalm + uses: docker://vimeo/psalm-github-actions:4.3.2 + with: + args: --shepherd + composer_require_dev: true + security_analysis: true + report_file: results.sarif + - name: Upload Security Analysis results to GitHub + uses: github/codeql-action/upload-sarif@v1 + with: + sarif_file: results.sarif diff --git a/.gitignore b/.gitignore index 87b242c..56ed85a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ -/bin +/.phpcs-cache +/.phpunit.result.cache /composer.lock -/vendor +/phpcs.xml +/phpstan.neon +/psalm.xml +/phpunit.xml +/vendor/ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index a0660de..0000000 --- a/.travis.yml +++ /dev/null @@ -1,33 +0,0 @@ -language: php - -sudo: false - -php: - - 7.2 - - 7.3 - - nightly - -env: - - # default environment without variables - - COMPOSER_DEPENDENCIES_OPTIONS="--prefer-lowest --prefer-stable" - -matrix: - fast_finish: true - allow_failures: - - php: nightly - -before_install: - - composer self-update - -install: - - composer update --no-interaction --no-suggest --prefer-source ${COMPOSER_DEPENDENCIES_OPTIONS} - -script: - - bin/phing ci-build - -after_success: - - if [ "${TRAVIS_ALLOW_FAILURE}" = false ]; then - wget https://github.com/satooshi/php-coveralls/releases/download/v1.0.0/coveralls.phar - && php coveralls.phar --verbose --config build/coveralls.yml - || true; - fi diff --git a/license.md b/LICENCE similarity index 100% rename from license.md rename to LICENCE diff --git a/readme.md b/README.md similarity index 78% rename from readme.md rename to README.md index 7ce3430..5a5a62d 100644 --- a/readme.md +++ b/README.md @@ -1,5 +1,8 @@ -Tracy BlueScreen Bundle -====================== +# Tracy BlueScreen Bundle + +[![GitHub Actions][GA master image]][GA master] +[![Type Coverage][Shepherd Image]][Shepherd Link] +[![Code Coverage][Coverage image]][CodeCov Master] **This bundle lets you use the [Tracy's debug screen](https://github.com/nette/tracy#visualization-of-errors-and-exceptions) in combination with the the default profiler in your Symfony application.** @@ -12,7 +15,7 @@ Why is Tracy's debug screen better than the Symfony default exception screen: * Fullscreen layout providing more space for information. * Look at the interactive [example screen](http://nette.github.io/tracy/tracy-exception.html). -However the Symfony profiler provides a lot of useful information about the application when an error occurs, so it is better to have them both available: +However, the Symfony profiler provides a lot of useful information about the application when an error occurs, so it is better to have them both available: ![Nette Tracy with Symfony profiler screenshot](docs/tracy-with-profiler.png) @@ -80,7 +83,7 @@ tracy_blue_screen: # Add paths which should be collapsed (for external/compiled code) so that actual error is expanded. collapse_paths: # Defaults: - - '%kernel.root_dir%/bootstrap.php.cache' + - '%kernel.project%/bootstrap.php.cache' - '%kernel.cache_dir%' # plus paths set in BlueScreen instance used (/vendor) @@ -96,16 +99,16 @@ services: info: - 'environment: %kernel.environment%' - vasek_purchart.tracy_blue_screen.tracy.blue_screen: '@my_blue_screen' + cdn77.tracy_blue_screen.tracy.blue_screen: '@my_blue_screen' ``` Installation ----------- -Install package [`vasek-purchart/tracy-blue-screen-bundle`](https://packagist.org/packages/vasek-purchart/tracy-blue-screen-bundle) with [Composer](https://getcomposer.org/): +Install package [`cdn77/tracy-blue-screen-bundle`](https://packagist.org/packages/cdn77/tracy-blue-screen-bundle) with [Composer](https://getcomposer.org/): ```bash -composer require vasek-purchart/tracy-blue-screen-bundle +composer require cdn77/tracy-blue-screen-bundle ``` Register the bundle in your application: @@ -113,6 +116,13 @@ Register the bundle in your application: // config/bundles.php return [ // ... - VasekPurchart\TracyBlueScreenBundle\TracyBlueScreenBundle::class => ['all' => true], + Cdn77\TracyBlueScreenBundle\TracyBlueScreenBundle::class => ['all' => true], ]; ``` + +[Coverage image]: https://codecov.io/gh/cdn77/Tracy-Blue-Screen-Bundle/branch/master/graph/badge.svg +[CodeCov Master]: https://codecov.io/gh/cdn77/Tracy-Blue-Screen-Bundle/branch/master +[GA master]: https://github.com/cdn77/Tracy-Blue-Screen-Bundle/actions?query=workflow%3A%22CI%22+branch%3Amaster +[GA master image]: https://github.com/cdn77/Tracy-Blue-Screen-Bundle/workflows/CI/badge.svg +[Shepherd Image]: https://shepherd.dev/github/cdn77/Tracy-Blue-Screen-Bundle/coverage.svg +[Shepherd Link]: https://shepherd.dev/github/cdn77/Tracy-Blue-Screen-Bundle diff --git a/build.xml b/build.xml deleted file mode 100644 index 5d3169e..0000000 --- a/build.xml +++ /dev/null @@ -1,115 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/build/.gitignore b/build/.gitignore deleted file mode 100644 index ce5a5b3..0000000 --- a/build/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/build.local.properties -/log diff --git a/build/coveralls.yml b/build/coveralls.yml deleted file mode 100644 index 252b3f3..0000000 --- a/build/coveralls.yml +++ /dev/null @@ -1,3 +0,0 @@ -coverage_clover: build/log/coverage/clover.xml -json_path: build/log/coverage/coveralls-upload.json -service_name: travis-ci diff --git a/build/cs-ruleset.xml b/build/cs-ruleset.xml deleted file mode 100644 index bae6e0b..0000000 --- a/build/cs-ruleset.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - src/BlueScreen/ConsoleBlueScreenErrorListener.php - - - - - - - - - */data/*.php - */exceptions/*Exception.php - - - */data/*.php - - diff --git a/composer.json b/composer.json index ddfdc2e..6dff194 100644 --- a/composer.json +++ b/composer.json @@ -1,46 +1,44 @@ { - "name": "vasek-purchart/tracy-blue-screen-bundle", + "name": "cdn77/tracy-blue-screen-bundle", "description": "This bundle lets you use the Tracy's debug screen in combination with the the default profiler in your Symfony application.", "keywords": ["tracy", "bluescreen", "error", "debug", "debugger", "exceptions", "bundle"], "license": "MIT", - "authors": [ - { - "name": "VaĊĦek Purchart", - "email": "me@vasekpurchart.cz", - "homepage": "http://vasekpurchart.cz" - } - ], "require": { - "php": "~7.2", - "symfony/config": "~4.0", - "symfony/console": "~4.0", - "symfony/dependency-injection": "~4.0", - "symfony/http-kernel": "~4.0", - "symfony/twig-bundle": "~4.0", - "symfony/yaml": "~4.0", - "tracy/tracy": "~2.4" + "php": "^7.3 || ^8.0", + "symfony/config": "^5.0", + "symfony/console": "^5.0", + "symfony/dependency-injection": "^5.0", + "symfony/http-kernel": "^5.0", + "symfony/twig-bundle": "^5.0", + "symfony/yaml": "^5.0", + "tracy/tracy": "^2.8" }, "require-dev": { - "consistence/coding-standard": "3.7", - "jakub-onderka/php-console-highlighter": "0.4", - "jakub-onderka/php-parallel-lint": "1.0", - "matthiasnoback/symfony-dependency-injection-test": "3.1", - "mikey179/vfsStream": "1.6.5", - "phing/phing": "2.16.1", - "phpunit/phpunit": "7.5.1" + "cdn77/coding-standard": "^4.0", + "matthiasnoback/symfony-dependency-injection-test": "^4.2", + "mikey179/vfsstream": "^1.6", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12.65", + "phpstan/phpstan-phpunit": "0.12.17", + "phpstan/phpstan-strict-rules": "^0.12.7", + "phpunit/phpunit": "^9.5", + "psalm/plugin-phpunit": "^0.15.0", + "vimeo/psalm": "^4.3" }, "autoload": { "psr-4": { - "VasekPurchart\\TracyBlueScreenBundle\\": "src" - }, - "classmap": [ "src" ] + "Cdn77\\TracyBlueScreenBundle\\": "src" + } }, "autoload-dev": { "psr-4": { - "VasekPurchart\\TracyBlueScreenBundle\\": "tests" + "Cdn77\\TracyBlueScreenBundle\\Tests\\": "tests" } }, "config": { - "bin-dir": "bin" + "sort-packages": true, + "platform": { + "php": "7.3.0" + } } } diff --git a/phpcs.xml.dist b/phpcs.xml.dist new file mode 100644 index 0000000..598f248 --- /dev/null +++ b/phpcs.xml.dist @@ -0,0 +1,14 @@ + + + + + + + + + src/ + tests/ + diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon new file mode 100644 index 0000000..5a7398d --- /dev/null +++ b/phpstan-baseline.neon @@ -0,0 +1,7 @@ +parameters: + ignoreErrors: + - + message: "#^Cannot call method integerNode\\(\\) on Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeParentInterface\\|null\\.$#" + count: 1 + path: src/DependencyInjection/Configuration.php + diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 0000000..358431c --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,8 @@ +parameters: + level: max + paths: + - %currentWorkingDirectory%/src + - %currentWorkingDirectory%/tests + +includes: + - phpstan-baseline.neon diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..9247927 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,21 @@ + + + + + tests + + + + + src + + + diff --git a/psalm-baseline.xml b/psalm-baseline.xml new file mode 100644 index 0000000..be50e11 --- /dev/null +++ b/psalm-baseline.xml @@ -0,0 +1,11 @@ + + + + + integerNode + + + integerNode + + + diff --git a/psalm.xml.dist b/psalm.xml.dist new file mode 100644 index 0000000..6beb420 --- /dev/null +++ b/psalm.xml.dist @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/BlueScreen/BlueScreenFactory.php b/src/BlueScreen/BlueScreenFactory.php index 9471a44..c151ff6 100644 --- a/src/BlueScreen/BlueScreenFactory.php +++ b/src/BlueScreen/BlueScreenFactory.php @@ -1,25 +1,22 @@ collapsePaths = array_merge($blueScreen->collapsePaths, $collapsePaths); +use function array_merge; - return $blueScreen; - } +final class BlueScreenFactory +{ + /** @param string[] $collapsePaths */ + public static function create(array $collapsePaths) : BlueScreen + { + $blueScreen = Debugger::getBlueScreen(); + $blueScreen->collapsePaths = array_merge($blueScreen->collapsePaths, $collapsePaths); + return $blueScreen; + } } diff --git a/src/BlueScreen/ConsoleBlueScreenErrorListener.php b/src/BlueScreen/ConsoleBlueScreenErrorListener.php index 1520dfb..9968af5 100644 --- a/src/BlueScreen/ConsoleBlueScreenErrorListener.php +++ b/src/BlueScreen/ConsoleBlueScreenErrorListener.php @@ -1,101 +1,98 @@ tracyLogger = $tracyLogger; - $this->blueScreen = $blueScreen; - $this->logDirectory = $logDirectory; - $this->browser = $browser; - } - - public function onConsoleError(ConsoleErrorEvent $event): void - { - if ($this->tracyLogger->directory === null) { - $this->tracyLogger->directory = $this->logDirectory; - } - - if ( - $this->tracyLogger->directory === null - || !is_dir($this->tracyLogger->directory) - || !is_writable($this->tracyLogger->directory) - ) { - throw new \InvalidArgumentException(sprintf( - 'Log directory must be a writable directory, %s [%s] given', - $this->tracyLogger->directory, - gettype($this->tracyLogger->directory) - ), 0, $event->getError()); - } - - $exception = $event->getError(); - $exceptionFile = $this->tracyLogger->getExceptionFile($exception); - $this->blueScreen->renderToFile($exception, $exceptionFile); - - $output = $event->getOutput(); - $this->printErrorMessage($output, sprintf('BlueScreen saved in file: %s', $exceptionFile)); - if ($this->browser === null) { - return; - } - // @codeCoverageIgnoreStart - // uses global state - $this->openBrowser($this->browser, $exceptionFile); - } - // @codeCoverageIgnoreEnd - - private function printErrorMessage(OutputInterface $output, string $message): void - { - $message = sprintf('%s', $message); - if ($output instanceof ConsoleOutputInterface) { - $output->getErrorOutput()->writeln($message); - } else { - $output->writeln($message); - } - } - - /** - * @codeCoverageIgnore uses global state - * - * @param string $browser - * @param string $file - */ - private function openBrowser(string $browser, string $file): void - { - static $showed = false; - if ($showed) { - return; - } - - exec(sprintf('%s %s', $browser, escapeshellarg($file))); - $showed = true; - } +use function escapeshellarg; +use function exec; +use function gettype; +use function is_dir; +use function is_writable; +use function sprintf; +final class ConsoleBlueScreenErrorListener +{ + /** @var TracyLogger */ + private $tracyLogger; + + /** @var BlueScreen */ + private $blueScreen; + + /** @var string|null */ + private $logDirectory; + + /** @var string|null */ + private $browser; + + public function __construct( + TracyLogger $tracyLogger, + BlueScreen $blueScreen, + ?string $logDirectory, + ?string $browser + ) { + $this->tracyLogger = $tracyLogger; + $this->blueScreen = $blueScreen; + $this->logDirectory = $logDirectory; + $this->browser = $browser; + } + + public function onConsoleError(ConsoleErrorEvent $event) : void + { + if ($this->tracyLogger->directory === null) { + $this->tracyLogger->directory = $this->logDirectory; + } + + if ( + $this->tracyLogger->directory === null + || ! is_dir($this->tracyLogger->directory) + || ! is_writable($this->tracyLogger->directory) + ) { + throw new InvalidArgumentException(sprintf( + 'Log directory must be a writable directory, %s [%s] given', + (string) $this->tracyLogger->directory, + gettype($this->tracyLogger->directory) + ), 0, $event->getError()); + } + + $exception = $event->getError(); + $exceptionFile = $this->tracyLogger->getExceptionFile($exception); + $this->blueScreen->renderToFile($exception, $exceptionFile); + + $output = $event->getOutput(); + $this->printErrorMessage($output, sprintf('BlueScreen saved in file: %s', $exceptionFile)); + if ($this->browser === null) { + return; + } + + $this->openBrowser($this->browser, $exceptionFile); + } + + private function printErrorMessage(OutputInterface $output, string $message) : void + { + $message = sprintf('%s', $message); + if ($output instanceof ConsoleOutputInterface) { + $output->getErrorOutput()->writeln($message); + } else { + $output->writeln($message); + } + } + + private function openBrowser(string $browser, string $file) : void + { + static $showed = false; + if ($showed) { + return; + } + + exec(sprintf('%s %s', $browser, escapeshellarg($file))); + $showed = true; + } } diff --git a/src/BlueScreen/ControllerBlueScreenExceptionListener.php b/src/BlueScreen/ControllerBlueScreenExceptionListener.php index 9fff4d2..91b00ed 100644 --- a/src/BlueScreen/ControllerBlueScreenExceptionListener.php +++ b/src/BlueScreen/ControllerBlueScreenExceptionListener.php @@ -1,51 +1,51 @@ blueScreen = $blueScreen; - } - - public function onKernelException(GetResponseForExceptionEvent $event): void - { - $this->forceExceptionControllerHtml($event->getRequest()); - $this->renderBlueScreen($event->getException()); - } - - private function forceExceptionControllerHtml(Request $request): void - { - $request->setRequestFormat('html'); - $request->attributes->set('_format', 'html'); - } - - private function renderBlueScreen(\Throwable $exception): void - { - if (!headers_sent()) { - // @codeCoverageIgnoreStart - // sends output and uses global state - $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1'; - $code = isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE ') !== false ? 503 : 500; - header($protocol . ' ' . $code, true, $code); - header('Content-Type: text/html; charset=UTF-8'); - } - // @codeCoverageIgnoreEnd - - $this->blueScreen->render($exception); - } +use function header; +use function headers_sent; +use function strpos; +final class ControllerBlueScreenExceptionListener +{ + /** @var BlueScreen */ + private $blueScreen; + + public function __construct(BlueScreen $blueScreen) + { + $this->blueScreen = $blueScreen; + } + + public function onKernelException(ExceptionEvent $event) : void + { + $this->forceExceptionControllerHtml($event->getRequest()); + $this->renderBlueScreen($event->getThrowable()); + } + + private function forceExceptionControllerHtml(Request $request) : void + { + $request->setRequestFormat('html'); + $request->attributes->set('_format', 'html'); + } + + private function renderBlueScreen(Throwable $exception) : void + { + if (! headers_sent()) { + $protocol = $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.1'; + $code = isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE ') !== false + ? 503 + : 500; + header($protocol . ' ' . $code, true, $code); + header('Content-Type: text/html; charset=UTF-8'); + } + + $this->blueScreen->render($exception); + } } diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 998e2ee..35be503 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -1,119 +1,122 @@ rootNode = $rootNode; - $this->kernelRootDir = $kernelRootDir; - $this->kernelLogsDir = $kernelLogsDir; - $this->kernelCacheDir = $kernelCacheDir; - } + /** @var string */ + private $kernelCacheDir; - public function getConfigTreeBuilder(): TreeBuilder - { - $treeBuilder = new TreeBuilder(); - $rootNode = $treeBuilder->root($this->rootNode); + public function __construct(string $alias, string $kernelRootDir, string $kernelLogsDir, string $kernelCacheDir) + { + $this->alias = $alias; + $this->kernelRootDir = $kernelRootDir; + $this->kernelLogsDir = $kernelLogsDir; + $this->kernelCacheDir = $kernelCacheDir; + } - $rootNode - ->children() - ->arrayNode(self::SECTION_CONTROLLER) - ->addDefaultsIfNotSet() - ->children() - ->scalarNode(self::PARAMETER_CONTROLLER_ENABLED) - ->info('Enable debug screen for controllers.') - ->defaultNull() - ->end() - ->integerNode(self::PARAMETER_CONTROLLER_LISTENER_PRIORITY) - ->info('Priority with which the listener will be registered.') - ->defaultValue(0) - ->end() - ->end() - ->end() - ->arrayNode(self::SECTION_CONSOLE) - ->addDefaultsIfNotSet() - ->children() - ->scalarNode(self::PARAMETER_CONSOLE_ENABLED) - ->info('Enable debug screen for console.') - ->defaultNull() - ->end() - ->scalarNode(self::PARAMETER_CONSOLE_LOG_DIRECTORY) - ->info( - 'Directory, where BlueScreens for console will be stored.' - . ' If you are already using Tracy for logging, set this to the same.' - . sprintf(' This will be only used, if given %s instance does not have a directory set.', TracyLogger::class) - ) - ->defaultValue($this->kernelLogsDir) - ->end() - ->scalarNode(self::PARAMETER_CONSOLE_BROWSER) - ->info( - 'Configure this to open generated BlueScreen in your browser.' - . ' Configuration option may be for example \'google-chrome\' or \'firefox\'' - . ' and it will be invoked as a shell command.' - ) - ->defaultNull() - ->end() - ->integerNode(self::PARAMETER_CONSOLE_LISTENER_PRIORITY) - ->info('Priority with which the listener will be registered.') - ->defaultValue(0) - ->end() - ->end() - ->end() - ->arrayNode(self::SECTION_BLUE_SCREEN) - ->addDefaultsIfNotSet() - ->children() - ->arrayNode(self::PARAMETER_COLLAPSE_PATHS) - ->info('Add paths which should be collapsed (for external/compiled code) so that actual error is expanded.') - ->prototype('scalar') - ->end() - ->defaultValue([ - $this->kernelRootDir . '/bootstrap.php.cache', - $this->kernelCacheDir, - ]) - ->end() - ->end() - ->end() - ->end() - ->end(); + public function getConfigTreeBuilder() : TreeBuilder + { + $treeBuilder = new TreeBuilder($this->alias); + $rootNode = $treeBuilder->getRootNode(); + assert($rootNode instanceof ArrayNodeDefinition); - return $treeBuilder; - } + $rootNode + ->children() + ->arrayNode(self::SECTION_CONTROLLER) + ->addDefaultsIfNotSet() + ->children() + ->scalarNode(self::PARAMETER_CONTROLLER_ENABLED) + ->info('Enable debug screen for controllers.') + ->defaultNull() + ->end() + ->integerNode(self::PARAMETER_CONTROLLER_LISTENER_PRIORITY) + ->info('Priority with which the listener will be registered.') + ->defaultValue(0) + ->end() + ->end() + ->end() + ->arrayNode(self::SECTION_CONSOLE) + ->addDefaultsIfNotSet() + ->children() + ->scalarNode(self::PARAMETER_CONSOLE_ENABLED) + ->info('Enable debug screen for console.') + ->defaultNull() + ->end() + ->scalarNode(self::PARAMETER_CONSOLE_LOG_DIRECTORY) + ->info( + 'Directory, where BlueScreens for console will be stored.' + . ' If you are already using Tracy for logging, set this to the same.' + . sprintf( + ' This will be only used, if given %s instance does not have a directory set.', + TracyLogger::class + ) + ) + ->defaultValue($this->kernelLogsDir) + ->end() + ->scalarNode(self::PARAMETER_CONSOLE_BROWSER) + ->info( + 'Configure this to open generated BlueScreen in your browser.' + . ' Configuration option may be for example \'google-chrome\' or \'firefox\'' + . ' and it will be invoked as a shell command.' + ) + ->defaultNull() + ->end() + ->integerNode(self::PARAMETER_CONSOLE_LISTENER_PRIORITY) + ->info('Priority with which the listener will be registered.') + ->defaultValue(0) + ->end() + ->end() + ->end() + ->arrayNode(self::SECTION_BLUE_SCREEN) + ->addDefaultsIfNotSet() + ->children() + ->arrayNode(self::PARAMETER_COLLAPSE_PATHS) + //phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong + ->info('Add paths which should be collapsed (for external/compiled code) so that actual error is expanded.') + ->prototype('scalar') + ->end() + ->defaultValue([ + $this->kernelRootDir . '/bootstrap.php.cache', + $this->kernelCacheDir, + ]) + ->end() + ->end() + ->end() + ->end() + ->end(); + return $treeBuilder; + } } diff --git a/src/DependencyInjection/Exception/TwigBundleRequired.php b/src/DependencyInjection/Exception/TwigBundleRequired.php new file mode 100644 index 0000000..f4092e1 --- /dev/null +++ b/src/DependencyInjection/Exception/TwigBundleRequired.php @@ -0,0 +1,16 @@ +hasExtension(self::TWIG_BUNDLE_ALIAS)) { - throw new \VasekPurchart\TracyBlueScreenBundle\DependencyInjection\TwigBundleRequiredException(); - } - - $container->loadFromExtension(self::TWIG_BUNDLE_ALIAS, [ - 'paths' => [ - $this->getTemplatesDirectory() => self::TWIG_TEMPLATES_NAMESPACE, - ], - ]); - } - - private function getTemplatesDirectory(): string - { - $bundleClassReflection = new ReflectionClass(TracyBlueScreenBundle::class); - $srcDirectoryPath = dirname($bundleClassReflection->getFileName()); - - return $srcDirectoryPath . '/Resources/views'; - } - - /** - * @param mixed[] $mergedConfig - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - */ - public function loadInternal(array $mergedConfig, ContainerBuilder $container): void - { - $container->setParameter( - self::CONTAINER_PARAMETER_BLUE_SCREEN_COLLAPSE_PATHS, - $mergedConfig[Configuration::SECTION_BLUE_SCREEN][Configuration::PARAMETER_COLLAPSE_PATHS] - ); - $container->setParameter( - self::CONTAINER_PARAMETER_CONSOLE_BROWSER, - $mergedConfig[Configuration::SECTION_CONSOLE][Configuration::PARAMETER_CONSOLE_BROWSER] - ); - $container->setParameter( - self::CONTAINER_PARAMETER_CONSOLE_LISTENER_PRIORITY, - $mergedConfig[Configuration::SECTION_CONSOLE][Configuration::PARAMETER_CONSOLE_LISTENER_PRIORITY] - ); - $container->setParameter( - self::CONTAINER_PARAMETER_CONSOLE_LOG_DIRECTORY, - $mergedConfig[Configuration::SECTION_CONSOLE][Configuration::PARAMETER_CONSOLE_LOG_DIRECTORY] - ); - $container->setParameter( - self::CONTAINER_PARAMETER_CONTROLLER_LISTENER_PRIORITY, - $mergedConfig[Configuration::SECTION_CONTROLLER][Configuration::PARAMETER_CONTROLLER_LISTENER_PRIORITY] - ); - - $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/config')); - $loader->load('services.yml'); - - $environment = $container->getParameter('kernel.environment'); - $debug = $container->getParameter('kernel.debug'); - - if ($this->isEnabled( - $mergedConfig[Configuration::SECTION_CONSOLE][Configuration::PARAMETER_CONSOLE_ENABLED], - $environment, - $debug - )) { - $loader->load('console_listener.yml'); - } - if ($this->isEnabled( - $mergedConfig[Configuration::SECTION_CONTROLLER][Configuration::PARAMETER_CONTROLLER_ENABLED], - $environment, - $debug - )) { - $loader->load('controller_listener.yml'); - } - } - - /** - * @param mixed[] $config - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * @return \VasekPurchart\TracyBlueScreenBundle\DependencyInjection\Configuration - */ - public function getConfiguration(array $config, ContainerBuilder $container): Configuration - { - return new Configuration( - $this->getAlias(), - $container->getParameter('kernel.root_dir'), - $container->getParameter('kernel.logs_dir'), - $container->getParameter('kernel.cache_dir') - ); - } - - private function isEnabled(?bool $configOption, string $environment, bool $debug): bool - { - if ($configOption === null) { - return $environment === 'dev' && $debug === true; - } - - return $configOption; - } +use function assert; +use function dirname; +//phpcs:disable SlevomatCodingStandard.Files.LineLength.LineTooLong +final class TracyBlueScreenExtension extends ConfigurableExtension implements PrependExtensionInterface +{ + public const CONTAINER_PARAMETER_BLUE_SCREEN_COLLAPSE_PATHS = 'cdn77.tracy_blue_screen.blue_screen.collapse_paths'; + public const CONTAINER_PARAMETER_CONSOLE_BROWSER = 'cdn77.tracy_blue_screen.console.browser'; + public const CONTAINER_PARAMETER_CONSOLE_LISTENER_PRIORITY = 'cdn77.tracy_blue_screen.console.listener_priority'; + public const CONTAINER_PARAMETER_CONSOLE_LOG_DIRECTORY = 'cdn77.tracy_blue_screen.console.log_directory'; + public const CONTAINER_PARAMETER_CONTROLLER_LISTENER_PRIORITY = 'cdn77.tracy_blue_screen.controller.listener_priority'; + private const TWIG_BUNDLE_ALIAS = 'twig'; + private const TWIG_TEMPLATES_NAMESPACE = 'Twig'; + + public function prepend(ContainerBuilder $container) : void + { + if (! $container->hasExtension(self::TWIG_BUNDLE_ALIAS)) { + throw new TwigBundleRequired(); + } + + $container->loadFromExtension( + self::TWIG_BUNDLE_ALIAS, + [ + 'paths' => [ + $this->getTemplatesDirectory() => self::TWIG_TEMPLATES_NAMESPACE, + ], + ] + ); + } + + /** @param mixed[] $mergedConfig */ + public function loadInternal(array $mergedConfig, ContainerBuilder $container) : void + { + $container->setParameter( + self::CONTAINER_PARAMETER_BLUE_SCREEN_COLLAPSE_PATHS, + $mergedConfig[Configuration::SECTION_BLUE_SCREEN][Configuration::PARAMETER_COLLAPSE_PATHS] + ); + $container->setParameter( + self::CONTAINER_PARAMETER_CONSOLE_BROWSER, + $mergedConfig[Configuration::SECTION_CONSOLE][Configuration::PARAMETER_CONSOLE_BROWSER] + ); + $container->setParameter( + self::CONTAINER_PARAMETER_CONSOLE_LISTENER_PRIORITY, + $mergedConfig[Configuration::SECTION_CONSOLE][Configuration::PARAMETER_CONSOLE_LISTENER_PRIORITY] + ); + $container->setParameter( + self::CONTAINER_PARAMETER_CONSOLE_LOG_DIRECTORY, + $mergedConfig[Configuration::SECTION_CONSOLE][Configuration::PARAMETER_CONSOLE_LOG_DIRECTORY] + ); + $container->setParameter( + self::CONTAINER_PARAMETER_CONTROLLER_LISTENER_PRIORITY, + $mergedConfig[Configuration::SECTION_CONTROLLER][Configuration::PARAMETER_CONTROLLER_LISTENER_PRIORITY] + ); + + $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/config')); + $loader->load('services.yml'); + + $environment = $container->getParameter('kernel.environment'); + $debug = $container->getParameter('kernel.debug'); + + if ( + $this->isEnabled( + $mergedConfig[Configuration::SECTION_CONSOLE][Configuration::PARAMETER_CONSOLE_ENABLED], + $environment, + $debug + ) + ) { + $loader->load('console_listener.yml'); + } + + if ( + ! $this->isEnabled( + $mergedConfig[Configuration::SECTION_CONTROLLER][Configuration::PARAMETER_CONTROLLER_ENABLED], + $environment, + $debug + ) + ) { + return; + } + + $loader->load('controller_listener.yml'); + } + + /** @param mixed[] $config */ + public function getConfiguration(array $config, ContainerBuilder $container) : Configuration + { + return new Configuration( + $this->getAlias(), + $container->getParameter('kernel.project_dir'), + $container->getParameter('kernel.logs_dir'), + $container->getParameter('kernel.cache_dir') + ); + } + + private function getTemplatesDirectory() : string + { + $bundleClassReflection = new ReflectionClass(TracyBlueScreenBundle::class); + $fileName = $bundleClassReflection->getFileName(); + assert($fileName !== false); + + $srcDirectoryPath = dirname($fileName); + + return $srcDirectoryPath . '/Resources/views'; + } + + private function isEnabled(?bool $configOption, string $environment, bool $debug) : bool + { + if ($configOption === null) { + return $environment === 'dev' && $debug === true; + } + + return $configOption; + } } diff --git a/src/DependencyInjection/config/console_listener.yml b/src/DependencyInjection/config/console_listener.yml index 32fe6d9..cf0df03 100644 --- a/src/DependencyInjection/config/console_listener.yml +++ b/src/DependencyInjection/config/console_listener.yml @@ -1,13 +1,13 @@ services: - vasek_purchart.tracy_blue_screen.blue_screen.console_blue_screen_error_listener: - class: VasekPurchart\TracyBlueScreenBundle\BlueScreen\ConsoleBlueScreenErrorListener + cdn77.tracy_blue_screen.blue_screen.console_blue_screen_error_listener: + class: Cdn77\TracyBlueScreenBundle\BlueScreen\ConsoleBlueScreenErrorListener arguments: - - '@vasek_purchart.tracy_blue_screen.tracy.logger' - - '@vasek_purchart.tracy_blue_screen.tracy.blue_screen' - - '%vasek_purchart.tracy_blue_screen.console.log_directory%' - - '%vasek_purchart.tracy_blue_screen.console.browser%' + - '@cdn77.tracy_blue_screen.tracy.logger' + - '@cdn77.tracy_blue_screen.tracy.blue_screen' + - '%cdn77.tracy_blue_screen.console.log_directory%' + - '%cdn77.tracy_blue_screen.console.browser%' tags: - name: kernel.event_listener event: console.error - priority: '%vasek_purchart.tracy_blue_screen.console.listener_priority%' + priority: '%cdn77.tracy_blue_screen.console.listener_priority%' diff --git a/src/DependencyInjection/config/controller_listener.yml b/src/DependencyInjection/config/controller_listener.yml index f298839..33e371b 100644 --- a/src/DependencyInjection/config/controller_listener.yml +++ b/src/DependencyInjection/config/controller_listener.yml @@ -1,10 +1,10 @@ services: - vasek_purchart.tracy_blue_screen.blue_screen.controller_blue_screen_exception_listener: - class: VasekPurchart\TracyBlueScreenBundle\BlueScreen\ControllerBlueScreenExceptionListener + cdn77.tracy_blue_screen.blue_screen.controller_blue_screen_exception_listener: + class: Cdn77\TracyBlueScreenBundle\BlueScreen\ControllerBlueScreenExceptionListener arguments: - - '@vasek_purchart.tracy_blue_screen.tracy.blue_screen' + - '@cdn77.tracy_blue_screen.tracy.blue_screen' tags: - name: kernel.event_listener event: kernel.exception - priority: '%vasek_purchart.tracy_blue_screen.controller.listener_priority%' + priority: '%cdn77.tracy_blue_screen.controller.listener_priority%' diff --git a/src/DependencyInjection/config/services.yml b/src/DependencyInjection/config/services.yml index c670640..a46ede0 100644 --- a/src/DependencyInjection/config/services.yml +++ b/src/DependencyInjection/config/services.yml @@ -1,16 +1,16 @@ services: - vasek_purchart.tracy_blue_screen.tracy.blue_screen: '@vasek_purchart.tracy_blue_screen.tracy.blue_screen.default' + cdn77.tracy_blue_screen.tracy.blue_screen: '@cdn77.tracy_blue_screen.tracy.blue_screen.default' - vasek_purchart.tracy_blue_screen.tracy.blue_screen.default: + cdn77.tracy_blue_screen.tracy.blue_screen.default: class: Tracy\BlueScreen - factory: [VasekPurchart\TracyBlueScreenBundle\BlueScreen\BlueScreenFactory, create] + factory: [Cdn77\TracyBlueScreenBundle\BlueScreen\BlueScreenFactory, create] arguments: - - '%vasek_purchart.tracy_blue_screen.blue_screen.collapse_paths%' + - '%cdn77.tracy_blue_screen.blue_screen.collapse_paths%' public: false - vasek_purchart.tracy_blue_screen.tracy.logger: '@vasek_purchart.tracy_blue_screen.tracy.logger.default' + cdn77.tracy_blue_screen.tracy.logger: '@cdn77.tracy_blue_screen.tracy.logger.default' - vasek_purchart.tracy_blue_screen.tracy.logger.default: + cdn77.tracy_blue_screen.tracy.logger.default: class: Tracy\Logger factory: [Tracy\Debugger, getLogger] public: false diff --git a/src/DependencyInjection/exceptions/TwigBundleRequiredException.php b/src/DependencyInjection/exceptions/TwigBundleRequiredException.php deleted file mode 100644 index 1decb6d..0000000 --- a/src/DependencyInjection/exceptions/TwigBundleRequiredException.php +++ /dev/null @@ -1,15 +0,0 @@ -createMock(Command::class); - $input = $this->createMock(InputInterface::class); - $output = $this->createMock(OutputInterface::class); - $output - ->expects($this->once()) - ->method('writeln') - ->with($this->stringContains('saved in file')); - $exception = new \Exception('Foobar!'); - - $event = new ConsoleErrorEvent($input, $output, $exception, $command); - - $logger = $this->createMock(TracyLogger::class); - $logger - ->expects($this->once()) - ->method('getExceptionFile') - ->with($exception) - ->will($this->returnValue($file)); - - $blueScreen = $this->createMock(BlueScreen::class); - $blueScreen - ->expects($this->once()) - ->method('renderToFile') - ->with($exception, $file); - - $listener = new ConsoleBlueScreenErrorListener( - $logger, - $blueScreen, - $directory, - null - ); - $listener->onConsoleError($event); - } - - public function testUsesErrorOutputIfPossible(): void - { - vfsStream::setup('tracy'); - $directory = vfsStream::url('tracy'); - $file = $directory . '/exception.html'; - - $command = $this->createMock(Command::class); - $input = $this->createMock(InputInterface::class); - $errorOutput = $this->createMock(OutputInterface::class); - $errorOutput - ->expects($this->once()) - ->method('writeln') - ->with($this->stringContains('saved in file')); - $output = $this->createMock(ConsoleOutputInterface::class); - $output - ->expects($this->once()) - ->method('getErrorOutput') - ->will($this->returnValue($errorOutput)); - - $exception = new \Exception('Foobar!'); - - $event = new ConsoleErrorEvent($input, $output, $exception, $command); - - $logger = $this->createMock(TracyLogger::class); - $logger - ->expects($this->once()) - ->method('getExceptionFile') - ->with($exception) - ->will($this->returnValue($file)); - - $blueScreen = $this->createMock(BlueScreen::class); - $blueScreen - ->expects($this->once()) - ->method('renderToFile') - ->with($exception, $file); - - $listener = new ConsoleBlueScreenErrorListener( - $logger, - $blueScreen, - $directory, - null - ); - $listener->onConsoleError($event); - } - - public function testMissingLogDir(): void - { - $command = $this->createMock(Command::class); - $input = $this->createMock(InputInterface::class); - $output = $this->createMock(OutputInterface::class); - $exception = new \Exception('Foobar!'); - - $event = new ConsoleErrorEvent($input, $output, $exception, $command); - - $logger = $this->createMock(TracyLogger::class); - $blueScreen = $this->createMock(BlueScreen::class); - - $listener = new ConsoleBlueScreenErrorListener( - $logger, - $blueScreen, - null, - null - ); - - $this->expectException(\InvalidArgumentException::class); - - $listener->onConsoleError($event); - } - + public function testLogTracy() : void + { + vfsStream::setup('tracy'); + $directory = vfsStream::url('tracy'); + $file = $directory . '/exception.html'; + + $command = $this->createMock(Command::class); + $input = $this->createMock(InputInterface::class); + $output = $this->createMock(OutputInterface::class); + $output + ->expects(self::once()) + ->method('writeln') + ->with(self::stringContains('saved in file')); + $exception = new Exception('Foobar!'); + + $event = new ConsoleErrorEvent($input, $output, $exception, $command); + + $logger = $this->createMock(TracyLogger::class); + $logger + ->expects(self::once()) + ->method('getExceptionFile') + ->with($exception) + ->willReturn($file); + + $blueScreen = $this->createMock(BlueScreen::class); + $blueScreen + ->expects(self::once()) + ->method('renderToFile') + ->with($exception, $file); + + $listener = new ConsoleBlueScreenErrorListener( + $logger, + $blueScreen, + $directory, + null + ); + $listener->onConsoleError($event); + } + + public function testUsesErrorOutputIfPossible() : void + { + vfsStream::setup('tracy'); + $directory = vfsStream::url('tracy'); + $file = $directory . '/exception.html'; + + $command = $this->createMock(Command::class); + $input = $this->createMock(InputInterface::class); + $errorOutput = $this->createMock(OutputInterface::class); + $errorOutput + ->expects(self::once()) + ->method('writeln') + ->with(self::stringContains('saved in file')); + $output = $this->createMock(ConsoleOutputInterface::class); + $output + ->expects(self::once()) + ->method('getErrorOutput') + ->willReturn($errorOutput); + + $exception = new Exception('Foobar!'); + + $event = new ConsoleErrorEvent($input, $output, $exception, $command); + + $logger = $this->createMock(TracyLogger::class); + $logger + ->expects(self::once()) + ->method('getExceptionFile') + ->with($exception) + ->willReturn($file); + + $blueScreen = $this->createMock(BlueScreen::class); + $blueScreen + ->expects(self::once()) + ->method('renderToFile') + ->with($exception, $file); + + $listener = new ConsoleBlueScreenErrorListener( + $logger, + $blueScreen, + $directory, + null + ); + $listener->onConsoleError($event); + } + + public function testMissingLogDir() : void + { + $command = $this->createMock(Command::class); + $input = $this->createMock(InputInterface::class); + $output = $this->createMock(OutputInterface::class); + $exception = new Exception('Foobar!'); + + $event = new ConsoleErrorEvent($input, $output, $exception, $command); + + $logger = $this->createMock(TracyLogger::class); + $blueScreen = $this->createMock(BlueScreen::class); + + $listener = new ConsoleBlueScreenErrorListener( + $logger, + $blueScreen, + null, + null + ); + + $this->expectException(InvalidArgumentException::class); + + $listener->onConsoleError($event); + } } diff --git a/tests/BlueScreen/ControllerBlueScreenExceptionListenerTest.php b/tests/BlueScreen/ControllerBlueScreenExceptionListenerTest.php index c4d0b29..0c0b6e7 100644 --- a/tests/BlueScreen/ControllerBlueScreenExceptionListenerTest.php +++ b/tests/BlueScreen/ControllerBlueScreenExceptionListenerTest.php @@ -1,34 +1,35 @@ createMock(HttpKernelInterface::class); - $request = new Request(); - $requestType = HttpKernelInterface::MASTER_REQUEST; - $exception = new \Exception('Foobar!'); - - $event = new GetResponseForExceptionEvent($kernel, $request, $requestType, $exception); - - $blueScreen = $this->createMock(BlueScreen::class); - $blueScreen - ->expects($this->once()) - ->method('render') - ->with($exception); - - $listener = new ControllerBlueScreenExceptionListener($blueScreen); - $listener->onKernelException($event); - } - + public function testRenderTracy() : void + { + $kernel = $this->createMock(HttpKernelInterface::class); + $request = new Request(); + $requestType = HttpKernelInterface::MASTER_REQUEST; + $exception = new Exception('Foobar!'); + + $event = new ExceptionEvent($kernel, $request, $requestType, $exception); + + $blueScreen = $this->createMock(BlueScreen::class); + $blueScreen + ->expects(self::once()) + ->method('render') + ->with($exception); + + $listener = new ControllerBlueScreenExceptionListener($blueScreen); + $listener->onKernelException($event); + } } diff --git a/tests/DependencyInjection/TracyBlueScreenExtensionConsoleTest.php b/tests/DependencyInjection/TracyBlueScreenExtensionConsoleTest.php index af73d3a..79abd48 100644 --- a/tests/DependencyInjection/TracyBlueScreenExtensionConsoleTest.php +++ b/tests/DependencyInjection/TracyBlueScreenExtensionConsoleTest.php @@ -1,143 +1,180 @@ setParameter('kernel.root_dir', __DIR__); - $this->setParameter('kernel.project_dir', __DIR__); - $this->setParameter('kernel.logs_dir', __DIR__ . '/tests-logs-dir'); - $this->setParameter('kernel.cache_dir', __DIR__ . '/tests-cache-dir'); - $this->setParameter('kernel.environment', 'dev'); - $this->setParameter('kernel.debug', true); - $this->setParameter('kernel.bundles_metadata', [ - 'TwigBundle' => [ - 'namespace' => 'Symfony\\Bundle\\TwigBundle', - 'path' => __DIR__, - ], - ]); - } - - /** - * @return \Symfony\Component\DependencyInjection\Extension\ExtensionInterface[] - */ - protected function getContainerExtensions(): array - { - return [ - new TracyBlueScreenExtension(), - new TwigExtension(), - ]; - } - - public function testEnabledByDefault(): void - { - $this->loadExtensions(); - - $this->assertContainerBuilderHasService('vasek_purchart.tracy_blue_screen.blue_screen.console_blue_screen_error_listener', ConsoleBlueScreenErrorListener::class); - $this->assertContainerBuilderHasServiceDefinitionWithTag('vasek_purchart.tracy_blue_screen.blue_screen.console_blue_screen_error_listener', 'kernel.event_listener', [ - 'event' => 'console.error', - 'priority' => '%' . TracyBlueScreenExtension::CONTAINER_PARAMETER_CONSOLE_LISTENER_PRIORITY . '%', - ]); - } - - public function testDisabled(): void - { - $this->loadExtensions([ - 'tracy_blue_screen' => [ - 'console' => [ - 'enabled' => false, - ], - ], - ]); - - $this->assertContainerBuilderNotHasService('vasek_purchart.tracy_blue_screen.blue_screen.console_blue_screen_error_listener'); - } - - public function testEnabled(): void - { - $this->loadExtensions([ - 'tracy_blue_screen' => [ - 'console' => [ - 'enabled' => true, - ], - ], - ]); - - $this->assertContainerBuilderHasService('vasek_purchart.tracy_blue_screen.blue_screen.console_blue_screen_error_listener', ConsoleBlueScreenErrorListener::class); - $this->assertContainerBuilderHasServiceDefinitionWithTag('vasek_purchart.tracy_blue_screen.blue_screen.console_blue_screen_error_listener', 'kernel.event_listener', [ - 'event' => 'console.error', - 'priority' => '%' . TracyBlueScreenExtension::CONTAINER_PARAMETER_CONSOLE_LISTENER_PRIORITY . '%', - ]); - } - - public function testDefaultLogsDirIsKernelLogsDir(): void - { - $this->loadExtensions(); - - $this->assertContainerBuilderHasParameter(TracyBlueScreenExtension::CONTAINER_PARAMETER_CONSOLE_LOG_DIRECTORY, __DIR__ . '/tests-logs-dir'); - } - - public function testCustomLogsDir(): void - { - $this->loadExtensions([ - 'tracy_blue_screen' => [ - 'console' => [ - 'log_directory' => __DIR__ . '/foobar', - ], - ], - ]); - - $this->assertContainerBuilderHasParameter(TracyBlueScreenExtension::CONTAINER_PARAMETER_CONSOLE_LOG_DIRECTORY, __DIR__ . '/foobar'); - } - - public function testDefaultBrowserIsNull(): void - { - $this->loadExtensions(); - - $this->assertContainerBuilderHasParameter(TracyBlueScreenExtension::CONTAINER_PARAMETER_CONSOLE_BROWSER, null); - } - - public function testCustomBrowser(): void - { - $this->loadExtensions([ - 'tracy_blue_screen' => [ - 'console' => [ - 'browser' => 'google-chrome', - ], - ], - ]); - - $this->assertContainerBuilderHasParameter(TracyBlueScreenExtension::CONTAINER_PARAMETER_CONSOLE_BROWSER, 'google-chrome'); - } - - public function testConfigureListenerPriority(): void - { - $this->loadExtensions([ - 'tracy_blue_screen' => [ - 'console' => [ - 'listener_priority' => 123, - ], - ], - ]); - - $this->assertContainerBuilderHasParameter(TracyBlueScreenExtension::CONTAINER_PARAMETER_CONSOLE_LISTENER_PRIORITY, 123); - } - - /** - * @param mixed[] $configuration format: extensionAlias(string) => configuration(mixed[]) - */ - private function loadExtensions(array $configuration = []): void - { - TracyBlueScreenExtensionTest::loadExtensionsToContainer($this->container, $configuration, $this->getMinimalConfiguration()); - } - + public function setUp() : void + { + parent::setUp(); + + $this->setParameter('kernel.project_dir', __DIR__); + $this->setParameter('kernel.logs_dir', __DIR__ . '/tests-logs-dir'); + $this->setParameter('kernel.cache_dir', __DIR__ . '/tests-cache-dir'); + $this->setParameter('kernel.environment', 'dev'); + $this->setParameter('kernel.debug', true); + $this->setParameter( + 'kernel.bundles_metadata', + [ + 'TwigBundle' => [ + 'namespace' => 'Symfony\\Bundle\\TwigBundle', + 'path' => __DIR__, + ], + ] + ); + } + + public function testEnabledByDefault() : void + { + $this->loadExtensions(); + + $this->assertContainerBuilderHasService( + 'cdn77.tracy_blue_screen.blue_screen.console_blue_screen_error_listener', + ConsoleBlueScreenErrorListener::class + ); + $this->assertContainerBuilderHasServiceDefinitionWithTag( + 'cdn77.tracy_blue_screen.blue_screen.console_blue_screen_error_listener', + 'kernel.event_listener', + [ + 'event' => 'console.error', + 'priority' => '%' . TracyBlueScreenExtension::CONTAINER_PARAMETER_CONSOLE_LISTENER_PRIORITY . '%', + ] + ); + } + + public function testDisabled() : void + { + $this->loadExtensions( + [ + 'tracy_blue_screen' => [ + 'console' => ['enabled' => false], + ], + ] + ); + + $this->assertContainerBuilderNotHasService( + 'cdn77.tracy_blue_screen.blue_screen.console_blue_screen_error_listener' + ); + } + + public function testEnabled() : void + { + $this->loadExtensions( + [ + 'tracy_blue_screen' => [ + 'console' => ['enabled' => true], + ], + ] + ); + + $this->assertContainerBuilderHasService( + 'cdn77.tracy_blue_screen.blue_screen.console_blue_screen_error_listener', + ConsoleBlueScreenErrorListener::class + ); + $this->assertContainerBuilderHasServiceDefinitionWithTag( + 'cdn77.tracy_blue_screen.blue_screen.console_blue_screen_error_listener', + 'kernel.event_listener', + [ + 'event' => 'console.error', + 'priority' => '%' . TracyBlueScreenExtension::CONTAINER_PARAMETER_CONSOLE_LISTENER_PRIORITY . '%', + ] + ); + } + + public function testDefaultLogsDirIsKernelLogsDir() : void + { + $this->loadExtensions(); + + $this->assertContainerBuilderHasParameter( + TracyBlueScreenExtension::CONTAINER_PARAMETER_CONSOLE_LOG_DIRECTORY, + __DIR__ . '/tests-logs-dir' + ); + } + + public function testCustomLogsDir() : void + { + $this->loadExtensions( + [ + 'tracy_blue_screen' => [ + 'console' => [ + 'log_directory' => __DIR__ . '/foobar', + ], + ], + ] + ); + + $this->assertContainerBuilderHasParameter( + TracyBlueScreenExtension::CONTAINER_PARAMETER_CONSOLE_LOG_DIRECTORY, + __DIR__ . '/foobar' + ); + } + + public function testDefaultBrowserIsNull() : void + { + $this->loadExtensions(); + + $this->assertContainerBuilderHasParameter( + TracyBlueScreenExtension::CONTAINER_PARAMETER_CONSOLE_BROWSER, + null + ); + } + + public function testCustomBrowser() : void + { + $this->loadExtensions( + [ + 'tracy_blue_screen' => [ + 'console' => ['browser' => 'google-chrome'], + ], + ] + ); + + $this->assertContainerBuilderHasParameter( + TracyBlueScreenExtension::CONTAINER_PARAMETER_CONSOLE_BROWSER, + 'google-chrome' + ); + } + + public function testConfigureListenerPriority() : void + { + $this->loadExtensions( + [ + 'tracy_blue_screen' => [ + 'console' => ['listener_priority' => 123], + ], + ] + ); + + $this->assertContainerBuilderHasParameter( + TracyBlueScreenExtension::CONTAINER_PARAMETER_CONSOLE_LISTENER_PRIORITY, + 123 + ); + } + + /** @return ExtensionInterface[] */ + protected function getContainerExtensions() : array + { + return [ + new TracyBlueScreenExtension(), + new TwigExtension(), + ]; + } + + /** @param mixed[] $configuration format: extensionAlias(string) => configuration(mixed[]) */ + private function loadExtensions(array $configuration = []) : void + { + TracyBlueScreenExtensionTest::loadExtensionsToContainer( + $this->container, + $configuration, + $this->getMinimalConfiguration() + ); + } } diff --git a/tests/DependencyInjection/TracyBlueScreenExtensionControllerTest.php b/tests/DependencyInjection/TracyBlueScreenExtensionControllerTest.php index 17234c4..89a659c 100644 --- a/tests/DependencyInjection/TracyBlueScreenExtensionControllerTest.php +++ b/tests/DependencyInjection/TracyBlueScreenExtensionControllerTest.php @@ -1,103 +1,126 @@ setParameter('kernel.root_dir', __DIR__); - $this->setParameter('kernel.project_dir', __DIR__); - $this->setParameter('kernel.logs_dir', __DIR__); - $this->setParameter('kernel.cache_dir', __DIR__ . '/tests-cache-dir'); - $this->setParameter('kernel.environment', 'dev'); - $this->setParameter('kernel.debug', true); - $this->setParameter('kernel.bundles_metadata', [ - 'TwigBundle' => [ - 'namespace' => 'Symfony\\Bundle\\TwigBundle', - 'path' => __DIR__, - ], - ]); - } - - /** - * @return \Symfony\Component\DependencyInjection\Extension\ExtensionInterface[] - */ - protected function getContainerExtensions(): array - { - return [ - new TracyBlueScreenExtension(), - new TwigExtension(), - ]; - } - - public function testEnabledByDefault(): void - { - $this->loadExtensions(); - - $this->assertContainerBuilderHasService('vasek_purchart.tracy_blue_screen.blue_screen.controller_blue_screen_exception_listener', ControllerBlueScreenExceptionListener::class); - $this->assertContainerBuilderHasServiceDefinitionWithTag('vasek_purchart.tracy_blue_screen.blue_screen.controller_blue_screen_exception_listener', 'kernel.event_listener', [ - 'event' => 'kernel.exception', - 'priority' => '%' . TracyBlueScreenExtension::CONTAINER_PARAMETER_CONTROLLER_LISTENER_PRIORITY . '%', - ]); - } - - public function testDisabled(): void - { - $this->loadExtensions([ - 'tracy_blue_screen' => [ - 'controller' => [ - 'enabled' => false, - ], - ], - ]); - - $this->assertContainerBuilderNotHasService('vasek_purchart.tracy_blue_screen.blue_screen.controller_blue_screen_exception_listener'); - } - - public function testEnabled(): void - { - $this->loadExtensions([ - 'tracy_blue_screen' => [ - 'controller' => [ - 'enabled' => true, - ], - ], - ]); - - $this->assertContainerBuilderHasService('vasek_purchart.tracy_blue_screen.blue_screen.controller_blue_screen_exception_listener', ControllerBlueScreenExceptionListener::class); - $this->assertContainerBuilderHasServiceDefinitionWithTag('vasek_purchart.tracy_blue_screen.blue_screen.controller_blue_screen_exception_listener', 'kernel.event_listener', [ - 'event' => 'kernel.exception', - 'priority' => '%' . TracyBlueScreenExtension::CONTAINER_PARAMETER_CONTROLLER_LISTENER_PRIORITY . '%', - ]); - } - - public function testConfigureListenerPriority(): void - { - $this->loadExtensions([ - 'tracy_blue_screen' => [ - 'controller' => [ - 'listener_priority' => 123, - ], - ], - ]); - - $this->assertContainerBuilderHasParameter(TracyBlueScreenExtension::CONTAINER_PARAMETER_CONTROLLER_LISTENER_PRIORITY, 123); - } - - /** - * @param mixed[] $configuration format: extensionAlias(string) => configuration(mixed[]) - */ - private function loadExtensions(array $configuration = []): void - { - TracyBlueScreenExtensionTest::loadExtensionsToContainer($this->container, $configuration, $this->getMinimalConfiguration()); - } - + public function setUp() : void + { + parent::setUp(); + + $this->setParameter('kernel.project_dir', __DIR__); + $this->setParameter('kernel.logs_dir', __DIR__); + $this->setParameter('kernel.cache_dir', __DIR__ . '/tests-cache-dir'); + $this->setParameter('kernel.environment', 'dev'); + $this->setParameter('kernel.debug', true); + $this->setParameter( + 'kernel.bundles_metadata', + [ + 'TwigBundle' => [ + 'namespace' => 'Symfony\\Bundle\\TwigBundle', + 'path' => __DIR__, + ], + ] + ); + } + + public function testEnabledByDefault() : void + { + $this->loadExtensions(); + + $this->assertContainerBuilderHasService( + 'cdn77.tracy_blue_screen.blue_screen.controller_blue_screen_exception_listener', + ControllerBlueScreenExceptionListener::class + ); + $this->assertContainerBuilderHasServiceDefinitionWithTag( + 'cdn77.tracy_blue_screen.blue_screen.controller_blue_screen_exception_listener', + 'kernel.event_listener', + [ + 'event' => 'kernel.exception', + 'priority' => '%' . TracyBlueScreenExtension::CONTAINER_PARAMETER_CONTROLLER_LISTENER_PRIORITY . '%', + ] + ); + } + + public function testDisabled() : void + { + $this->loadExtensions( + [ + 'tracy_blue_screen' => [ + 'controller' => ['enabled' => false], + ], + ] + ); + + $this->assertContainerBuilderNotHasService( + 'cdn77.tracy_blue_screen.blue_screen.controller_blue_screen_exception_listener' + ); + } + + public function testEnabled() : void + { + $this->loadExtensions( + [ + 'tracy_blue_screen' => [ + 'controller' => ['enabled' => true], + ], + ] + ); + + $this->assertContainerBuilderHasService( + 'cdn77.tracy_blue_screen.blue_screen.controller_blue_screen_exception_listener', + ControllerBlueScreenExceptionListener::class + ); + $this->assertContainerBuilderHasServiceDefinitionWithTag( + 'cdn77.tracy_blue_screen.blue_screen.controller_blue_screen_exception_listener', + 'kernel.event_listener', + [ + 'event' => 'kernel.exception', + 'priority' => '%' . TracyBlueScreenExtension::CONTAINER_PARAMETER_CONTROLLER_LISTENER_PRIORITY . '%', + ] + ); + } + + public function testConfigureListenerPriority() : void + { + $this->loadExtensions( + [ + 'tracy_blue_screen' => [ + 'controller' => ['listener_priority' => 123], + ], + ] + ); + + $this->assertContainerBuilderHasParameter( + TracyBlueScreenExtension::CONTAINER_PARAMETER_CONTROLLER_LISTENER_PRIORITY, + 123 + ); + } + + /** @return ExtensionInterface[] */ + protected function getContainerExtensions() : array + { + return [ + new TracyBlueScreenExtension(), + new TwigExtension(), + ]; + } + + /** @param mixed[] $configuration format: extensionAlias(string) => configuration(mixed[]) */ + private function loadExtensions(array $configuration = []) : void + { + TracyBlueScreenExtensionTest::loadExtensionsToContainer( + $this->container, + $configuration, + $this->getMinimalConfiguration() + ); + } } diff --git a/tests/DependencyInjection/TracyBlueScreenExtensionTest.php b/tests/DependencyInjection/TracyBlueScreenExtensionTest.php index 2a0fe0f..4c1c0f0 100644 --- a/tests/DependencyInjection/TracyBlueScreenExtensionTest.php +++ b/tests/DependencyInjection/TracyBlueScreenExtensionTest.php @@ -1,170 +1,191 @@ setParameter('kernel.root_dir', __DIR__); - $this->setParameter('kernel.project_dir', __DIR__); - $this->setParameter('kernel.logs_dir', __DIR__); - $this->setParameter('kernel.cache_dir', __DIR__ . '/tests-cache-dir'); - $this->setParameter('kernel.environment', 'dev'); - $this->setParameter('kernel.debug', true); - $this->setParameter('kernel.bundles_metadata', [ - 'TwigBundle' => [ - 'namespace' => 'Symfony\\Bundle\\TwigBundle', - 'path' => __DIR__, - ], - ]); - } - - /** - * @return \Symfony\Component\DependencyInjection\Extension\ExtensionInterface[] - */ - protected function getContainerExtensions(): array - { - return [ - new TracyBlueScreenExtension(), - new TwigExtension(), - ]; - } - - public function testDependsOnTwigBundle(): void - { - $containerBuilder = new ContainerBuilder(); - $extension = new TracyBlueScreenExtension(); - - $this->expectException(\VasekPurchart\TracyBlueScreenBundle\DependencyInjection\TwigBundleRequiredException::class); - $extension->prepend($containerBuilder); - } - - public function testOnlyAddCollapsePaths(): void - { - $this->loadExtensions(); - - $this->assertContainerBuilderHasService('vasek_purchart.tracy_blue_screen.tracy.blue_screen.default', BlueScreen::class); - - $blueScreen = $this->container->get('vasek_purchart.tracy_blue_screen.tracy.blue_screen.default'); - $collapsePaths = $blueScreen->collapsePaths; - - $this->assertArrayContainsStringPart('/bootstrap.php.cache', $collapsePaths); - $this->assertArrayContainsStringPart('/tests-cache-dir', $collapsePaths); - $this->assertArrayContainsStringPart('/vendor', $collapsePaths); - } - - public function testCollapseCacheDirsByDefault(): void - { - $this->loadExtensions(); - - $this->assertContainerBuilderHasParameter('vasek_purchart.tracy_blue_screen.blue_screen.collapse_paths'); - $collapsePaths = $this->container->getParameter('vasek_purchart.tracy_blue_screen.blue_screen.collapse_paths'); - - $this->assertArrayContainsStringPart('/bootstrap.php.cache', $collapsePaths); - $this->assertArrayContainsStringPart('/tests-cache-dir', $collapsePaths); - } - - public function testSetCollapseDirs(): void - { - $paths = [ - __DIR__ . '/foobar', - ]; - - $this->loadExtensions([ - 'tracy_blue_screen' => [ - 'blue_screen' => [ - 'collapse_paths' => $paths, - ], - ], - ]); - - $this->assertContainerBuilderHasParameter('vasek_purchart.tracy_blue_screen.blue_screen.collapse_paths'); - $collapsePaths = $this->container->getParameter('vasek_purchart.tracy_blue_screen.blue_screen.collapse_paths'); - - $this->assertEquals($paths, $collapsePaths); - } - - public function testEmptyCollapseDirs(): void - { - $this->loadExtensions([ - 'tracy_blue_screen' => [ - 'blue_screen' => [ - 'collapse_paths' => [], - ], - ], - ]); - - $this->assertContainerBuilderHasParameter('vasek_purchart.tracy_blue_screen.blue_screen.collapse_paths'); - $collapsePaths = $this->container->getParameter('vasek_purchart.tracy_blue_screen.blue_screen.collapse_paths'); - - $this->assertEmpty($collapsePaths); - } - - /** - * @param mixed[] $configuration format: extensionAlias(string) => configuration(mixed[]) - */ - private function loadExtensions(array $configuration = []): void - { - self::loadExtensionsToContainer($this->container, $configuration, $this->getMinimalConfiguration()); - } - - /** - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * @param mixed[] $configuration format: extensionAlias(string) => configuration(mixed[]) - * @param mixed[] $minimalConfiguration format: extensionAlias(string) => configuration(mixed[]) - */ - public static function loadExtensionsToContainer( - ContainerBuilder $container, - array $configuration = [], - array $minimalConfiguration = [] - ): void - { - $configurations = []; - foreach ($container->getExtensions() as $extensionAlias => $extension) { - $configurations[$extensionAlias] = []; - if (array_key_exists($extensionAlias, $minimalConfiguration)) { - $container->loadFromExtension($extensionAlias, $minimalConfiguration[$extensionAlias]); - $configurations[$extensionAlias][] = $minimalConfiguration[$extensionAlias]; - } - if (array_key_exists($extensionAlias, $configuration)) { - $container->loadFromExtension($extensionAlias, $configuration[$extensionAlias]); - $configurations[$extensionAlias][] = $configuration[$extensionAlias]; - } - } - foreach ($container->getExtensions() as $extensionAlias => $extension) { - if ($extension instanceof PrependExtensionInterface) { - $extension->prepend($container); - } - } - foreach ($container->getExtensions() as $extensionAlias => $extension) { - $extension->load($configurations[$extensionAlias], $container); - } - } - - /** - * @param string $string - * @param string[] $array - */ - private function assertArrayContainsStringPart(string $string, array $array): void - { - $found = false; - foreach ($array as $item) { - if (strpos($item, $string) !== false) { - $found = true; - break; - } - } - $this->assertTrue($found, sprintf('%s not found in any elements of the given %s', $string, var_export($array, true))); - } +use function array_key_exists; +use function assert; +use function sprintf; +use function strpos; +use function var_export; +final class TracyBlueScreenExtensionTest extends AbstractExtensionTestCase +{ + /** + * @param mixed[] $configuration format: extensionAlias(string) => configuration(mixed[]) + * @param mixed[] $minimalConfiguration format: extensionAlias(string) => configuration(mixed[]) + */ + public static function loadExtensionsToContainer( + ContainerBuilder $container, + array $configuration = [], + array $minimalConfiguration = [] + ) : void { + $configurations = []; + foreach ($container->getExtensions() as $extensionAlias => $extension) { + $configurations[$extensionAlias] = []; + if (array_key_exists($extensionAlias, $minimalConfiguration)) { + $container->loadFromExtension($extensionAlias, $minimalConfiguration[$extensionAlias]); + $configurations[$extensionAlias][] = $minimalConfiguration[$extensionAlias]; + } + + if (! array_key_exists($extensionAlias, $configuration)) { + continue; + } + + $container->loadFromExtension($extensionAlias, $configuration[$extensionAlias]); + $configurations[$extensionAlias][] = $configuration[$extensionAlias]; + } + + foreach ($container->getExtensions() as $extensionAlias => $extension) { + if (! ($extension instanceof PrependExtensionInterface)) { + continue; + } + + $extension->prepend($container); + } + + foreach ($container->getExtensions() as $extensionAlias => $extension) { + $extension->load($configurations[$extensionAlias], $container); + } + } + + public function setUp() : void + { + parent::setUp(); + + $this->setParameter('kernel.project_dir', __DIR__); + $this->setParameter('kernel.logs_dir', __DIR__); + $this->setParameter('kernel.cache_dir', __DIR__ . '/tests-cache-dir'); + $this->setParameter('kernel.environment', 'dev'); + $this->setParameter('kernel.debug', true); + $this->setParameter( + 'kernel.bundles_metadata', + [ + 'TwigBundle' => [ + 'namespace' => 'Symfony\\Bundle\\TwigBundle', + 'path' => __DIR__, + ], + ] + ); + } + + public function testDependsOnTwigBundle() : void + { + $containerBuilder = new ContainerBuilder(); + $extension = new TracyBlueScreenExtension(); + + $this->expectException(TwigBundleRequired::class); + $extension->prepend($containerBuilder); + } + + public function testOnlyAddCollapsePaths() : void + { + $this->loadExtensions(); + + $this->assertContainerBuilderHasService( + 'cdn77.tracy_blue_screen.tracy.blue_screen.default', + BlueScreen::class + ); + + $blueScreen = $this->container->get('cdn77.tracy_blue_screen.tracy.blue_screen.default'); + assert($blueScreen instanceof BlueScreen); + + $collapsePaths = $blueScreen->collapsePaths; + + $this->assertArrayContainsStringPart('/bootstrap.php.cache', $collapsePaths); + $this->assertArrayContainsStringPart('/tests-cache-dir', $collapsePaths); + $this->assertArrayContainsStringPart('/vendor', $collapsePaths); + } + + public function testCollapseCacheDirsByDefault() : void + { + $this->loadExtensions(); + + $this->assertContainerBuilderHasParameter('cdn77.tracy_blue_screen.blue_screen.collapse_paths'); + $collapsePaths = $this->container->getParameter('cdn77.tracy_blue_screen.blue_screen.collapse_paths'); + + $this->assertArrayContainsStringPart('/bootstrap.php.cache', $collapsePaths); + $this->assertArrayContainsStringPart('/tests-cache-dir', $collapsePaths); + } + + public function testSetCollapseDirs() : void + { + $paths = [ + __DIR__ . '/foobar', + ]; + + $this->loadExtensions( + [ + 'tracy_blue_screen' => [ + 'blue_screen' => ['collapse_paths' => $paths], + ], + ] + ); + + $this->assertContainerBuilderHasParameter('cdn77.tracy_blue_screen.blue_screen.collapse_paths'); + $collapsePaths = $this->container->getParameter('cdn77.tracy_blue_screen.blue_screen.collapse_paths'); + + self::assertEquals($paths, $collapsePaths); + } + + public function testEmptyCollapseDirs() : void + { + $this->loadExtensions( + [ + 'tracy_blue_screen' => [ + 'blue_screen' => [ + 'collapse_paths' => [], + ], + ], + ] + ); + + $this->assertContainerBuilderHasParameter('cdn77.tracy_blue_screen.blue_screen.collapse_paths'); + $collapsePaths = $this->container->getParameter('cdn77.tracy_blue_screen.blue_screen.collapse_paths'); + + self::assertEmpty($collapsePaths); + } + + /** @return ExtensionInterface[] */ + protected function getContainerExtensions() : array + { + return [ + new TracyBlueScreenExtension(), + new TwigExtension(), + ]; + } + + /** @param mixed[] $configuration format: extensionAlias(string) => configuration(mixed[]) */ + private function loadExtensions(array $configuration = []) : void + { + self::loadExtensionsToContainer($this->container, $configuration, $this->getMinimalConfiguration()); + } + + /** @param string[] $array */ + private function assertArrayContainsStringPart(string $string, array $array) : void + { + $found = false; + foreach ($array as $item) { + if (strpos($item, $string) !== false) { + $found = true; + + break; + } + } + + self::assertTrue( + $found, + sprintf('%s not found in any elements of the given %s', $string, var_export($array, true)) + ); + } } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 9b200f7..b0ccdc7 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,6 +1,6 @@ - - - - - - - - ../src - - -