├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── .gitignore ├── .php-cs-fixer.dist.php ├── LICENSE ├── Makefile ├── README.md ├── composer.json ├── composer.lock ├── docker-compose.yml ├── docker └── php-cli │ └── Dockerfile ├── phpstan.neon ├── phpunit.xml.dist ├── readme ├── example-compact.png ├── example-default.png ├── example-profiling.png ├── example-quote.png └── logo.png ├── src ├── Configuration.php ├── PhpUnitExtension.php ├── Quotes.php └── Subscriber │ └── Application │ ├── ApplicationFinishedSubscriber.php │ └── ApplicationStartedSubscriber.php └── tests ├── ExampleTests ├── TestThatHasAllStatusesTest.php └── TestThatPassesTest.php ├── OutputTest.php ├── SpyOutput.php ├── Unit ├── ConfigurationTest.php ├── PhpUnitExtensionTest.php └── Subscriber │ └── Application │ ├── ApplicationFinishedSubscriberTest.php │ └── ApplicationStartedSubscriberTest.php ├── phpunit.test-compact-mode.xml ├── phpunit.test-disable-by-default.xml ├── phpunit.test-profiling.xml ├── phpunit.test-with-quotes.xml └── phpunit.test.xml /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: robiningelbrecht 4 | custom: ['https://www.buymeacoffee.com/ingelbrecht'] 5 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | pull_request: 4 | workflow_dispatch: 5 | jobs: 6 | test-suite: 7 | name: PHPStan, PHPcs & Testsuite 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | php-versions: [ '8.1', '8.2' ] 12 | phpunit-versions: [ '10.3', '11.0' ] 13 | exclude: 14 | - php-versions: 8.1 15 | phpunit-versions: 11.0 16 | 17 | steps: 18 | # https://github.com/marketplace/actions/setup-php-action 19 | - name: Setup PHP ${{ matrix.php-versions }} with Xdebug 3.x 20 | uses: shivammathur/setup-php@v2 21 | with: 22 | php-version: ${{ matrix.php-versions }} 23 | coverage: xdebug 24 | 25 | # https://github.com/marketplace/actions/checkout 26 | - name: Checkout code 27 | uses: actions/checkout@v3 28 | 29 | - name: Install PHPUnit ${{ matrix.phpunit-versions }} 30 | run: | 31 | rm composer.lock 32 | composer require phpunit/phpunit ^${{ matrix.phpunit-versions }} 33 | 34 | - name: Install dependencies 35 | run: composer install --prefer-dist 36 | 37 | - name: Run PHPStan 38 | run: vendor/bin/phpstan analyse 39 | 40 | #- name: Run PHPcs fixer dry-run 41 | # run: vendor/bin/php-cs-fixer fix --dry-run --stop-on-violation --config=.php-cs-fixer.dist.php 42 | 43 | - name: Run test suite 44 | run: vendor/bin/phpunit --fail-on-incomplete --log-junit junit.xml --coverage-clover clover.xml 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | .phpunit.cache 3 | .phpunit.result.cache 4 | .php-cs-fixer.cache -------------------------------------------------------------------------------- /.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | in(__DIR__) 5 | ->exclude('var') 6 | ->exclude('vendor') 7 | ->exclude('docker') 8 | ->exclude('bin'); 9 | 10 | return (new PhpCsFixer\Config) 11 | ->setRules([ 12 | '@Symfony' => true 13 | ]) 14 | ->setFinder($finder); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Robin Ingelbrecht 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | compose=docker compose 2 | 3 | dc: 4 | @${compose} -f docker-compose.yml $(cmd) 5 | 6 | dcr: 7 | @make dc cmd="run --rm php-cli $(cmd)" 8 | 9 | stop: 10 | @make dc cmd="stop" 11 | 12 | up: 13 | @make dc cmd="up -d" 14 | 15 | build-containers: 16 | @make dc cmd="up -d --build" 17 | 18 | down: 19 | @make dc cmd="down" 20 | 21 | composer: 22 | @make dcr cmd="composer $(arg)" 23 | 24 | # Code quality tools. 25 | phpunit: 26 | @make dcr cmd="vendor/bin/phpunit -d --enable-pretty-print -d --compact $(arg)" 27 | 28 | phpunit-with-coverage-report: 29 | @make phpunit arg="--coverage-clover=clover.xml -d --min-coverage=min-coverage-rules.php" 30 | 31 | phpstan: 32 | @make dcr cmd="vendor/bin/phpstan --memory-limit=1G $(arg)" 33 | 34 | csfix: 35 | @make dcr cmd="vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php" 36 | 37 | delete-snapshots: 38 | find . -name __snapshots__ -type d -prune -exec rm -rf {} \; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Better PHPUnit CLI output

2 | 3 |

4 | PHPUnit 5 |

6 | 7 |

8 | CI 9 | License 10 | 11 | PHPStan Enabled 12 | PHP 13 | PHPUnit 14 | PHPUnit 15 |

16 | 17 | --- 18 | 19 | I really like how [Pest PHP](https://pestphp.com/) formats and outputs test results, 20 | but I still prefer to use [PHPUnit](https://phpunit.de/). Luckily there's [Collision](https://github.com/nunomaduro/collision). 21 | This package is designed to give you beautiful error reporting when interacting with your app through the command line. 22 | 23 | ## Installation 24 | 25 | ```bash 26 | composer require robiningelbrecht/phpunit-pretty-print --dev 27 | ``` 28 | 29 | ## Configuration 30 | 31 | Navigate to your `phpunit.xml.dist` file and add following config to set default options 32 | (you can also set these options at run time): 33 | 34 | ```xml 35 | 36 | 37 | 38 | 39 | ``` 40 | 41 | Also make sure the `color` attribute is set to `true`: 42 | 43 | ```xml 44 | 46 | 47 | ``` 48 | 49 | ## Options 50 | 51 | All these options can be set at runtime as well, see usage. 52 | 53 | ### Output profiling report 54 | 55 | ```xml 56 | 57 | 58 | 59 | 60 | 61 | ``` 62 | 63 | ### Enable compact mode 64 | 65 | ```xml 66 | 67 | 68 | 69 | 70 | 71 | ``` 72 | 73 | ### Feel good about yourself after running your testsuite by displaying a Chuck Noris quote 74 | 75 | ```xml 76 | 77 | 78 | 79 | 80 | 81 | ``` 82 | 83 | ### Disable pretty print. 84 | 85 | This can be useful when you only want to prettify the output when forced via CLI (see usage). 86 | 87 | ```xml 88 | 89 | 90 | 91 | 92 | 93 | ``` 94 | 95 | ## Usage 96 | 97 | ```bash 98 | > vendor/bin/phpunit 99 | ``` 100 | 101 |

102 | Example default 103 |

104 | 105 | ### Output profiling report 106 | 107 | ```bash 108 | > vendor/bin/phpunit -d --profiling 109 | ``` 110 | 111 |

112 | Example profiling 113 |

114 | 115 | ### Enable compact mode 116 | 117 | ```bash 118 | > vendor/bin/phpunit -d --compact 119 | ``` 120 | 121 |

122 | Example compact 123 |

124 | 125 | ### Display Chuck Norris quote 126 | 127 | ```bash 128 | > vendor/bin/phpunit -d --display-quote 129 | ``` 130 | 131 |

132 | Example quote 133 |

134 | 135 | ### Enable/disable pretty print 136 | 137 | ```bash 138 | > vendor/bin/phpunit -d --enable-pretty-print 139 | > vendor/bin/phpunit -d --disable-pretty-print 140 | ``` 141 | 142 | ### Combine multiple options 143 | 144 | ```bash 145 | > vendor/bin/phpunit --configuration=tests/phpunit.test.xml -d --compact -d --display-quote 146 | ``` 147 | 148 | ## PHPUnit 9.x 149 | 150 | This package does not support PHPUnit 9.x but Collision does out of the box. Run 151 | 152 | ```bash 153 | composer require nunomaduro/collision:^6.0 154 | ``` 155 | 156 | Then add the Collision `printerClass` to your `phpunit.xml` in the `phpunit` section: 157 | 158 | ```xml 159 | 161 | ``` 162 | 163 | ## Acknowledgements 164 | 165 | * API used for Chuck Noris quotes: https://api.chucknorris.io/ 166 | * CLI formatting: https://github.com/nunomaduro/collision 167 | 168 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "robiningelbrecht/phpunit-pretty-print", 3 | "description": "Prettify PHPUnit output", 4 | "keywords": [ 5 | "Testing", 6 | "PHP", 7 | "phpunit" 8 | ], 9 | "type": "library", 10 | "license": "MIT", 11 | "authors": [ 12 | { 13 | "name": "Robin Ingelbrecht", 14 | "email": "ingelbrecht_robin@hotmail.com" 15 | } 16 | ], 17 | "require": { 18 | "php": "^8.1", 19 | "nunomaduro/collision": "^7.8|^8.0", 20 | "phpunit/phpunit": "^10.3|^11.0|^12.0" 21 | }, 22 | "autoload": { 23 | "psr-4": { 24 | "RobinIngelbrecht\\PHPUnitPrettyPrint\\": "src/" 25 | } 26 | }, 27 | "autoload-dev": { 28 | "psr-4": { 29 | "Tests\\": "tests/" 30 | } 31 | }, 32 | "require-dev": { 33 | "friendsofphp/php-cs-fixer": "^3.9", 34 | "phpstan/phpstan": "^1.10", 35 | "spatie/phpunit-snapshot-assertions": "^5.0" 36 | }, 37 | "config": { 38 | "sort-packages": true 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | php-cli: 3 | build: ./docker/php-cli 4 | container_name: phpunit-pretty-print-php-cli 5 | environment: 6 | - PHP_CS_FIXER_IGNORE_ENV=true 7 | volumes: 8 | - ./:/var/www/ 9 | working_dir: /var/www 10 | 11 | -------------------------------------------------------------------------------- /docker/php-cli/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.4-cli 2 | 3 | RUN apt-get update && apt-get install -y zip unzip git curl 4 | RUN docker-php-ext-install mysqli pdo pdo_mysql 5 | 6 | COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: 9 3 | paths: 4 | - src 5 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | tests/OutputTest.php 13 | tests/Unit 14 | 15 | 16 | 17 | 18 | src 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /readme/example-compact.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robiningelbrecht/phpunit-pretty-print/1faaf1b078674ef448e6ab24721db539d3cfca72/readme/example-compact.png -------------------------------------------------------------------------------- /readme/example-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robiningelbrecht/phpunit-pretty-print/1faaf1b078674ef448e6ab24721db539d3cfca72/readme/example-default.png -------------------------------------------------------------------------------- /readme/example-profiling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robiningelbrecht/phpunit-pretty-print/1faaf1b078674ef448e6ab24721db539d3cfca72/readme/example-profiling.png -------------------------------------------------------------------------------- /readme/example-quote.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robiningelbrecht/phpunit-pretty-print/1faaf1b078674ef448e6ab24721db539d3cfca72/readme/example-quote.png -------------------------------------------------------------------------------- /readme/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robiningelbrecht/phpunit-pretty-print/1faaf1b078674ef448e6ab24721db539d3cfca72/readme/logo.png -------------------------------------------------------------------------------- /src/Configuration.php: -------------------------------------------------------------------------------- 1 | displayProfiling; 19 | } 20 | 21 | public function displayQuote(): bool 22 | { 23 | return $this->displayQuote; 24 | } 25 | 26 | public function useCompactMode(): bool 27 | { 28 | return $this->useCompactMode; 29 | } 30 | 31 | public static function fromParameterCollection(ParameterCollection $parameters): self 32 | { 33 | if (!$useProfiling = in_array('--profiling', $_SERVER['argv'], true)) { 34 | $useProfiling = $parameters->has('displayProfiling') && !self::isFalsy($parameters->get('displayProfiling')); 35 | } 36 | if (!$useCompactMode = in_array('--compact', $_SERVER['argv'], true)) { 37 | $useCompactMode = $parameters->has('useCompactMode') && !self::isFalsy($parameters->get('useCompactMode')); 38 | } 39 | if (!$displayQuote = in_array('--display-quote', $_SERVER['argv'], true)) { 40 | $displayQuote = $parameters->has('displayQuote') && !self::isFalsy($parameters->get('displayQuote')); 41 | } 42 | 43 | return new self( 44 | $useProfiling, 45 | $displayQuote, 46 | $useCompactMode, 47 | ); 48 | } 49 | 50 | public static function isFalsy(mixed $value): bool 51 | { 52 | if (is_bool($value)) { 53 | return !$value; 54 | } 55 | if ('true' === $value) { 56 | return false; 57 | } 58 | if ('false' === $value) { 59 | return true; 60 | } 61 | if (is_int($value)) { 62 | return !$value; 63 | } 64 | 65 | return true; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/PhpUnitExtension.php: -------------------------------------------------------------------------------- 1 | isEnabled($parameters)) { 19 | return; 20 | } 21 | 22 | $_SERVER['PHPUNIT_RANDOM_ORDER_SEED'] = TestSuiteSorter::ORDER_RANDOMIZED === $configuration->executionOrder() ? $configuration->randomOrderSeed() : null; 23 | $configuration = Configuration::fromParameterCollection($parameters); 24 | 25 | $facade->replaceOutput(); 26 | $facade->replaceProgressOutput(); 27 | $facade->replaceResultOutput(); 28 | 29 | $_SERVER['COLLISION_PRINTER'] = true; 30 | if ($configuration->useCompactMode()) { 31 | $_SERVER['COLLISION_PRINTER_COMPACT'] = true; 32 | } 33 | if ($configuration->displayProfiling()) { 34 | $_SERVER['COLLISION_PRINTER_PROFILE'] = true; 35 | } 36 | 37 | EnsurePrinterIsRegisteredSubscriber::register(); 38 | 39 | $facade->registerSubscriber(new ApplicationStartedSubscriber()); 40 | if (!$configuration->displayQuote()) { 41 | return; 42 | } 43 | $facade->registerSubscriber(new ApplicationFinishedSubscriber()); 44 | } 45 | 46 | private function isEnabled(ParameterCollection $parameters): bool 47 | { 48 | if (!$parameters->has('enableByDefault') 49 | && !in_array('--enable-pretty-print', $_SERVER['argv'], true) 50 | && !in_array('--disable-pretty-print', $_SERVER['argv'], true)) { 51 | // Nothing has been set, assume the extension is enabled for backwards compatible reasons. 52 | return true; 53 | } 54 | 55 | if (in_array('--enable-pretty-print', $_SERVER['argv'], true)) { 56 | return true; 57 | } 58 | if (in_array('--disable-pretty-print', $_SERVER['argv'], true)) { 59 | return false; 60 | } 61 | 62 | if ($parameters->has('enableByDefault') && !Configuration::isFalsy($parameters->get('enableByDefault'))) { 63 | return true; 64 | } 65 | 66 | return false; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Quotes.php: -------------------------------------------------------------------------------- 1 | %s', Quotes::getRandom())); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Subscriber/Application/ApplicationStartedSubscriber.php: -------------------------------------------------------------------------------- 1 | '); 15 | render(sprintf( 16 | '
  Runtime: %s
', 17 | $event->runtime()->asString() 18 | )); 19 | if (!empty($_SERVER['PHPUNIT_RANDOM_ORDER_SEED'])) { 20 | render(sprintf( 21 | '
  Random Seed: %s
', 22 | $_SERVER['PHPUNIT_RANDOM_ORDER_SEED'] 23 | )); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/ExampleTests/TestThatHasAllStatusesTest.php: -------------------------------------------------------------------------------- 1 | assertTrue(true); 24 | } 25 | 26 | public function testFail(): void 27 | { 28 | $this->assertTrue(false); 29 | } 30 | 31 | public function testFailWithDiff(): void 32 | { 33 | $this->assertEquals( 34 | ['one', 'two'], 35 | ['two', 'one'] 36 | ); 37 | } 38 | 39 | public function testError(): void 40 | { 41 | throw new \Exception('error'); 42 | } 43 | 44 | public function testRisky(): void 45 | { 46 | } 47 | 48 | public function testSkip(): void 49 | { 50 | $this->markTestSkipped('skipped'); 51 | } 52 | 53 | public function testIncomplete(): void 54 | { 55 | $this->markTestIncomplete('incomplete'); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tests/ExampleTests/TestThatPassesTest.php: -------------------------------------------------------------------------------- 1 | assertFalse(false); 15 | $this->assertEquals(1, 1); 16 | } 17 | 18 | public function testDoSomeMoreAssertions(): void 19 | { 20 | usleep(self::SLEEP_IN_MICRO_SECONDS); 21 | $this->assertFalse(false); 22 | $this->assertEquals(1, 1); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/OutputTest.php: -------------------------------------------------------------------------------- 1 | assertStringContainsString('Tests: 3 failed, 1 risky, 1 incomplete, 1 skipped, 3 passed (7 assertions)', $output); 23 | $this->assertStringNotContainsString('Random Seed:', $output); 24 | } 25 | 26 | public function testWithProfiling(): void 27 | { 28 | $command = [ 29 | 'vendor/bin/phpunit', 30 | '--configuration=tests/phpunit.test-profiling.xml', 31 | ]; 32 | 33 | exec(implode(' ', $command), $out); 34 | $output = implode(PHP_EOL, $out); 35 | 36 | $this->assertMatchesRegularExpression('/Runtime: PHPUnit [\s\S]+ using PHP [\s\S]+ on [\s\S]+/', $output); 37 | $this->assertStringContainsString('Tests: 2 passed (4 assertions)', $output); 38 | $this->assertStringContainsString('Top 10 slowest tests', $output); 39 | } 40 | 41 | public function testWithProfilingAtRunTime(): void 42 | { 43 | $command = [ 44 | 'vendor/bin/phpunit', 45 | 'tests/ExampleTests/TestThatPassesTest.php', 46 | '--configuration=tests/phpunit.test.xml', 47 | '-d --profiling', 48 | ]; 49 | exec(implode(' ', $command), $out); 50 | $output = implode(PHP_EOL, $out); 51 | 52 | $this->assertMatchesRegularExpression('/Runtime: PHPUnit [\s\S]+ using PHP [\s\S]+ on [\s\S]+/', $output); 53 | $this->assertStringContainsString('Tests: 2 passed (4 assertions)', $output); 54 | $this->assertStringContainsString('Top 10 slowest tests', $output); 55 | } 56 | 57 | public function testPrintCompactMode(): void 58 | { 59 | $command = [ 60 | 'vendor/bin/phpunit', 61 | '--configuration=tests/phpunit.test-compact-mode.xml', 62 | ]; 63 | 64 | exec(implode(' ', $command), $out); 65 | $output = implode(PHP_EOL, $out); 66 | 67 | $this->assertMatchesRegularExpression('/Runtime: PHPUnit [\s\S]+ using PHP [\s\S]+ on [\s\S]+/', $output); 68 | $this->assertStringContainsString('.⨯⨯⨯!si..', $output); 69 | $this->assertStringContainsString('Tests: 3 failed, 1 risky, 1 incomplete, 1 skipped, 3 passed (7 assertions)', $output); 70 | } 71 | 72 | public function testPrintCompactModeAtRunTime(): void 73 | { 74 | $command = [ 75 | 'vendor/bin/phpunit', 76 | '--configuration=tests/phpunit.test.xml', 77 | '-d --compact', 78 | ]; 79 | exec(implode(' ', $command), $out); 80 | $output = implode(PHP_EOL, $out); 81 | 82 | $this->assertMatchesRegularExpression('/Runtime: PHPUnit [\s\S]+ using PHP [\s\S]+ on [\s\S]+/', $output); 83 | $this->assertStringContainsString('.⨯⨯⨯!si..', $output); 84 | $this->assertStringContainsString('Tests: 3 failed, 1 risky, 1 incomplete, 1 skipped, 3 passed (7 assertions)', $output); 85 | } 86 | 87 | public function testPrintWithQuote(): void 88 | { 89 | $command = [ 90 | 'vendor/bin/phpunit', 91 | '--configuration=tests/phpunit.test-with-quotes.xml', 92 | ]; 93 | 94 | exec(implode(' ', $command), $out); 95 | 96 | $print = implode(PHP_EOL, $out); 97 | 98 | $printContainsQuote = false; 99 | foreach (Quotes::getAll() as $quote) { 100 | if (!str_contains($print, $quote)) { 101 | continue; 102 | } 103 | 104 | $printContainsQuote = true; 105 | $this->addToAssertionCount(1); 106 | } 107 | 108 | if (!$printContainsQuote) { 109 | $this->fail('Quote not found'); 110 | } 111 | } 112 | 113 | public function testPrintWithQuoteAtRuntime(): void 114 | { 115 | $command = [ 116 | 'vendor/bin/phpunit', 117 | '--configuration=tests/phpunit.test.xml', 118 | '-d --display-quote', 119 | ]; 120 | 121 | exec(implode(' ', $command), $out); 122 | 123 | $print = implode(PHP_EOL, $out); 124 | 125 | $printContainsQuote = false; 126 | foreach (Quotes::getAll() as $quote) { 127 | if (!str_contains($print, $quote)) { 128 | continue; 129 | } 130 | 131 | $printContainsQuote = true; 132 | $this->addToAssertionCount(1); 133 | } 134 | 135 | if (!$printContainsQuote) { 136 | $this->fail('Quote not found'); 137 | } 138 | } 139 | 140 | public function testPrintWithRandomSeed(): void 141 | { 142 | $command = [ 143 | 'vendor/bin/phpunit', 144 | '--configuration=tests/phpunit.test.xml', 145 | '--order-by=random', 146 | ]; 147 | 148 | exec(implode(' ', $command), $out); 149 | $output = implode(PHP_EOL, $out); 150 | $this->assertStringContainsString('Tests: 3 failed, 1 risky, 1 incomplete, 1 skipped, 3 passed (7 assertions)', $output); 151 | $this->assertStringContainsString('Random Seed:', $output); 152 | } 153 | 154 | public function testItShouldBeEnabled(): void 155 | { 156 | $command = [ 157 | 'vendor/bin/phpunit', 158 | 'tests/ExampleTests/TestThatPassesTest.php', 159 | '--configuration=tests/phpunit.test-disable-by-default.xml', 160 | '-d --enable-pretty-print', 161 | ]; 162 | exec(implode(' ', $command), $out); 163 | $output = implode(PHP_EOL, $out); 164 | 165 | $this->assertMatchesRegularExpression('/Runtime: PHPUnit [\s\S]+ using PHP [\s\S]+ on [\s\S]+/', $output); 166 | $this->assertStringContainsString('Tests: 2 passed (4 assertions)', $output); 167 | } 168 | 169 | public function testItShouldBeDisabled(): void 170 | { 171 | $command = [ 172 | 'vendor/bin/phpunit', 173 | 'tests/ExampleTests/TestThatPassesTest.php', 174 | '--configuration=tests/phpunit.test-disable-by-default.xml', 175 | ]; 176 | exec(implode(' ', $command), $out); 177 | $output = implode(PHP_EOL, $out); 178 | 179 | $this->assertDoesNotMatchRegularExpression('/Runtime: PHPUnit [\s\S]+ using PHP [\s\S]+ on [\s\S]+/', $output); 180 | $this->assertStringContainsString('OK (2 tests, 4 assertions)', $output); 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /tests/SpyOutput.php: -------------------------------------------------------------------------------- 1 | messages = [...$this->messages, ...$messages]; 17 | } 18 | 19 | public function write(string|iterable $messages, bool $newline = false, int $options = self::OUTPUT_NORMAL): void 20 | { 21 | if (!is_iterable($messages)) { 22 | $messages = [$messages]; 23 | } 24 | $this->messages = [...$this->messages, ...$messages]; 25 | } 26 | 27 | public function __toString(): string 28 | { 29 | return implode(PHP_EOL, $this->messages); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Unit/ConfigurationTest.php: -------------------------------------------------------------------------------- 1 | originalServer = $_SERVER; 18 | } 19 | 20 | protected function tearDown(): void 21 | { 22 | parent::tearDown(); 23 | 24 | $_SERVER = $this->originalServer; 25 | } 26 | 27 | public function testFromParameterCollection(): void 28 | { 29 | $configuration = Configuration::fromParameterCollection(ParameterCollection::fromArray([ 30 | 'displayProfiling' => 'true', 31 | 'useCompactMode' => 'true', 32 | 'displayQuote' => 'true', 33 | ])); 34 | 35 | $this->assertTrue($configuration->displayProfiling()); 36 | $this->assertTrue($configuration->useCompactMode()); 37 | $this->assertTrue($configuration->displayQuote()); 38 | 39 | $configuration = Configuration::fromParameterCollection(ParameterCollection::fromArray([])); 40 | 41 | $this->assertFalse($configuration->displayProfiling()); 42 | $this->assertFalse($configuration->useCompactMode()); 43 | $this->assertFalse($configuration->displayQuote()); 44 | } 45 | 46 | public function testFromParameterCollectionWithServerArguments(): void 47 | { 48 | $_SERVER['argv'][] = '--profiling'; 49 | $_SERVER['argv'][] = '--compact'; 50 | $_SERVER['argv'][] = '--display-quote'; 51 | $configuration = Configuration::fromParameterCollection(ParameterCollection::fromArray([])); 52 | 53 | $this->assertTrue($configuration->displayProfiling()); 54 | $this->assertTrue($configuration->useCompactMode()); 55 | $this->assertTrue($configuration->displayQuote()); 56 | } 57 | 58 | public function testIsFalsy(): void 59 | { 60 | $this->assertTrue(Configuration::isFalsy(0)); 61 | $this->assertTrue(Configuration::isFalsy('false')); 62 | $this->assertTrue(Configuration::isFalsy(false)); 63 | $this->assertTrue(Configuration::isFalsy('test')); 64 | 65 | $this->assertFalse(Configuration::isFalsy(1)); 66 | $this->assertFalse(Configuration::isFalsy('true')); 67 | $this->assertFalse(Configuration::isFalsy(true)); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /tests/Unit/PhpUnitExtensionTest.php: -------------------------------------------------------------------------------- 1 | originalServer = $_SERVER; 21 | 22 | // We need to do some reflection magic here because of pour decisions in PHPUnit 23 | $eventFacade = EventFacade::instance(); 24 | $reflection = new \ReflectionClass($eventFacade); 25 | $sealed = $reflection->getProperty('sealed'); 26 | $sealed->setAccessible(true); 27 | $sealed->setValue($eventFacade, false); 28 | } 29 | 30 | protected function tearDown(): void 31 | { 32 | parent::tearDown(); 33 | 34 | $_SERVER = $this->originalServer; 35 | } 36 | 37 | public function testBootstrapWithAllOptions(): void 38 | { 39 | $facade = new Facade(); 40 | 41 | $configuration = (new Builder())->build([]); 42 | $parameters = ParameterCollection::fromArray([ 43 | 'displayProfiling' => 'true', 44 | 'useCompactMode' => 'true', 45 | 'displayQuote' => 'true', 46 | ]); 47 | 48 | $extension = new PhpUnitExtension(); 49 | 50 | $extension->bootstrap( 51 | $configuration, 52 | $facade, 53 | $parameters 54 | ); 55 | 56 | $this->assertTrue($facade->replacesOutput()); 57 | $this->assertTrue($facade->replacesProgressOutput()); 58 | $this->assertTrue($facade->replacesResultOutput()); 59 | 60 | $this->assertContains('COLLISION_PRINTER_COMPACT', array_keys($_SERVER)); 61 | $this->assertContains('COLLISION_PRINTER_PROFILE', array_keys($_SERVER)); 62 | } 63 | 64 | public function testBootstrapInCompactMode(): void 65 | { 66 | $facade = new Facade(); 67 | $configuration = (new Builder())->build([]); 68 | $parameters = ParameterCollection::fromArray([ 69 | 'useCompactMode' => 'true', 70 | ]); 71 | 72 | $extension = new PhpUnitExtension(); 73 | 74 | $extension->bootstrap( 75 | $configuration, 76 | $facade, 77 | $parameters 78 | ); 79 | 80 | $this->assertTrue($facade->replacesOutput()); 81 | $this->assertTrue($facade->replacesProgressOutput()); 82 | $this->assertTrue($facade->replacesResultOutput()); 83 | 84 | $this->assertContains('COLLISION_PRINTER_COMPACT', array_keys($_SERVER)); 85 | $this->assertNotContains('COLLISION_PRINTER_PROFILE', array_keys($_SERVER)); 86 | } 87 | 88 | public function testBootstrapWithDisplayProfiling(): void 89 | { 90 | $facade = new Facade(); 91 | $configuration = (new Builder())->build([]); 92 | $parameters = ParameterCollection::fromArray([ 93 | 'displayProfiling' => 'true', 94 | ]); 95 | 96 | $extension = new PhpUnitExtension(); 97 | 98 | $extension->bootstrap( 99 | $configuration, 100 | $facade, 101 | $parameters 102 | ); 103 | 104 | $this->assertTrue($facade->replacesOutput()); 105 | $this->assertTrue($facade->replacesProgressOutput()); 106 | $this->assertTrue($facade->replacesResultOutput()); 107 | 108 | $this->assertNotContains('COLLISION_PRINTER_COMPACT', array_keys($_SERVER)); 109 | $this->assertContains('COLLISION_PRINTER_PROFILE', array_keys($_SERVER)); 110 | } 111 | 112 | public function testBootstrapWithDisplayQuote(): void 113 | { 114 | $facade = new Facade(); 115 | $configuration = (new Builder())->build([]); 116 | $parameters = ParameterCollection::fromArray([ 117 | 'displayQuote' => 'true', 118 | ]); 119 | 120 | $extension = new PhpUnitExtension(); 121 | 122 | $extension->bootstrap( 123 | $configuration, 124 | $facade, 125 | $parameters 126 | ); 127 | 128 | $this->assertTrue($facade->replacesOutput()); 129 | $this->assertTrue($facade->replacesProgressOutput()); 130 | $this->assertTrue($facade->replacesResultOutput()); 131 | 132 | $this->assertNotContains('COLLISION_PRINTER_COMPACT', array_keys($_SERVER)); 133 | $this->assertNotContains('COLLISION_PRINTER_PROFILE', array_keys($_SERVER)); 134 | } 135 | 136 | public function testItShouldBeEnabledThroughCli(): void 137 | { 138 | $facade = new Facade(); 139 | $configuration = (new Builder())->build([]); 140 | $parameters = ParameterCollection::fromArray([ 141 | 'displayProfiling' => 'true', 142 | 'useCompactMode' => 'true', 143 | 'displayQuote' => 'true', 144 | 'enableByDefault' => 'false', 145 | ]); 146 | 147 | $extension = new PhpUnitExtension(); 148 | 149 | $_SERVER['argv'][] = '--enable-pretty-print'; 150 | 151 | $extension->bootstrap( 152 | $configuration, 153 | $facade, 154 | $parameters 155 | ); 156 | 157 | $this->assertTrue($facade->replacesOutput()); 158 | $this->assertTrue($facade->replacesProgressOutput()); 159 | $this->assertTrue($facade->replacesResultOutput()); 160 | 161 | $this->assertContains('COLLISION_PRINTER_COMPACT', array_keys($_SERVER)); 162 | $this->assertContains('COLLISION_PRINTER_PROFILE', array_keys($_SERVER)); 163 | } 164 | 165 | public function testItShouldBeDisabledThroughCli(): void 166 | { 167 | $facade = new Facade(); 168 | $configuration = (new Builder())->build([]); 169 | $parameters = ParameterCollection::fromArray([ 170 | 'displayProfiling' => 'true', 171 | 'useCompactMode' => 'true', 172 | 'displayQuote' => 'true', 173 | ]); 174 | 175 | $extension = new PhpUnitExtension(); 176 | 177 | $_SERVER['argv'][] = '--disable-pretty-print'; 178 | 179 | $extension->bootstrap( 180 | $configuration, 181 | $facade, 182 | $parameters 183 | ); 184 | 185 | $this->assertFalse($facade->replacesOutput()); 186 | $this->assertFalse($facade->replacesProgressOutput()); 187 | $this->assertFalse($facade->replacesResultOutput()); 188 | 189 | $this->assertNotContains('COLLISION_PRINTER_COMPACT', array_keys($_SERVER)); 190 | $this->assertNotContains('COLLISION_PRINTER_PROFILE', array_keys($_SERVER)); 191 | } 192 | 193 | public function testItShouldBeEnabledWithParameter(): void 194 | { 195 | $facade = new Facade(); 196 | $configuration = (new Builder())->build([]); 197 | $parameters = ParameterCollection::fromArray([ 198 | 'displayProfiling' => 'true', 199 | 'useCompactMode' => 'true', 200 | 'displayQuote' => 'true', 201 | 'enableByDefault' => 'true', 202 | ]); 203 | 204 | $extension = new PhpUnitExtension(); 205 | 206 | $extension->bootstrap( 207 | $configuration, 208 | $facade, 209 | $parameters 210 | ); 211 | 212 | $this->assertTrue($facade->replacesOutput()); 213 | $this->assertTrue($facade->replacesProgressOutput()); 214 | $this->assertTrue($facade->replacesResultOutput()); 215 | 216 | $this->assertContains('COLLISION_PRINTER_COMPACT', array_keys($_SERVER)); 217 | $this->assertContains('COLLISION_PRINTER_PROFILE', array_keys($_SERVER)); 218 | } 219 | 220 | public function testItShouldBeDisabledWithParameter(): void 221 | { 222 | $facade = new Facade(); 223 | $configuration = (new Builder())->build([]); 224 | $parameters = ParameterCollection::fromArray([ 225 | 'displayProfiling' => 'true', 226 | 'useCompactMode' => 'true', 227 | 'displayQuote' => 'true', 228 | 'enableByDefault' => 'false', 229 | ]); 230 | 231 | $extension = new PhpUnitExtension(); 232 | 233 | $extension->bootstrap( 234 | $configuration, 235 | $facade, 236 | $parameters 237 | ); 238 | 239 | $this->assertFalse($facade->replacesOutput()); 240 | $this->assertFalse($facade->replacesProgressOutput()); 241 | $this->assertFalse($facade->replacesResultOutput()); 242 | 243 | $this->assertNotContains('COLLISION_PRINTER_COMPACT', array_keys($_SERVER)); 244 | $this->assertNotContains('COLLISION_PRINTER_PROFILE', array_keys($_SERVER)); 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /tests/Unit/Subscriber/Application/ApplicationFinishedSubscriberTest.php: -------------------------------------------------------------------------------- 1 | notify(new Finished( 30 | new Info( 31 | new Snapshot( 32 | HRTime::fromSecondsAndNanoseconds(1, 0), 33 | MemoryUsage::fromBytes(100), 34 | MemoryUsage::fromBytes(100), 35 | new GarbageCollectorStatus(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) 36 | ), 37 | Duration::fromSecondsAndNanoseconds(1, 0), 38 | MemoryUsage::fromBytes(100), 39 | Duration::fromSecondsAndNanoseconds(1, 0), 40 | MemoryUsage::fromBytes(100), 41 | ), 42 | 0 43 | )); 44 | 45 | $this->assertEquals(3, substr_count($spyOutput, '')); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tests/Unit/Subscriber/Application/ApplicationStartedSubscriberTest.php: -------------------------------------------------------------------------------- 1 | notify(new Started( 31 | new Info( 32 | new Snapshot( 33 | HRTime::fromSecondsAndNanoseconds(1, 0), 34 | MemoryUsage::fromBytes(100), 35 | MemoryUsage::fromBytes(100), 36 | new GarbageCollectorStatus(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) 37 | ), 38 | Duration::fromSecondsAndNanoseconds(1, 0), 39 | MemoryUsage::fromBytes(100), 40 | Duration::fromSecondsAndNanoseconds(1, 0), 41 | MemoryUsage::fromBytes(100), 42 | ), 43 | new Runtime() 44 | )); 45 | 46 | $this->assertMatchesRegularExpression('/Runtime: PHPUnit [\s\S]+ using PHP [\s\S]+ on [\s\S]+/', $spyOutput); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/phpunit.test-compact-mode.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | ExampleTests 19 | 20 | 21 | 22 | 23 | 24 | src 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /tests/phpunit.test-disable-by-default.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | ExampleTests 19 | 20 | 21 | 22 | 23 | 24 | src 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /tests/phpunit.test-profiling.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | ExampleTests/TestThatPassesTest.php 19 | 20 | 21 | 22 | 23 | 24 | src 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /tests/phpunit.test-with-quotes.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | ExampleTests 19 | 20 | 21 | 22 | 23 | 24 | src 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /tests/phpunit.test.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | ExampleTests 19 | 20 | 21 | 22 | 23 | 24 | src 25 | 26 | 27 | 28 | 29 | --------------------------------------------------------------------------------