diff --git a/src/Support/StateGenerator.php b/src/Support/StateGenerator.php index f9b32d604..a55204e6b 100644 --- a/src/Support/StateGenerator.php +++ b/src/Support/StateGenerator.php @@ -10,15 +10,21 @@ 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 @@ -55,23 +61,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,84 +78,34 @@ 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; + $this->addIssueEvents($state, $testResult->errors(), TestResult::FAIL); - $state->add(TestResult::fromPestParallelTestCase( - $test, - TestResult::WARN, - ThrowableBuilder::from(new TestOutcome($testResultEvent->description())) - )); - } + foreach ($testResult->testSuiteSkippedEvents() as $index => $testResultEvent) { + $this->addStandaloneEvent( + $state, + $testResultEvent->testSuite()->name(), + TestResult::SKIPPED, + $testResultEvent->message(), + $index, + ); } + $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++) { $state->add(TestResult::fromPestParallelTestCase( @@ -185,7 +126,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 { @@ -203,4 +174,33 @@ private function addTriggeredPhpunitEvents(State $state, array $testResultEvents } } } + + /** + * @param list $events + */ + private function addStandaloneEvents(State $state, array $events, string $className, string $type): void + { + foreach ($events as $index => $event) { + $this->addStandaloneEvent($state, $className, $type, $event->message(), $index); + } + } + + private function addStandaloneEvent(State $state, string $className, string $type, string $message, int $index): void + { + $methodName = 'event#'.$index; + + $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/.snapshots/Failure.php.inc b/tests/.snapshots/Failure.php.inc index 5c3878fa9..0b31262f5 100644 --- a/tests/.snapshots/Failure.php.inc +++ b/tests/.snapshots/Failure.php.inc @@ -47,10 +47,10 @@ ##teamcity[testSuiteFinished name='Tests/tests/Failure' 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 - 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 58dbeddf4..e09696981 100644 --- a/tests/.snapshots/success.txt +++ b/tests/.snapshots/success.txt @@ -1820,6 +1820,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_____') @@ -1938,4 +1946,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, 1329 passed (3010 assertions) \ No newline at end of file + Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 40 todos, 35 skipped, 1335 passed (3030 assertions) \ No newline at end of file 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); +}); diff --git a/tests/Visual/Parallel.php b/tests/Visual/Parallel.php index 1055526b2..eff9d506c 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, 1313 passed (2959 assertions)';", + "\$expected = '2 deprecated, 4 warnings, 5 incomplete, 3 notices, 40 todos, 27 skipped, 1319 passed (2979 assertions)';", $file, ); file_put_contents(__FILE__, $file); } - $expected = '2 deprecated, 4 warnings, 5 incomplete, 3 notices, 40 todos, 27 skipped, 1313 passed (2959 assertions)'; + $expected = '2 deprecated, 4 warnings, 5 incomplete, 3 notices, 40 todos, 27 skipped, 1319 passed (2979 assertions)'; expect($output) ->toContain("Tests: {$expected}")