Files
pest/src/Support/StateGenerator.php
T
2026-07-19 13:59:31 +01:00

239 lines
9.1 KiB
PHP

<?php
declare(strict_types=1);
namespace Pest\Support;
use NunoMaduro\Collision\Adapters\Phpunit\State;
use NunoMaduro\Collision\Adapters\Phpunit\TestResult;
use NunoMaduro\Collision\Exceptions\TestOutcome;
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\Errored;
use PHPUnit\Event\Test\Failed;
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\Framework\SkippedWithMessageException;
use PHPUnit\Metadata\MetadataCollection;
use PHPUnit\TestRunner\TestResult\TestResult as PHPUnitTestResult;
final class StateGenerator
{
public function fromPhpUnitTestResult(int $passedTests, PHPUnitTestResult $testResult): State
{
$state = new State;
foreach ($testResult->testErroredEvents() as $testResultEvent) {
if ($testResultEvent instanceof Errored) {
$state->add(TestResult::fromPestParallelTestCase(
$testResultEvent->test(),
TestResult::FAIL,
$testResultEvent->throwable()
));
} else {
$this->addClassLevelEvent($state, $testResultEvent);
}
}
foreach ($testResult->testFailedEvents() as $testResultEvent) {
if ($testResultEvent instanceof Failed) {
$state->add(TestResult::fromPestParallelTestCase(
$testResultEvent->test(),
TestResult::FAIL,
$testResultEvent->throwable()
));
} else {
$this->addClassLevelEvent($state, $testResultEvent);
}
}
$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()))
));
}
}
foreach ($testResult->testSkippedEvents() as $testResultEvent) {
if ($testResultEvent->message() === '__TODO__') {
$state->add(TestResult::fromPestParallelTestCase($testResultEvent->test(), TestResult::TODO));
continue;
}
$state->add(TestResult::fromPestParallelTestCase(
$testResultEvent->test(),
TestResult::SKIPPED,
ThrowableBuilder::from(new SkippedWithMessageException($testResultEvent->message()))
));
}
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->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->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->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()))
));
}
}
// for each test that passed, we need to add it to the state
for ($i = 0; $i < $passedTests; $i++) {
$state->add(TestResult::fromPestParallelTestCase(
new TestMethod(
"$i", // @phpstan-ignore-line
'', // @phpstan-ignore-line
'', // @phpstan-ignore-line
1,
TestDoxBuilder::fromClassNameAndMethodName('', ''), // @phpstan-ignore-line
MetadataCollection::fromArray([]),
TestDataCollection::fromArray([])
),
TestResult::PASS
));
}
return $state;
}
/**
* Adds the given class-level "hook" failure to the state. Collision's
* `fromBeforeFirstTestMethodErrored` only accepts `BeforeFirstTestMethodErrored`
* events, so the remaining class-level events get a synthesized test method.
*/
private function addClassLevelEvent(State $state, AfterLastTestMethodErrored|AfterLastTestMethodFailed|BeforeFirstTestMethodErrored|BeforeFirstTestMethodFailed $event): void
{
if ($event instanceof BeforeFirstTestMethodErrored) {
$state->add(TestResult::fromBeforeFirstTestMethodErrored($event));
return;
}
$methodName = $event instanceof BeforeFirstTestMethodFailed ? 'beforeAll' : 'afterAll';
$state->add(TestResult::fromPestParallelTestCase(
new TestMethod(
$event->testClassName(), // @phpstan-ignore-line
$methodName,
'', // @phpstan-ignore-line
1,
TestDoxBuilder::fromClassNameAndMethodName($event->testClassName(), $methodName), // @phpstan-ignore-line
MetadataCollection::fromArray([]),
TestDataCollection::fromArray([])
),
TestResult::FAIL,
$event->throwable()
));
}
/**
* @param array<string, list<PhpunitDeprecationTriggered|PhpunitErrorTriggered|PhpunitNoticeTriggered|PhpunitWarningTriggered>> $testResultEvents
*/
private function addTriggeredPhpunitEvents(State $state, array $testResultEvents, string $type): void
{
foreach ($testResultEvents as $events) {
foreach ($events as $event) {
if (! $event->test()->isTestMethod()) {
continue;
}
$state->add(TestResult::fromPestParallelTestCase(
$event->test(),
$type,
ThrowableBuilder::from(new TestOutcome($event->message()))
));
}
}
}
}