diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b0872989e0b..7d0aaaf8b4f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: - name: Install PHP uses: shivammathur/setup-php@v2 with: - php-version: 8.1 + php-version: 8.2 extensions: none, iconv, json, phar, tokenizer coverage: none tools: none @@ -45,7 +45,7 @@ jobs: - name: Install PHP uses: shivammathur/setup-php@v2 with: - php-version: 8.1 + php-version: 8.2 extensions: none, ctype, curl, date, dom, json, libxml, mbstring, phar, simplexml, soap, tokenizer, xml, xmlwriter, zlib coverage: none tools: none @@ -190,7 +190,7 @@ jobs: - name: Install PHP with extensions uses: shivammathur/setup-php@v2 with: - php-version: 8.1 + php-version: 8.2 coverage: pcov extensions: none, curl, dom, json, libxml, mbstring, phar, soap, tokenizer, xml, xmlwriter ini-values: assert.exception=1, zend.assertions=1, error_reporting=-1, log_errors_max_len=0, display_errors=On @@ -226,7 +226,7 @@ jobs: - name: Install PHP with extensions uses: shivammathur/setup-php@v2 with: - php-version: 8.1 + php-version: 8.2 coverage: none extensions: ${{ env.PHP_EXTENSIONS }} ini-values: ${{ env.PHP_INI_VALUES }} diff --git a/.phive/phars.xml b/.phive/phars.xml index b1395e36f42..dd0e74dbbdb 100644 --- a/.phive/phars.xml +++ b/.phive/phars.xml @@ -1,7 +1,7 @@ - + diff --git a/ChangeLog-10.0.md b/ChangeLog-10.0.md index 71d197babab..ba508d8bc0f 100644 --- a/ChangeLog-10.0.md +++ b/ChangeLog-10.0.md @@ -2,7 +2,7 @@ All notable changes of the PHPUnit 10.0 release series are documented in this file using the [Keep a CHANGELOG](https://keepachangelog.com/) principles. -## [10.0.16] - 2023-MM-DD +## [10.0.16] - 2023-03-13 ### Added @@ -15,6 +15,7 @@ All notable changes of the PHPUnit 10.0 release series are documented in this fi ### Fixed * [#5278](https://github.com/sebastianbergmann/phpunit/issues/5278): Test Runner swallows output made during test +* [#5279](https://github.com/sebastianbergmann/phpunit/pull/5279): `StringContains` constraint does not correctly handle option for ignoring line endings ## [10.0.15] - 2023-03-09 @@ -260,7 +261,7 @@ All notable changes of the PHPUnit 10.0 release series are documented in this fi * PHP 7.3, PHP 7.4, and PHP 8.0 are no longer supported * `phpunit/php-code-coverage` [no longer supports PHPDBG and Xdebug 2](https://github.com/sebastianbergmann/php-code-coverage/blob/10.0.0/ChangeLog.md#1000---2023-02-03) -[10.0.16]: https://github.com/sebastianbergmann/phpunit/compare/10.0.15...10.0 +[10.0.16]: https://github.com/sebastianbergmann/phpunit/compare/10.0.15...10.0.16 [10.0.15]: https://github.com/sebastianbergmann/phpunit/compare/10.0.14...10.0.15 [10.0.14]: https://github.com/sebastianbergmann/phpunit/compare/10.0.13...10.0.14 [10.0.13]: https://github.com/sebastianbergmann/phpunit/compare/10.0.12...10.0.13 diff --git a/build/templates/binary-phar-autoload.php.in b/build/templates/binary-phar-autoload.php.in index 6de2ef6fdec..eaeff8a8996 100644 --- a/build/templates/binary-phar-autoload.php.in +++ b/build/templates/binary-phar-autoload.php.in @@ -30,22 +30,31 @@ if (version_compare('8.1.0', PHP_VERSION, '>')) { die(1); } -foreach (['dom', 'json', 'libxml', 'mbstring', 'tokenizer', 'xml', 'xmlwriter'] as $extension) { - if (extension_loaded($extension)) { - continue; +$requiredExtensions = ['dom', 'json', 'libxml', 'mbstring', 'tokenizer', 'xml', 'xmlwriter']; + +$unavailableExtensions = array_filter( + $requiredExtensions, + static function ($extension) { + return !extension_loaded($extension); } +); +if ([] !== $unavailableExtensions) { fwrite( STDERR, sprintf( - 'PHPUnit requires the "%s" extension.' . PHP_EOL, - $extension + 'PHPUnit requires the "%s" extensions, but the "%s" %s not available.' . PHP_EOL, + count($unavailableExtensions) === 1 ? 'extension is' : 'extensions are', + implode('", "', $requiredExtensions), + implode('", "', $unavailableExtensions) ) ); die(1); } +unset($requiredExtensions, $unavailableExtensions); + if (__FILE__ === realpath($_SERVER['SCRIPT_NAME'])) { $execute = true; } else { diff --git a/composer.json b/composer.json index 165c61f2e9a..4c0ca2a36ca 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,8 @@ } ], "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues" + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy" }, "prefer-stable": true, "require": { diff --git a/phpunit b/phpunit index 62ab6325ed0..7e8ce537310 100755 --- a/phpunit +++ b/phpunit @@ -38,22 +38,31 @@ if (version_compare('8.1.0', PHP_VERSION, '>')) { die(1); } -foreach (['dom', 'json', 'libxml', 'mbstring', 'tokenizer', 'xml', 'xmlwriter'] as $extension) { - if (extension_loaded($extension)) { - continue; +$requiredExtensions = ['dom', 'json', 'libxml', 'mbstring', 'tokenizer', 'xml', 'xmlwriter']; + +$unavailableExtensions = array_filter( + $requiredExtensions, + static function ($extension) { + return !extension_loaded($extension); } +); +if ([] !== $unavailableExtensions) { fwrite( STDERR, sprintf( - 'PHPUnit requires the "%s" extension.' . PHP_EOL, - $extension + 'PHPUnit requires the "%s" extensions, but the "%s" %s not available.' . PHP_EOL, + count($unavailableExtensions) === 1 ? 'extension is' : 'extensions are', + implode('", "', $requiredExtensions), + implode('", "', $unavailableExtensions) ) ); die(1); } +unset($requiredExtensions, $unavailableExtensions); + if (!ini_get('date.timezone')) { ini_set('date.timezone', 'UTC'); } diff --git a/src/Event/Value/Test/TestMethodBuilder.php b/src/Event/Value/Test/TestMethodBuilder.php index 11ecf850631..3c8cf3fdda9 100644 --- a/src/Event/Value/Test/TestMethodBuilder.php +++ b/src/Event/Value/Test/TestMethodBuilder.php @@ -42,7 +42,7 @@ public static function fromTestCase(TestCase $testCase): TestMethod $location['file'], $location['line'], TestDoxBuilder::fromTestCase($testCase), - MetadataRegistry::parser()->for($testCase::class, $methodName), + MetadataRegistry::parser()->forClassAndMethod($testCase::class, $methodName), self::dataFor($testCase), ); } diff --git a/src/Framework/Constraint/String/StringContains.php b/src/Framework/Constraint/String/StringContains.php index be00ad06b46..aa48286fa65 100644 --- a/src/Framework/Constraint/String/StringContains.php +++ b/src/Framework/Constraint/String/StringContains.php @@ -27,6 +27,10 @@ final class StringContains extends Constraint public function __construct(string $string, bool $ignoreCase = false, bool $ignoreLineEndings = false) { + if ($ignoreLineEndings) { + $string = $this->normalizeLineEndings($string); + } + $this->string = $string; $this->ignoreCase = $ignoreCase; $this->ignoreLineEndings = $ignoreLineEndings; @@ -43,10 +47,6 @@ public function toString(): string $string = mb_strtolower($this->string, 'UTF-8'); } - if ($this->ignoreLineEndings) { - $string = $this->normalizeLineEndings($string); - } - return sprintf( 'contains "%s"', $string diff --git a/src/Framework/TestSuite.php b/src/Framework/TestSuite.php index 3539eb6d74c..7bd49513fc7 100644 --- a/src/Framework/TestSuite.php +++ b/src/Framework/TestSuite.php @@ -115,7 +115,7 @@ public static function fromClassReflector(ReflectionClass $class): static return $testSuite; } - foreach ((new Reflection)->publicMethodsInTestClass($class) as $method) { + foreach (Reflection::publicMethodsInTestClass($class) as $method) { if ($method->getDeclaringClass()->getName() === Assert::class) { continue; } diff --git a/src/Metadata/Api/DataProvider.php b/src/Metadata/Api/DataProvider.php index 4acc56a7c76..7adfa165d49 100644 --- a/src/Metadata/Api/DataProvider.php +++ b/src/Metadata/Api/DataProvider.php @@ -273,7 +273,7 @@ private function valueObjectForTestMethodWithoutTestData(string $className, stri $className, $methodName ), - MetadataRegistry::parser()->for( + MetadataRegistry::parser()->forClassAndMethod( $className, $methodName ), diff --git a/src/Metadata/Api/HookMethods.php b/src/Metadata/Api/HookMethods.php index 19daf6460d9..26c6f5d86de 100644 --- a/src/Metadata/Api/HookMethods.php +++ b/src/Metadata/Api/HookMethods.php @@ -43,7 +43,7 @@ public function hookMethods(string $className): array self::$hookMethods[$className] = self::emptyHookMethodsArray(); - foreach ((new Reflection)->methodsInTestClass(new ReflectionClass($className)) as $method) { + foreach (Reflection::methodsInTestClass(new ReflectionClass($className)) as $method) { $methodName = $method->getName(); assert(!empty($methodName)); diff --git a/src/Metadata/Parser/CachingParser.php b/src/Metadata/Parser/CachingParser.php index 0aecab56fbf..ae20e1a4801 100644 --- a/src/Metadata/Parser/CachingParser.php +++ b/src/Metadata/Parser/CachingParser.php @@ -9,8 +9,6 @@ */ namespace PHPUnit\Metadata\Parser; -use function class_exists; -use function method_exists; use PHPUnit\Metadata\MetadataCollection; /** @@ -77,21 +75,4 @@ public function forClassAndMethod(string $className, string $methodName): Metada return $this->classAndMethodCache[$key]; } - - /** - * @psalm-param class-string $className - * @psalm-param non-empty-string $methodName - */ - public function for(string $className, string $methodName): MetadataCollection - { - if (!class_exists($className)) { - return MetadataCollection::fromArray([]); - } - - if (method_exists($className, $methodName)) { - return $this->forClassAndMethod($className, $methodName); - } - - return $this->forClass($className); - } } diff --git a/src/Metadata/Parser/Registry.php b/src/Metadata/Parser/Registry.php index f0d6f329426..2e2d401134c 100644 --- a/src/Metadata/Parser/Registry.php +++ b/src/Metadata/Parser/Registry.php @@ -17,9 +17,9 @@ */ final class Registry { - private static ?CachingParser $instance = null; + private static ?Parser $instance = null; - public static function parser(): CachingParser + public static function parser(): Parser { return self::$instance ?? self::$instance = self::build(); } @@ -28,7 +28,7 @@ private function __construct() { } - private static function build(): CachingParser + private static function build(): Parser { return new CachingParser( new ParserChain( diff --git a/src/Runner/Version.php b/src/Runner/Version.php index 77c7dd527ec..1e73e336fd0 100644 --- a/src/Runner/Version.php +++ b/src/Runner/Version.php @@ -34,7 +34,7 @@ public static function id(): string } if (self::$version === '') { - self::$version = (new VersionId('10.0.15', dirname(__DIR__, 2)))->asString(); + self::$version = (new VersionId('10.0.16', dirname(__DIR__, 2)))->asString(); } return self::$version; diff --git a/src/Util/Reflection.php b/src/Util/Reflection.php index 46aeeb933da..f1fe5825a12 100644 --- a/src/Util/Reflection.php +++ b/src/Util/Reflection.php @@ -47,23 +47,23 @@ public static function sourceLocationFor(string $className, string $methodName): /** * @psalm-return list */ - public function publicMethodsInTestClass(ReflectionClass $class): array + public static function publicMethodsInTestClass(ReflectionClass $class): array { - return $this->filterMethods($class, ReflectionMethod::IS_PUBLIC); + return self::filterMethods($class, ReflectionMethod::IS_PUBLIC); } /** * @psalm-return list */ - public function methodsInTestClass(ReflectionClass $class): array + public static function methodsInTestClass(ReflectionClass $class): array { - return $this->filterMethods($class, null); + return self::filterMethods($class, null); } /** * @psalm-return list */ - private function filterMethods(ReflectionClass $class, ?int $filter): array + private static function filterMethods(ReflectionClass $class, ?int $filter): array { $methods = []; diff --git a/tests/unit/Framework/Constraint/String/StringContainsTest.php b/tests/unit/Framework/Constraint/String/StringContainsTest.php index ec0831d7e19..ddfef153510 100644 --- a/tests/unit/Framework/Constraint/String/StringContainsTest.php +++ b/tests/unit/Framework/Constraint/String/StringContainsTest.php @@ -59,6 +59,15 @@ public static function provider(): array 'prefix SUBSTRING suffix', ], + [ + true, + '', + true, + false, + 'SUBSTRING', + 'prefix substring suffix', + ], + [ true, '', @@ -68,6 +77,24 @@ public static function provider(): array "prefix substring\r\n suffix", ], + [ + true, + '', + false, + true, + "substring\r suffix", + "prefix substring\n suffix", + ], + + [ + true, + '', + false, + true, + "substring\r\n suffix", + "prefix substring\r suffix", + ], + [ true, '', diff --git a/tests/unit/Util/ReflectionTest.php b/tests/unit/Util/ReflectionTest.php new file mode 100644 index 00000000000..4c964bc7d38 --- /dev/null +++ b/tests/unit/Util/ReflectionTest.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use function realpath; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Small; +use PHPUnit\Framework\TestCase; +use PHPUnit\TestFixture\BankAccountTest; +use ReflectionClass; + +#[CoversClass(Reflection::class)] +#[Small] +final class ReflectionTest extends TestCase +{ + public function testFindsSourceLocationForMethod(): void + { + $this->assertSame( + [ + 'file' => realpath(__DIR__ . '/../../_files/BankAccountTest.php'), + 'line' => 30, + ], + Reflection::sourceLocationFor(BankAccountTest::class, 'testBalanceIsInitiallyZero') + ); + } + + public function testReturnsUnknownSourceLocationForMethodThatDoesNotExist(): void + { + $this->assertSame( + [ + 'file' => 'unknown', + 'line' => 0, + ], + Reflection::sourceLocationFor('DoesNotExist', 'doesNotExist') + ); + } + + public function testFindsPublicMethodsInTestClass(): void + { + $methods = Reflection::publicMethodsInTestClass(new ReflectionClass(BankAccountTest::class)); + + $this->assertCount(3, $methods); + $this->assertSame('testBalanceIsInitiallyZero', $methods[0]->getName()); + $this->assertSame('testBalanceCannotBecomeNegative', $methods[1]->getName()); + $this->assertSame('testBalanceCannotBecomeNegative2', $methods[2]->getName()); + } + + public function testFindsMethodsInTestClass(): void + { + $methods = Reflection::methodsInTestClass(new ReflectionClass(BankAccountTest::class)); + + $this->assertCount(4, $methods); + $this->assertSame('setUp', $methods[0]->getName()); + $this->assertSame('testBalanceIsInitiallyZero', $methods[1]->getName()); + $this->assertSame('testBalanceCannotBecomeNegative', $methods[2]->getName()); + $this->assertSame('testBalanceCannotBecomeNegative2', $methods[3]->getName()); + } +} diff --git a/tools/php-cs-fixer b/tools/php-cs-fixer index 6e70d2d9fea..381ec510b3d 100755 Binary files a/tools/php-cs-fixer and b/tools/php-cs-fixer differ