From 9233648e97fedd1391620c1a31108a3b6cf8a2d3 Mon Sep 17 00:00:00 2001 From: Sebastian Bergmann Date: Fri, 8 Dec 2023 10:13:17 +0100 Subject: [PATCH] Initial work on expected E_USER_DEPRECATED issues --- .psalm/baseline.xml | 4 ++ src/Framework/TestCase.php | 65 +++++++++++++++++++ src/Runner/TestResult/Collector.php | 21 +++++- src/Runner/TestResult/Facade.php | 11 ++++ .../_files/TestForDeprecatedFeatureTest.php | 50 ++++++++++++++ .../expectation-on-user-deprecation.phpt | 59 +++++++++++++++++ 6 files changed, 208 insertions(+), 2 deletions(-) create mode 100644 tests/end-to-end/event/_files/TestForDeprecatedFeatureTest.php create mode 100644 tests/end-to-end/event/expectation-on-user-deprecation.phpt diff --git a/.psalm/baseline.xml b/.psalm/baseline.xml index 9a50e98044c..74b21d1990b 100644 --- a/.psalm/baseline.xml +++ b/.psalm/baseline.xml @@ -396,6 +396,10 @@ objectForTrait + Facade::deprecationsTriggeredByCurrentTest() + Facade::deprecationsTriggeredByCurrentTest() + Facade::deprecationsTriggeredByCurrentTest() + Facade::deprecationsTriggeredByCurrentTest() getMethod diff --git a/src/Framework/TestCase.php b/src/Framework/TestCase.php index 745b28046d4..afd1bd9bb93 100644 --- a/src/Framework/TestCase.php +++ b/src/Framework/TestCase.php @@ -47,6 +47,7 @@ use function ob_start; use function parse_url; use function pathinfo; +use function preg_match; use function preg_replace; use function setlocale; use function sprintf; @@ -84,6 +85,7 @@ use PHPUnit\Metadata\Api\HookMethods; use PHPUnit\Metadata\Api\Requirements; use PHPUnit\Metadata\Parser\Registry as MetadataRegistry; +use PHPUnit\TestRunner\TestResult\Facade; use PHPUnit\TestRunner\TestResult\PassedTests; use PHPUnit\TextUI\Configuration\Registry as ConfigurationRegistry; use PHPUnit\Util\Test as TestUtil; @@ -188,6 +190,16 @@ abstract class TestCase extends Assert implements Reorderable, SelfDescribing, T */ private array $failureTypes = []; + /** + * @psalm-var ?non-empty-string + */ + private ?string $expectedUserDeprecationMessage = null; + + /** + * @psalm-var ?non-empty-string + */ + private ?string $expectedUserDeprecationMessageRegularExpression = null; + /** * @psalm-param non-empty-string $name * @@ -446,6 +458,7 @@ final public function runBare(): void $this->wasPrepared = true; $this->testResult = $this->runTest(); + $this->verifyDeprecationExpectations(); $this->verifyMockObjects(); $this->invokePostConditionHookMethods($hookMethods, $emitter); @@ -1080,6 +1093,22 @@ final protected function expectNotToPerformAssertions(): void $this->doesNotPerformAssertions = true; } + /** + * @psalm-param non-empty-string $expectedUserDeprecationMessage + */ + final protected function expectUserDeprecationMessage(string $expectedUserDeprecationMessage): void + { + $this->expectedUserDeprecationMessage = $expectedUserDeprecationMessage; + } + + /** + * @psalm-param non-empty-string $expectedUserDeprecationMessageRegularExpression + */ + final protected function expectUserDeprecationMessageMatches(string $expectedUserDeprecationMessageRegularExpression): void + { + $this->expectedUserDeprecationMessageRegularExpression = $expectedUserDeprecationMessageRegularExpression; + } + /** * Returns a builder object to create mock objects using a fluent interface. * @@ -1535,6 +1564,42 @@ protected function onNotSuccessfulTest(Throwable $t): never throw $t; } + /** + * @throws ExpectationFailedException + */ + private function verifyDeprecationExpectations(): void + { + if ($this->expectedUserDeprecationMessageRegularExpression !== null) { + $this->numberOfAssertionsPerformed++; + + foreach (Facade::deprecationsTriggeredByCurrentTest() as $deprecation) { + if (preg_match($this->expectedUserDeprecationMessageRegularExpression, $deprecation) > 0) { + return; + } + } + + throw new ExpectationFailedException( + sprintf( + 'Expected deprecation with message matching regular expression "%s" was not triggered', + $this->expectedUserDeprecationMessageRegularExpression, + ), + ); + } elseif ($this->expectedUserDeprecationMessage !== null) { + $this->numberOfAssertionsPerformed++; + + if (in_array($this->expectedUserDeprecationMessage, Facade::deprecationsTriggeredByCurrentTest(), true)) { + return; + } + + throw new ExpectationFailedException( + sprintf( + 'Expected deprecation with message "%s" was not triggered', + $this->expectedUserDeprecationMessage, + ), + ); + } + } + /** * @throws Throwable */ diff --git a/src/Runner/TestResult/Collector.php b/src/Runner/TestResult/Collector.php index bd6d540a931..21cf05d7e31 100644 --- a/src/Runner/TestResult/Collector.php +++ b/src/Runner/TestResult/Collector.php @@ -59,6 +59,11 @@ final class Collector private bool $prepared = false; private bool $currentTestSuiteForTestClassFailed = false; + /** + * @psalm-var list + */ + private array $deprecationsTriggeredByCurrentTest = []; + /** * @psalm-var non-negative-int */ @@ -279,7 +284,8 @@ public function testSuiteFinished(TestSuiteFinished $event): void public function testPrepared(): void { - $this->prepared = true; + $this->prepared = true; + $this->deprecationsTriggeredByCurrentTest = []; } public function testFinished(Finished $event): void @@ -288,7 +294,8 @@ public function testFinished(Finished $event): void $this->numberOfTestsRun++; - $this->prepared = false; + $this->prepared = false; + $this->deprecationsTriggeredByCurrentTest = []; } public function beforeTestClassMethodErrored(BeforeFirstTestMethodErrored $event): void @@ -348,6 +355,8 @@ public function testConsideredRisky(ConsideredRisky $event): void public function testTriggeredDeprecation(DeprecationTriggered $event): void { + $this->deprecationsTriggeredByCurrentTest[] = $event->message(); + if ($event->ignoredByTest()) { return; } @@ -652,6 +661,14 @@ public function hasWarnings(): bool !empty($this->testRunnerTriggeredWarningEvents); } + /** + * @psalm-return list + */ + public function deprecationsTriggeredByCurrentTest(): array + { + return $this->deprecationsTriggeredByCurrentTest; + } + /** * @psalm-return non-empty-string */ diff --git a/src/Runner/TestResult/Facade.php b/src/Runner/TestResult/Facade.php index 790e99705d6..1069815ad36 100644 --- a/src/Runner/TestResult/Facade.php +++ b/src/Runner/TestResult/Facade.php @@ -39,6 +39,17 @@ public static function result(): TestResult return self::collector()->result(); } + /** + * @psalm-return list + * + * @throws EventFacadeIsSealedException + * @throws UnknownSubscriberTypeException + */ + public static function deprecationsTriggeredByCurrentTest(): array + { + return self::collector()->deprecationsTriggeredByCurrentTest(); + } + /** * @throws EventFacadeIsSealedException * @throws UnknownSubscriberTypeException diff --git a/tests/end-to-end/event/_files/TestForDeprecatedFeatureTest.php b/tests/end-to-end/event/_files/TestForDeprecatedFeatureTest.php new file mode 100644 index 00000000000..a15293076a8 --- /dev/null +++ b/tests/end-to-end/event/_files/TestForDeprecatedFeatureTest.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TestFixture\Event; + +use const E_USER_DEPRECATED; +use function trigger_error; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; +use PHPUnit\Framework\TestCase; + +final class TestForDeprecatedFeatureTest extends TestCase +{ + #[IgnoreDeprecations] + public function testOne(): void + { + $this->expectUserDeprecationMessage('message'); + + @trigger_error('message', E_USER_DEPRECATED); + } + + #[IgnoreDeprecations] + public function testTwo(): void + { + $this->expectUserDeprecationMessage('message'); + + @trigger_error('something else', E_USER_DEPRECATED); + } + + #[IgnoreDeprecations] + public function testThree(): void + { + $this->expectUserDeprecationMessageMatches('/message/'); + + @trigger_error('...message...', E_USER_DEPRECATED); + } + + #[IgnoreDeprecations] + public function testFour(): void + { + $this->expectUserDeprecationMessage('message'); + + @trigger_error('something else', E_USER_DEPRECATED); + } +} diff --git a/tests/end-to-end/event/expectation-on-user-deprecation.phpt b/tests/end-to-end/event/expectation-on-user-deprecation.phpt new file mode 100644 index 00000000000..bb50fd1f7a7 --- /dev/null +++ b/tests/end-to-end/event/expectation-on-user-deprecation.phpt @@ -0,0 +1,59 @@ +--TEST-- +The right events are emitted in the right order for a test which expects the tested code to trigger E_USER_DEPRECATED issues +--FILE-- +run($_SERVER['argv']); + +print file_get_contents($traceFile); + +unlink($traceFile); +--EXPECTF-- +PHPUnit Started (PHPUnit %s using %s) +Test Runner Configured +Test Suite Loaded (4 tests) +Event Facade Sealed +Test Runner Started +Test Suite Sorted +Test Runner Execution Started (4 tests) +Test Suite Started (PHPUnit\TestFixture\Event\TestForDeprecatedFeatureTest, 4 tests) +Test Preparation Started (PHPUnit\TestFixture\Event\TestForDeprecatedFeatureTest::testOne) +Test Prepared (PHPUnit\TestFixture\Event\TestForDeprecatedFeatureTest::testOne) +Test Triggered Test-Ignored Deprecation (PHPUnit\TestFixture\Event\TestForDeprecatedFeatureTest::testOne) +message +Test Passed (PHPUnit\TestFixture\Event\TestForDeprecatedFeatureTest::testOne) +Test Finished (PHPUnit\TestFixture\Event\TestForDeprecatedFeatureTest::testOne) +Test Preparation Started (PHPUnit\TestFixture\Event\TestForDeprecatedFeatureTest::testTwo) +Test Prepared (PHPUnit\TestFixture\Event\TestForDeprecatedFeatureTest::testTwo) +Test Triggered Test-Ignored Deprecation (PHPUnit\TestFixture\Event\TestForDeprecatedFeatureTest::testTwo) +something else +Test Failed (PHPUnit\TestFixture\Event\TestForDeprecatedFeatureTest::testTwo) +Expected deprecation with message "message" was not triggered +Test Finished (PHPUnit\TestFixture\Event\TestForDeprecatedFeatureTest::testTwo) +Test Preparation Started (PHPUnit\TestFixture\Event\TestForDeprecatedFeatureTest::testThree) +Test Prepared (PHPUnit\TestFixture\Event\TestForDeprecatedFeatureTest::testThree) +Test Triggered Test-Ignored Deprecation (PHPUnit\TestFixture\Event\TestForDeprecatedFeatureTest::testThree) +...message... +Test Passed (PHPUnit\TestFixture\Event\TestForDeprecatedFeatureTest::testThree) +Test Finished (PHPUnit\TestFixture\Event\TestForDeprecatedFeatureTest::testThree) +Test Preparation Started (PHPUnit\TestFixture\Event\TestForDeprecatedFeatureTest::testFour) +Test Prepared (PHPUnit\TestFixture\Event\TestForDeprecatedFeatureTest::testFour) +Test Triggered Test-Ignored Deprecation (PHPUnit\TestFixture\Event\TestForDeprecatedFeatureTest::testFour) +something else +Test Failed (PHPUnit\TestFixture\Event\TestForDeprecatedFeatureTest::testFour) +Expected deprecation with message "message" was not triggered +Test Finished (PHPUnit\TestFixture\Event\TestForDeprecatedFeatureTest::testFour) +Test Suite Finished (PHPUnit\TestFixture\Event\TestForDeprecatedFeatureTest, 4 tests) +Test Runner Execution Finished +Test Runner Finished +PHPUnit Finished (Shell Exit Code: 1)