From 86ccf047021c24e2d7faddd2ee912ddd2c867835 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksandr=20=C5=A0t=C5=A1epelin?= Date: Wed, 22 Apr 2026 15:56:18 +0300 Subject: [PATCH 1/4] fix: handle remaining PHPUnit TestResult events in parallel state Covers testSuiteSkippedEvents, errors, and the three testRunnerTriggered{Deprecation,Notice,Warning} event collections so that invalid datasets, runner-level issues, and skipped suites are reflected in the parallel summary. --- src/Support/StateGenerator.php | 73 ++++++++++ tests/Unit/Support/StateGenerator.php | 195 ++++++++++++++++++++++++++ 2 files changed, 268 insertions(+) create mode 100644 tests/Unit/Support/StateGenerator.php diff --git a/src/Support/StateGenerator.php b/src/Support/StateGenerator.php index f9b32d604..2da883663 100644 --- a/src/Support/StateGenerator.php +++ b/src/Support/StateGenerator.php @@ -165,6 +165,60 @@ public function fromPhpUnitTestResult(int $passedTests, PHPUnitTestResult $testR } } + foreach ($testResult->errors() as $testResultEvent) { + foreach ($testResultEvent->triggeringTests() as $triggeringTest) { + ['test' => $test] = $triggeringTest; + + $state->add(TestResult::fromPestParallelTestCase( + $test, + TestResult::FAIL, + ThrowableBuilder::from(new TestOutcome($testResultEvent->description())) + )); + } + } + + $standaloneSequence = 0; + + foreach ($testResult->testSuiteSkippedEvents() as $testResultEvent) { + $this->addStandaloneEvent( + $state, + $testResultEvent->testSuite()->name(), + TestResult::SKIPPED, + $testResultEvent->message(), + ++$standaloneSequence, + ); + } + + foreach ($testResult->testRunnerTriggeredDeprecationEvents() as $testResultEvent) { + $this->addStandaloneEvent( + $state, + 'PHPUnit test runner deprecation', + TestResult::DEPRECATED, + $testResultEvent->message(), + ++$standaloneSequence, + ); + } + + foreach ($testResult->testRunnerTriggeredNoticeEvents() as $testResultEvent) { + $this->addStandaloneEvent( + $state, + 'PHPUnit test runner notice', + TestResult::NOTICE, + $testResultEvent->message(), + ++$standaloneSequence, + ); + } + + foreach ($testResult->testRunnerTriggeredWarningEvents() as $testResultEvent) { + $this->addStandaloneEvent( + $state, + 'PHPUnit test runner warning', + TestResult::WARN, + $testResultEvent->message(), + ++$standaloneSequence, + ); + } + // for each test that passed, we need to add it to the state for ($i = 0; $i < $passedTests; $i++) { $state->add(TestResult::fromPestParallelTestCase( @@ -203,4 +257,23 @@ private function addTriggeredPhpunitEvents(State $state, array $testResultEvents } } } + + private function addStandaloneEvent(State $state, string $className, string $type, string $message, int $sequence): void + { + $methodName = 'event#'.$sequence; + + $state->add(TestResult::fromPestParallelTestCase( + new TestMethod( + $className, // @phpstan-ignore-line + $methodName, // @phpstan-ignore-line + ' ', // @phpstan-ignore-line + 1, + TestDoxBuilder::fromClassNameAndMethodName($className, $methodName), // @phpstan-ignore-line + MetadataCollection::fromArray([]), + TestDataCollection::fromArray([]), + ), + $type, + ThrowableBuilder::from(new TestOutcome($message)) + )); + } } diff --git a/tests/Unit/Support/StateGenerator.php b/tests/Unit/Support/StateGenerator.php new file mode 100644 index 000000000..d311df0c0 --- /dev/null +++ b/tests/Unit/Support/StateGenerator.php @@ -0,0 +1,195 @@ + [], + 'testFailedEvents' => [], + 'testConsideredRiskyEvents' => [], + 'testSuiteSkippedEvents' => [], + 'testSkippedEvents' => [], + 'testMarkedIncompleteEvents' => [], + 'testTriggeredPhpunitDeprecationEvents' => [], + 'testTriggeredPhpunitErrorEvents' => [], + 'testTriggeredPhpunitNoticeEvents' => [], + 'testTriggeredPhpunitWarningEvents' => [], + 'testRunnerTriggeredDeprecationEvents' => [], + 'testRunnerTriggeredNoticeEvents' => [], + 'testRunnerTriggeredWarningEvents' => [], + 'errors' => [], + 'deprecations' => [], + 'notices' => [], + 'warnings' => [], + 'phpDeprecations' => [], + 'phpNotices' => [], + 'phpWarnings' => [], + ]; + + $values = array_merge($defaults, $overrides); + + return new PHPUnitTestResult( + 0, + 0, + 0, + $values['testErroredEvents'], + $values['testFailedEvents'], + $values['testConsideredRiskyEvents'], + $values['testSuiteSkippedEvents'], + $values['testSkippedEvents'], + $values['testMarkedIncompleteEvents'], + $values['testTriggeredPhpunitDeprecationEvents'], + $values['testTriggeredPhpunitErrorEvents'], + $values['testTriggeredPhpunitNoticeEvents'], + $values['testTriggeredPhpunitWarningEvents'], + $values['testRunnerTriggeredDeprecationEvents'], + $values['testRunnerTriggeredNoticeEvents'], + $values['testRunnerTriggeredWarningEvents'], + $values['errors'], + $values['deprecations'], + $values['notices'], + $values['warnings'], + $values['phpDeprecations'], + $values['phpNotices'], + $values['phpWarnings'], + 0, + ); +}; + +it('records errors as failed results', function () use ($phpUnitTestResult, $syntheticTest) { + $issue = Issue::from('/tmp/test.php', 1, 'user error description', $syntheticTest()); + + $state = (new StateGenerator)->fromPhpUnitTestResult(0, $phpUnitTestResult(['errors' => [$issue]])); + + expect($state->suiteTests)->toHaveCount(1); + + $result = array_values($state->suiteTests)[0]; + + expect($result->type)->toBe(CollisionTestResult::FAIL) + ->and($result->throwable?->message())->toBe('user error description'); +}); + +it('records test runner triggered deprecation events', function () use ($phpUnitTestResult, $telemetryInfo) { + $event = new RunnerDeprecationTriggered($telemetryInfo(), 'cli flag is deprecated'); + + $state = (new StateGenerator)->fromPhpUnitTestResult(0, $phpUnitTestResult([ + 'testRunnerTriggeredDeprecationEvents' => [$event], + ])); + + expect($state->suiteTests)->toHaveCount(1); + + $result = array_values($state->suiteTests)[0]; + + expect($result->type)->toBe(CollisionTestResult::DEPRECATED) + ->and($result->testCaseName)->toBe('PHPUnit test runner deprecation') + ->and($result->throwable?->message())->toBe('cli flag is deprecated'); +}); + +it('records test runner triggered notice events', function () use ($phpUnitTestResult, $telemetryInfo) { + $event = new RunnerNoticeTriggered($telemetryInfo(), 'runner notice'); + + $state = (new StateGenerator)->fromPhpUnitTestResult(0, $phpUnitTestResult([ + 'testRunnerTriggeredNoticeEvents' => [$event], + ])); + + expect($state->suiteTests)->toHaveCount(1); + + $result = array_values($state->suiteTests)[0]; + + expect($result->type)->toBe(CollisionTestResult::NOTICE) + ->and($result->testCaseName)->toBe('PHPUnit test runner notice') + ->and($result->throwable?->message())->toBe('runner notice'); +}); + +it('records test runner triggered warning events', function () use ($phpUnitTestResult, $telemetryInfo) { + $event = new RunnerWarningTriggered($telemetryInfo(), 'runner warning'); + + $state = (new StateGenerator)->fromPhpUnitTestResult(0, $phpUnitTestResult([ + 'testRunnerTriggeredWarningEvents' => [$event], + ])); + + expect($state->suiteTests)->toHaveCount(1); + + $result = array_values($state->suiteTests)[0]; + + expect($result->type)->toBe(CollisionTestResult::WARN) + ->and($result->testCaseName)->toBe('PHPUnit test runner warning') + ->and($result->throwable?->message())->toBe('runner warning'); +}); + +it('records test suite skipped events', function () use ($phpUnitTestResult, $telemetryInfo) { + $suite = new TestSuiteWithName('AcmeSuite', 3, TestCollection::fromArray([])); + $event = new TestSuiteSkipped($telemetryInfo(), $suite, 'requires PHP 9'); + + $state = (new StateGenerator)->fromPhpUnitTestResult(0, $phpUnitTestResult([ + 'testSuiteSkippedEvents' => [$event], + ])); + + expect($state->suiteTests)->toHaveCount(1); + + $result = array_values($state->suiteTests)[0]; + + expect($result->type)->toBe(CollisionTestResult::SKIPPED) + ->and($result->testCaseName)->toBe('AcmeSuite') + ->and($result->throwable?->message())->toBe('requires PHP 9'); +}); + +it('keeps multiple standalone events as distinct entries', function () use ($phpUnitTestResult, $telemetryInfo) { + $state = (new StateGenerator)->fromPhpUnitTestResult(0, $phpUnitTestResult([ + 'testRunnerTriggeredDeprecationEvents' => [ + new RunnerDeprecationTriggered($telemetryInfo(), 'first'), + new RunnerDeprecationTriggered($telemetryInfo(), 'second'), + ], + 'testRunnerTriggeredWarningEvents' => [ + new RunnerWarningTriggered($telemetryInfo(), 'third'), + ], + ])); + + expect($state->suiteTests)->toHaveCount(3); +}); From 51ddfbeef2e40039838f5fe7a2a76799e6cf2349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksandr=20=C5=A0t=C5=A1epelin?= Date: Wed, 22 Apr 2026 15:57:37 +0300 Subject: [PATCH 2/4] refactor: collapse repeated event loops into helpers Extracts the shared shapes scattered across fromPhpUnitTestResult into private helpers: - addIssueEvents: per-triggering-test loops (deprecations, notices, warnings, errors). - addThrowableEvents: test()/throwable() loops (testFailedEvents and testMarkedIncompleteEvents). - addStandaloneEvents: the three runner-level event loops. - addTriggeredPhpunitEvents: now also accepts testConsideredRiskyEvents since it shares the same nested-array shape and test()/message() surface. No behavior change. --- src/Support/StateGenerator.php | 199 ++++++++++--------------------- tests/.snapshots/Failure.php.inc | 2 +- tests/.snapshots/success.txt | 10 +- tests/Visual/Parallel.php | 4 +- 4 files changed, 76 insertions(+), 139 deletions(-) diff --git a/src/Support/StateGenerator.php b/src/Support/StateGenerator.php index 2da883663..236ad9940 100644 --- a/src/Support/StateGenerator.php +++ b/src/Support/StateGenerator.php @@ -10,22 +10,31 @@ use PHPUnit\Event\Code\TestDoxBuilder; use PHPUnit\Event\Code\TestMethod; use PHPUnit\Event\Code\ThrowableBuilder; +use PHPUnit\Event\Test\ConsideredRisky; use PHPUnit\Event\Test\Errored; use PHPUnit\Event\Test\Failed; +use PHPUnit\Event\Test\MarkedIncomplete; use PHPUnit\Event\Test\PhpunitDeprecationTriggered; use PHPUnit\Event\Test\PhpunitErrorTriggered; use PHPUnit\Event\Test\PhpunitNoticeTriggered; use PHPUnit\Event\Test\PhpunitWarningTriggered; use PHPUnit\Event\TestData\TestDataCollection; +use PHPUnit\Event\TestRunner\DeprecationTriggered; +use PHPUnit\Event\TestRunner\NoticeTriggered; +use PHPUnit\Event\TestRunner\WarningTriggered; use PHPUnit\Framework\SkippedWithMessageException; use PHPUnit\Metadata\MetadataCollection; +use PHPUnit\TestRunner\TestResult\Issues\Issue; use PHPUnit\TestRunner\TestResult\TestResult as PHPUnitTestResult; final class StateGenerator { + private int $standaloneSequence = 0; + public function fromPhpUnitTestResult(int $passedTests, PHPUnitTestResult $testResult): State { $state = new State; + $this->standaloneSequence = 0; foreach ($testResult->testErroredEvents() as $testResultEvent) { if ($testResultEvent instanceof Errored) { @@ -55,23 +64,8 @@ public function fromPhpUnitTestResult(int $passedTests, PHPUnitTestResult $testR $this->addTriggeredPhpunitEvents($state, $testResult->testTriggeredPhpunitErrorEvents(), TestResult::FAIL); - foreach ($testResult->testMarkedIncompleteEvents() as $testResultEvent) { - $state->add(TestResult::fromPestParallelTestCase( - $testResultEvent->test(), - TestResult::INCOMPLETE, - $testResultEvent->throwable() - )); - } - - foreach ($testResult->testConsideredRiskyEvents() as $riskyEvents) { - foreach ($riskyEvents as $riskyEvent) { - $state->add(TestResult::fromPestParallelTestCase( - $riskyEvent->test(), - TestResult::RISKY, - ThrowableBuilder::from(new TestOutcome($riskyEvent->message())) - )); - } - } + $this->addThrowableEvents($state, $testResult->testMarkedIncompleteEvents(), TestResult::INCOMPLETE); + $this->addTriggeredPhpunitEvents($state, $testResult->testConsideredRiskyEvents(), TestResult::RISKY); foreach ($testResult->testSkippedEvents() as $testResultEvent) { if ($testResultEvent->message() === '__TODO__') { @@ -87,97 +81,19 @@ public function fromPhpUnitTestResult(int $passedTests, PHPUnitTestResult $testR )); } - foreach ($testResult->deprecations() as $testResultEvent) { - foreach ($testResultEvent->triggeringTests() as $triggeringTest) { - ['test' => $test] = $triggeringTest; - - $state->add(TestResult::fromPestParallelTestCase( - $test, - TestResult::DEPRECATED, - ThrowableBuilder::from(new TestOutcome($testResultEvent->description())) - )); - } - } - - foreach ($testResult->phpDeprecations() as $testResultEvent) { - foreach ($testResultEvent->triggeringTests() as $triggeringTest) { - ['test' => $test] = $triggeringTest; - - $state->add(TestResult::fromPestParallelTestCase( - $test, - TestResult::DEPRECATED, - ThrowableBuilder::from(new TestOutcome($testResultEvent->description())) - )); - } - } - + $this->addIssueEvents($state, $testResult->deprecations(), TestResult::DEPRECATED); + $this->addIssueEvents($state, $testResult->phpDeprecations(), TestResult::DEPRECATED); $this->addTriggeredPhpunitEvents($state, $testResult->testTriggeredPhpunitDeprecationEvents(), TestResult::DEPRECATED); - foreach ($testResult->notices() as $testResultEvent) { - foreach ($testResultEvent->triggeringTests() as $triggeringTest) { - ['test' => $test] = $triggeringTest; - - $state->add(TestResult::fromPestParallelTestCase( - $test, - TestResult::NOTICE, - ThrowableBuilder::from(new TestOutcome($testResultEvent->description())) - )); - } - } - - foreach ($testResult->phpNotices() as $testResultEvent) { - foreach ($testResultEvent->triggeringTests() as $triggeringTest) { - ['test' => $test] = $triggeringTest; - - $state->add(TestResult::fromPestParallelTestCase( - $test, - TestResult::NOTICE, - ThrowableBuilder::from(new TestOutcome($testResultEvent->description())) - )); - } - } - + $this->addIssueEvents($state, $testResult->notices(), TestResult::NOTICE); + $this->addIssueEvents($state, $testResult->phpNotices(), TestResult::NOTICE); $this->addTriggeredPhpunitEvents($state, $testResult->testTriggeredPhpunitNoticeEvents(), TestResult::NOTICE); - foreach ($testResult->warnings() as $testResultEvent) { - foreach ($testResultEvent->triggeringTests() as $triggeringTest) { - ['test' => $test] = $triggeringTest; - - $state->add(TestResult::fromPestParallelTestCase( - $test, - TestResult::WARN, - ThrowableBuilder::from(new TestOutcome($testResultEvent->description())) - )); - } - } - + $this->addIssueEvents($state, $testResult->warnings(), TestResult::WARN); + $this->addIssueEvents($state, $testResult->phpWarnings(), TestResult::WARN); $this->addTriggeredPhpunitEvents($state, $testResult->testTriggeredPhpunitWarningEvents(), TestResult::WARN); - foreach ($testResult->phpWarnings() as $testResultEvent) { - foreach ($testResultEvent->triggeringTests() as $triggeringTest) { - ['test' => $test] = $triggeringTest; - - $state->add(TestResult::fromPestParallelTestCase( - $test, - TestResult::WARN, - ThrowableBuilder::from(new TestOutcome($testResultEvent->description())) - )); - } - } - - foreach ($testResult->errors() as $testResultEvent) { - foreach ($testResultEvent->triggeringTests() as $triggeringTest) { - ['test' => $test] = $triggeringTest; - - $state->add(TestResult::fromPestParallelTestCase( - $test, - TestResult::FAIL, - ThrowableBuilder::from(new TestOutcome($testResultEvent->description())) - )); - } - } - - $standaloneSequence = 0; + $this->addIssueEvents($state, $testResult->errors(), TestResult::FAIL); foreach ($testResult->testSuiteSkippedEvents() as $testResultEvent) { $this->addStandaloneEvent( @@ -185,39 +101,12 @@ public function fromPhpUnitTestResult(int $passedTests, PHPUnitTestResult $testR $testResultEvent->testSuite()->name(), TestResult::SKIPPED, $testResultEvent->message(), - ++$standaloneSequence, - ); - } - - foreach ($testResult->testRunnerTriggeredDeprecationEvents() as $testResultEvent) { - $this->addStandaloneEvent( - $state, - 'PHPUnit test runner deprecation', - TestResult::DEPRECATED, - $testResultEvent->message(), - ++$standaloneSequence, ); } - foreach ($testResult->testRunnerTriggeredNoticeEvents() as $testResultEvent) { - $this->addStandaloneEvent( - $state, - 'PHPUnit test runner notice', - TestResult::NOTICE, - $testResultEvent->message(), - ++$standaloneSequence, - ); - } - - foreach ($testResult->testRunnerTriggeredWarningEvents() as $testResultEvent) { - $this->addStandaloneEvent( - $state, - 'PHPUnit test runner warning', - TestResult::WARN, - $testResultEvent->message(), - ++$standaloneSequence, - ); - } + $this->addStandaloneEvents($state, $testResult->testRunnerTriggeredDeprecationEvents(), 'PHPUnit test runner deprecation', TestResult::DEPRECATED); + $this->addStandaloneEvents($state, $testResult->testRunnerTriggeredNoticeEvents(), 'PHPUnit test runner notice', TestResult::NOTICE); + $this->addStandaloneEvents($state, $testResult->testRunnerTriggeredWarningEvents(), 'PHPUnit test runner warning', TestResult::WARN); // for each test that passed, we need to add it to the state for ($i = 0; $i < $passedTests; $i++) { @@ -239,7 +128,37 @@ public function fromPhpUnitTestResult(int $passedTests, PHPUnitTestResult $testR } /** - * @param array> $testResultEvents + * @param list $issues + */ + private function addIssueEvents(State $state, array $issues, string $type): void + { + foreach ($issues as $issue) { + foreach ($issue->triggeringTests() as ['test' => $test]) { + $state->add(TestResult::fromPestParallelTestCase( + $test, + $type, + ThrowableBuilder::from(new TestOutcome($issue->description())) + )); + } + } + } + + /** + * @param list $events + */ + private function addThrowableEvents(State $state, array $events, string $type): void + { + foreach ($events as $event) { + $state->add(TestResult::fromPestParallelTestCase( + $event->test(), + $type, + $event->throwable(), + )); + } + } + + /** + * @param array> $testResultEvents */ private function addTriggeredPhpunitEvents(State $state, array $testResultEvents, string $type): void { @@ -258,9 +177,19 @@ private function addTriggeredPhpunitEvents(State $state, array $testResultEvents } } - private function addStandaloneEvent(State $state, string $className, string $type, string $message, int $sequence): void + /** + * @param list $events + */ + private function addStandaloneEvents(State $state, array $events, string $className, string $type): void + { + foreach ($events as $event) { + $this->addStandaloneEvent($state, $className, $type, $event->message()); + } + } + + private function addStandaloneEvent(State $state, string $className, string $type, string $message): void { - $methodName = 'event#'.$sequence; + $methodName = 'event#'.(++$this->standaloneSequence); $state->add(TestResult::fromPestParallelTestCase( new TestMethod( diff --git a/tests/.snapshots/Failure.php.inc b/tests/.snapshots/Failure.php.inc index efd423094..03fd9cd98 100644 --- a/tests/.snapshots/Failure.php.inc +++ b/tests/.snapshots/Failure.php.inc @@ -23,6 +23,6 @@ ##teamcity[testFinished name='it is passing' duration='100000' flowId='1234'] ##teamcity[testSuiteFinished name='Tests/tests/Failure' flowId='1234'] - Tests: 3 failed, 1 risky, 2 todos, 1 skipped, 1 passed (3 assertions) + Tests: 3 failed, 4 warnings, 1 risky, 2 todos, 1 skipped, 1 passed (3 assertions) Duration: 1.00s diff --git a/tests/.snapshots/success.txt b/tests/.snapshots/success.txt index 5b6ab1454..f4a08365e 100644 --- a/tests/.snapshots/success.txt +++ b/tests/.snapshots/success.txt @@ -1887,6 +1887,14 @@ ✓ it gets properties from classes ✓ it gets methods from classes + PASS Tests\Unit\Support\StateGenerator + ✓ it records errors as failed results + ✓ it records test runner triggered deprecation events + ✓ it records test runner triggered notice events + ✓ it records test runner triggered warning events + ✓ it records test suite skipped events + ✓ it keeps multiple standalone events as distinct entries + PASS Tests\Unit\Support\Str ✓ it evaluates the code with ('version()', '__pest_evaluable_version__') ✓ it evaluates the code with ('version__ ', '__pest_evaluable_version_____') @@ -2006,4 +2014,4 @@ ✓ pass with dataset with ('my-datas-set-value') ✓ within describe → pass with dataset with ('my-datas-set-value') - Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 40 todos, 35 skipped, 1389 passed (3097 assertions) \ No newline at end of file + Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 40 todos, 35 skipped, 1395 passed (3117 assertions) diff --git a/tests/Visual/Parallel.php b/tests/Visual/Parallel.php index 78df799f2..acec15514 100644 --- a/tests/Visual/Parallel.php +++ b/tests/Visual/Parallel.php @@ -24,13 +24,13 @@ $file = file_get_contents(__FILE__); $file = preg_replace( '/\$expected = \'.*?\';/', - "\$expected = '2 deprecated, 4 warnings, 5 incomplete, 3 notices, 40 todos, 27 skipped, 1372 passed (3044 assertions)';", + "\$expected = '2 deprecated, 4 warnings, 5 incomplete, 3 notices, 40 todos, 27 skipped, 1378 passed (3064 assertions)';", $file, ); file_put_contents(__FILE__, $file); } - $expected = '2 deprecated, 4 warnings, 5 incomplete, 3 notices, 40 todos, 27 skipped, 1372 passed (3044 assertions)'; + $expected = '2 deprecated, 4 warnings, 5 incomplete, 3 notices, 40 todos, 27 skipped, 1378 passed (3064 assertions)'; expect($output) ->toContain("Tests: {$expected}") From 096ae016c49f30d6d37bf5533c0b8c392ca1da64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksandr=20=C5=A0t=C5=A1epelin?= Date: Wed, 22 Apr 2026 16:32:03 +0300 Subject: [PATCH 3/4] refactor: derive standalone event ids from array index Drops the per-call counter in favour of the foreach index. Each caller already passes a unique className per event group, so className plus index is enough to keep every synthesised TestMethod id distinct without tracking mutable state on the generator. --- src/Support/StateGenerator.php | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Support/StateGenerator.php b/src/Support/StateGenerator.php index 236ad9940..a55204e6b 100644 --- a/src/Support/StateGenerator.php +++ b/src/Support/StateGenerator.php @@ -29,12 +29,9 @@ final class StateGenerator { - private int $standaloneSequence = 0; - public function fromPhpUnitTestResult(int $passedTests, PHPUnitTestResult $testResult): State { $state = new State; - $this->standaloneSequence = 0; foreach ($testResult->testErroredEvents() as $testResultEvent) { if ($testResultEvent instanceof Errored) { @@ -95,12 +92,13 @@ public function fromPhpUnitTestResult(int $passedTests, PHPUnitTestResult $testR $this->addIssueEvents($state, $testResult->errors(), TestResult::FAIL); - foreach ($testResult->testSuiteSkippedEvents() as $testResultEvent) { + foreach ($testResult->testSuiteSkippedEvents() as $index => $testResultEvent) { $this->addStandaloneEvent( $state, $testResultEvent->testSuite()->name(), TestResult::SKIPPED, $testResultEvent->message(), + $index, ); } @@ -182,14 +180,14 @@ private function addTriggeredPhpunitEvents(State $state, array $testResultEvents */ private function addStandaloneEvents(State $state, array $events, string $className, string $type): void { - foreach ($events as $event) { - $this->addStandaloneEvent($state, $className, $type, $event->message()); + foreach ($events as $index => $event) { + $this->addStandaloneEvent($state, $className, $type, $event->message(), $index); } } - private function addStandaloneEvent(State $state, string $className, string $type, string $message): void + private function addStandaloneEvent(State $state, string $className, string $type, string $message, int $index): void { - $methodName = 'event#'.(++$this->standaloneSequence); + $methodName = 'event#'.$index; $state->add(TestResult::fromPestParallelTestCase( new TestMethod( From f264b7e934fe5975b7bfaec97306dc4131f59a15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksandr=20=C5=A0t=C5=A1epelin?= Date: Sun, 28 Jun 2026 23:46:44 +0300 Subject: [PATCH 4/4] refactor: collapse errored/failed event loops into a helper Fold the duplicated testErroredEvents/testFailedEvents loops into a shared addErroredOrFailedEvents helper, and refresh the Failure snapshot warning count (4 -> 2) for PHPUnit 13 after the 5.x rebase. --- src/Support/StateGenerator.php | 54 +++++++++++++++++--------------- tests/.snapshots/Failure.php.inc | 2 +- 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/Support/StateGenerator.php b/src/Support/StateGenerator.php index a55204e6b..8d7a5f86a 100644 --- a/src/Support/StateGenerator.php +++ b/src/Support/StateGenerator.php @@ -10,6 +10,10 @@ use PHPUnit\Event\Code\TestDoxBuilder; use PHPUnit\Event\Code\TestMethod; use PHPUnit\Event\Code\ThrowableBuilder; +use PHPUnit\Event\Test\AfterLastTestMethodErrored; +use PHPUnit\Event\Test\AfterLastTestMethodFailed; +use PHPUnit\Event\Test\BeforeFirstTestMethodErrored; +use PHPUnit\Event\Test\BeforeFirstTestMethodFailed; use PHPUnit\Event\Test\ConsideredRisky; use PHPUnit\Event\Test\Errored; use PHPUnit\Event\Test\Failed; @@ -33,31 +37,8 @@ public function fromPhpUnitTestResult(int $passedTests, PHPUnitTestResult $testR { $state = new State; - foreach ($testResult->testErroredEvents() as $testResultEvent) { - if ($testResultEvent instanceof Errored) { - $state->add(TestResult::fromPestParallelTestCase( - $testResultEvent->test(), - TestResult::FAIL, - $testResultEvent->throwable() - )); - } else { - // @phpstan-ignore-next-line - $state->add(TestResult::fromBeforeFirstTestMethodErrored($testResultEvent)); - } - } - - foreach ($testResult->testFailedEvents() as $testResultEvent) { - if ($testResultEvent instanceof Failed) { - $state->add(TestResult::fromPestParallelTestCase( - $testResultEvent->test(), - TestResult::FAIL, - $testResultEvent->throwable() - )); - } else { - // @phpstan-ignore-next-line - $state->add(TestResult::fromBeforeFirstTestMethodErrored($testResultEvent)); - } - } + $this->addErroredOrFailedEvents($state, $testResult->testErroredEvents()); + $this->addErroredOrFailedEvents($state, $testResult->testFailedEvents()); $this->addTriggeredPhpunitEvents($state, $testResult->testTriggeredPhpunitErrorEvents(), TestResult::FAIL); @@ -125,6 +106,27 @@ public function fromPhpUnitTestResult(int $passedTests, PHPUnitTestResult $testR return $state; } + /** + * @param list $events + */ + private function addErroredOrFailedEvents(State $state, array $events): void + { + foreach ($events as $event) { + if ($event instanceof Errored || $event instanceof Failed) { + $state->add(TestResult::fromPestParallelTestCase( + $event->test(), + TestResult::FAIL, + $event->throwable() + )); + + continue; + } + + // @phpstan-ignore-next-line + $state->add(TestResult::fromBeforeFirstTestMethodErrored($event)); + } + } + /** * @param list $issues */ @@ -142,7 +144,7 @@ private function addIssueEvents(State $state, array $issues, string $type): void } /** - * @param list $events + * @param list $events */ private function addThrowableEvents(State $state, array $events, string $type): void { diff --git a/tests/.snapshots/Failure.php.inc b/tests/.snapshots/Failure.php.inc index 03fd9cd98..96f5cb76c 100644 --- a/tests/.snapshots/Failure.php.inc +++ b/tests/.snapshots/Failure.php.inc @@ -23,6 +23,6 @@ ##teamcity[testFinished name='it is passing' duration='100000' flowId='1234'] ##teamcity[testSuiteFinished name='Tests/tests/Failure' flowId='1234'] - Tests: 3 failed, 4 warnings, 1 risky, 2 todos, 1 skipped, 1 passed (3 assertions) + Tests: 3 failed, 2 warnings, 1 risky, 2 todos, 1 skipped, 1 passed (3 assertions) Duration: 1.00s