diff --git a/ChangeLog-10.5.md b/ChangeLog-10.5.md index 249145f8b2..3c0ba10280 100644 --- a/ChangeLog-10.5.md +++ b/ChangeLog-10.5.md @@ -4,6 +4,10 @@ All notable changes of the PHPUnit 10.5 release series are documented in this fi ## [10.5.31] - 2024-MM-DD +### Fixed + +* [#5884](https://github.com/sebastianbergmann/phpunit/issues/5884): TestDox printer does not consider that issues can be suppressed by attribute, baseline, source location, or `@` operator + ### Changed * [#5931](https://github.com/sebastianbergmann/phpunit/pull/5931): `name` property on `` element in JUnit XML logfile diff --git a/src/Logging/TestDox/TestResult/TestResultCollector.php b/src/Logging/TestDox/TestResult/TestResultCollector.php index 3d64d53cc3..df1de66b48 100644 --- a/src/Logging/TestDox/TestResult/TestResultCollector.php +++ b/src/Logging/TestDox/TestResult/TestResultCollector.php @@ -41,6 +41,8 @@ use PHPUnit\Event\UnknownSubscriberTypeException; use PHPUnit\Framework\TestStatus\TestStatus; use PHPUnit\Logging\TestDox\TestResult as TestDoxTestMethod; +use PHPUnit\TextUI\Configuration\Source; +use PHPUnit\TextUI\Configuration\SourceFilter; use ReflectionMethod; /** @@ -50,6 +52,8 @@ */ final class TestResultCollector { + private readonly Source $source; + /** * @psalm-var array> */ @@ -62,8 +66,10 @@ final class TestResultCollector * @throws EventFacadeIsSealedException * @throws UnknownSubscriberTypeException */ - public function __construct(Facade $facade) + public function __construct(Facade $facade, Source $source) { + $this->source = $source; + $this->registerSubscribers($facade); } @@ -214,6 +220,22 @@ public function testTriggeredDeprecation(DeprecationTriggered $event): void return; } + if ($event->ignoredByTest()) { + return; + } + + if ($event->ignoredByBaseline()) { + return; + } + + if (!$this->source->ignoreSuppressionOfDeprecations() && $event->wasSuppressed()) { + return; + } + + if ($this->source->restrictDeprecations() && !(new SourceFilter)->includes($this->source, $event->file())) { + return; + } + $this->updateTestStatus(TestStatus::deprecation()); } @@ -223,6 +245,18 @@ public function testTriggeredNotice(NoticeTriggered $event): void return; } + if ($event->ignoredByBaseline()) { + return; + } + + if (!$this->source->ignoreSuppressionOfNotices() && $event->wasSuppressed()) { + return; + } + + if ($this->source->restrictNotices() && !(new SourceFilter)->includes($this->source, $event->file())) { + return; + } + $this->updateTestStatus(TestStatus::notice()); } @@ -232,6 +266,18 @@ public function testTriggeredWarning(WarningTriggered $event): void return; } + if ($event->ignoredByBaseline()) { + return; + } + + if (!$this->source->ignoreSuppressionOfWarnings() && $event->wasSuppressed()) { + return; + } + + if ($this->source->restrictWarnings() && !(new SourceFilter)->includes($this->source, $event->file())) { + return; + } + $this->updateTestStatus(TestStatus::warning()); } @@ -241,6 +287,22 @@ public function testTriggeredPhpDeprecation(PhpDeprecationTriggered $event): voi return; } + if ($event->ignoredByTest()) { + return; + } + + if ($event->ignoredByBaseline()) { + return; + } + + if (!$this->source->ignoreSuppressionOfPhpDeprecations() && $event->wasSuppressed()) { + return; + } + + if ($this->source->restrictDeprecations() && !(new SourceFilter)->includes($this->source, $event->file())) { + return; + } + $this->updateTestStatus(TestStatus::deprecation()); } @@ -250,6 +312,18 @@ public function testTriggeredPhpNotice(PhpNoticeTriggered $event): void return; } + if ($event->ignoredByBaseline()) { + return; + } + + if (!$this->source->ignoreSuppressionOfPhpNotices() && $event->wasSuppressed()) { + return; + } + + if ($this->source->restrictNotices() && !(new SourceFilter)->includes($this->source, $event->file())) { + return; + } + $this->updateTestStatus(TestStatus::notice()); } @@ -259,6 +333,18 @@ public function testTriggeredPhpWarning(PhpWarningTriggered $event): void return; } + if ($event->ignoredByBaseline()) { + return; + } + + if (!$this->source->ignoreSuppressionOfPhpWarnings() && $event->wasSuppressed()) { + return; + } + + if ($this->source->restrictWarnings() && !(new SourceFilter)->includes($this->source, $event->file())) { + return; + } + $this->updateTestStatus(TestStatus::warning()); } diff --git a/src/TextUI/Application.php b/src/TextUI/Application.php index cb07032cc2..bca1c00e18 100644 --- a/src/TextUI/Application.php +++ b/src/TextUI/Application.php @@ -640,7 +640,10 @@ private function testDoxResultCollector(Configuration $configuration): ?TestDoxR if ($configuration->hasLogfileTestdoxHtml() || $configuration->hasLogfileTestdoxText() || $configuration->outputIsTestDox()) { - return new TestDoxResultCollector(EventFacade::instance()); + return new TestDoxResultCollector( + EventFacade::instance(), + $configuration->source(), + ); } return null; diff --git a/tests/end-to-end/regression/5884.phpt b/tests/end-to-end/regression/5884.phpt new file mode 100644 index 0000000000..ec08d6c5dc --- /dev/null +++ b/tests/end-to-end/regression/5884.phpt @@ -0,0 +1,29 @@ +--TEST-- +https://github.com/sebastianbergmann/phpunit/issues/5884 +--FILE-- +run($_SERVER['argv']); +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: %s +Configuration: %s + +Time: %s, Memory: %s + +Foo (PHPUnit\TestFixture\Issue5884\Foo) + ✔ Pcre has utf 8 support + ✔ Stream to non writable file with p h p unit error handler + ✔ Stream to non writable file without p h p unit error handler + ✔ Stream to invalid file + +OK, but some tests were skipped! +Tests: 6, Assertions: 5, Skipped: 2. diff --git a/tests/end-to-end/regression/5884/phpunit.xml b/tests/end-to-end/regression/5884/phpunit.xml new file mode 100644 index 0000000000..517ddd75e4 --- /dev/null +++ b/tests/end-to-end/regression/5884/phpunit.xml @@ -0,0 +1,21 @@ + + + + + tests + + + diff --git a/tests/end-to-end/regression/5884/src/Foo.php b/tests/end-to-end/regression/5884/src/Foo.php new file mode 100644 index 0000000000..6f973bc4fd --- /dev/null +++ b/tests/end-to-end/regression/5884/src/Foo.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue5884; + +use function error_get_last; +use function fopen; +use function is_array; +use function preg_match; +use Exception; + +final class Foo +{ + public static function pcreHasUtf8Support() + { + // This regex deliberately has a compile error to demonstrate the issue. + return (bool) @preg_match('/^.[/u', 'a'); + } + + public static function openFile($filename): void + { + // Silenced the PHP native warning in favour of throwing an exception. + $download = @fopen($filename, 'wb'); + + if ($download === false) { + $error = error_get_last(); + + if (!is_array($error)) { + // Shouldn't be possible, but can happen in test situations. + $error = ['message' => 'Failed to open stream']; + } + + throw new Exception($error['message']); + } + } +} diff --git a/tests/end-to-end/regression/5884/tests/FooTest.php b/tests/end-to-end/regression/5884/tests/FooTest.php new file mode 100644 index 0000000000..ccd8b6b07d --- /dev/null +++ b/tests/end-to-end/regression/5884/tests/FooTest.php @@ -0,0 +1,107 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Issue5884; + +use const E_USER_DEPRECATED; +use function chmod; +use function file_get_contents; +use function file_put_contents; +use function sys_get_temp_dir; +use function tempnam; +use function trigger_error; +use function unlink; +use Exception; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; +use PHPUnit\Framework\Attributes\RequiresPhpunit; +use PHPUnit\Framework\Attributes\WithoutErrorHandler; +use PHPUnit\Framework\TestCase; + +final class FooTest extends TestCase +{ + #[RequiresPhpunit('^11.0')] + public function testExpectUserDeprecationMessageNOTIgnoringDeprecations(): void + { + $this->expectUserDeprecationMessage('foo'); + + trigger_error('foo', E_USER_DEPRECATED); + } + + #[RequiresPhpunit('^11.0')] + #[IgnoreDeprecations] + public function testExpectUserDeprecationMessageANDIgnoringDeprecations(): void + { + $this->expectUserDeprecationMessage('foo'); + + trigger_error('foo', E_USER_DEPRECATED); + } + + public function testPcreHasUtf8Support(): void + { + $this->assertIsBool(Foo::pcreHasUtf8Support()); + } + + public function testStreamToNonWritableFileWithPHPUnitErrorHandler(): void + { + // Create an unwritable file. + $filename = tempnam(sys_get_temp_dir(), 'RLT'); + + if (file_put_contents($filename, 'foo')) { + chmod($filename, 0o444); + } + + try { + Foo::openFile($filename); + } catch (Exception $e) { + // This "Failed to open stream" exception is expected. + } + + // Now verify the original file is unchanged. + $contents = file_get_contents($filename); + $this->assertSame('foo', $contents); + + chmod($filename, 0o755); + unlink($filename); + } + + #[WithoutErrorHandler] + public function testStreamToNonWritableFileWithoutPHPUnitErrorHandler(): void + { + // Create an unwritable file. + $filename = tempnam(sys_get_temp_dir(), 'RLT'); + + if (file_put_contents($filename, 'foo')) { + chmod($filename, 0o444); + } + + try { + Foo::openFile($filename); + } catch (Exception $e) { + // This "Failed to open stream" exception is expected. + } + + // Now verify the original file is unchanged. + $contents = file_get_contents($filename); + $this->assertSame('foo', $contents); + + chmod($filename, 0o755); + unlink($filename); + } + + public function testStreamToInvalidFile(): void + { + $filename = tempnam(sys_get_temp_dir(), 'RLT') . '/missing/directory'; + + $this->expectException(Exception::class); + // First character (F) can be upper or lowercase depending on PHP version. + $this->expectExceptionMessage('ailed to open stream'); + + Foo::openFile($filename); + } +}