mirror of
https://github.com/pestphp/pest.git
synced 2026-03-06 07:47:22 +01:00
junit support
This commit is contained in:
@ -25,6 +25,7 @@ final class BootSubscribers implements Bootstrapper
|
||||
Subscribers\EnsureIgnorableTestCasesAreIgnored::class,
|
||||
Subscribers\EnsureKernelDumpIsFlushed::class,
|
||||
Subscribers\EnsureTeamCityEnabled::class,
|
||||
Subscribers\EnsureJunitEnabled::class,
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
228
src/Logging/JUnit/Converter.php
Normal file
228
src/Logging/JUnit/Converter.php
Normal file
@ -0,0 +1,228 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Logging\JUnit;
|
||||
|
||||
use NunoMaduro\Collision\Adapters\Phpunit\State;
|
||||
use Pest\Exceptions\ShouldNotHappen;
|
||||
use Pest\Support\StateGenerator;
|
||||
use Pest\Support\Str;
|
||||
use PHPUnit\Event\Code\Test;
|
||||
use PHPUnit\Event\Code\TestMethod;
|
||||
use PHPUnit\Event\Code\Throwable;
|
||||
use PHPUnit\Event\Test\BeforeFirstTestMethodErrored;
|
||||
use PHPUnit\Event\Test\ConsideredRisky;
|
||||
use PHPUnit\Event\Test\Errored;
|
||||
use PHPUnit\Event\Test\Failed;
|
||||
use PHPUnit\Event\Test\MarkedIncomplete;
|
||||
use PHPUnit\Event\Test\Skipped;
|
||||
use PHPUnit\Event\TestSuite\TestSuite;
|
||||
use PHPUnit\Framework\Exception as FrameworkException;
|
||||
use PHPUnit\TestRunner\TestResult\TestResult as PhpUnitTestResult;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Converter
|
||||
{
|
||||
private const PREFIX = 'P\\';
|
||||
|
||||
private readonly StateGenerator $stateGenerator;
|
||||
|
||||
/**
|
||||
* Creates a new instance of the Converter.
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly string $rootPath,
|
||||
) {
|
||||
$this->stateGenerator = new StateGenerator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the test case method name.
|
||||
*/
|
||||
public function getTestCaseMethodName(Test $test): string
|
||||
{
|
||||
if (! $test instanceof TestMethod) {
|
||||
throw ShouldNotHappen::fromMessage('Not an instance of TestMethod');
|
||||
}
|
||||
|
||||
return $test->testDox()->prettifiedMethodName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the test case location.
|
||||
*/
|
||||
public function getTestCaseLocation(Test $test, bool $withDescription = false): string
|
||||
{
|
||||
if (! $test instanceof TestMethod) {
|
||||
throw ShouldNotHappen::fromMessage('Not an instance of TestMethod');
|
||||
}
|
||||
|
||||
$path = $test->testDox()->prettifiedClassName();
|
||||
$relativePath = $this->toRelativePath($path);
|
||||
|
||||
// TODO: Get the description without the dataset.
|
||||
$description = $test->testDox()->prettifiedMethodName();
|
||||
|
||||
if (! $withDescription) {
|
||||
return $relativePath;
|
||||
}
|
||||
|
||||
return "$relativePath::$description";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the exception message.
|
||||
*/
|
||||
public function getExceptionMessage(Throwable $throwable): string
|
||||
{
|
||||
if (is_a($throwable->className(), FrameworkException::class, true)) {
|
||||
return $throwable->message();
|
||||
}
|
||||
|
||||
$buffer = $throwable->className();
|
||||
$throwableMessage = $throwable->message();
|
||||
|
||||
if ($throwableMessage !== '') {
|
||||
$buffer .= ": $throwableMessage";
|
||||
}
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the exception details.
|
||||
*/
|
||||
public function getExceptionDetails(Throwable $throwable): string
|
||||
{
|
||||
$buffer = $this->getStackTrace($throwable);
|
||||
|
||||
while ($throwable->hasPrevious()) {
|
||||
$throwable = $throwable->previous();
|
||||
|
||||
$buffer .= sprintf(
|
||||
"\nCaused by\n%s\n%s",
|
||||
$throwable->description(),
|
||||
$this->getStackTrace($throwable)
|
||||
);
|
||||
}
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the stack trace.
|
||||
*/
|
||||
public function getStackTrace(Throwable $throwable): string
|
||||
{
|
||||
$stackTrace = $throwable->stackTrace();
|
||||
|
||||
// Split stacktrace per frame.
|
||||
$frames = explode("\n", $stackTrace);
|
||||
|
||||
// Remove empty lines
|
||||
$frames = array_filter($frames);
|
||||
|
||||
// clean the paths of each frame.
|
||||
$frames = array_map(
|
||||
fn (string $frame): string => $this->toRelativePath($frame),
|
||||
$frames
|
||||
);
|
||||
|
||||
// Format stacktrace as `at <path>`
|
||||
$frames = array_map(
|
||||
fn (string $frame) => "at $frame",
|
||||
$frames
|
||||
);
|
||||
|
||||
return implode("\n", $frames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the test suite name.
|
||||
*/
|
||||
public function getTestSuiteName(TestSuite $testSuite): string
|
||||
{
|
||||
$name = $testSuite->name();
|
||||
|
||||
if (str_starts_with($name, self::PREFIX)) {
|
||||
return Str::after($name, self::PREFIX);
|
||||
}
|
||||
|
||||
return Str::after($name, $this->rootPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the test suite location.
|
||||
*/
|
||||
public function getTestSuiteLocation(TestSuite $testSuite): ?string
|
||||
{
|
||||
$tests = $testSuite->tests()->asArray();
|
||||
|
||||
// TODO: figure out how to get the file path without a test being there.
|
||||
if ($tests === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$firstTest = $tests[0];
|
||||
if (! $firstTest instanceof TestMethod) {
|
||||
throw ShouldNotHappen::fromMessage('Not an instance of TestMethod');
|
||||
}
|
||||
|
||||
$path = $firstTest->testDox()->prettifiedClassName();
|
||||
|
||||
return $this->toRelativePath($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the test suite size.
|
||||
*/
|
||||
public function getTestSuiteSize(TestSuite $testSuite): int
|
||||
{
|
||||
return $testSuite->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the given path in relative path.
|
||||
*/
|
||||
private function toRelativePath(string $path): string
|
||||
{
|
||||
// Remove cwd from the path.
|
||||
return str_replace("$this->rootPath".DIRECTORY_SEPARATOR, '', $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the test result.
|
||||
*/
|
||||
public function getStateFromResult(PhpUnitTestResult $result): State
|
||||
{
|
||||
$events = [
|
||||
...$result->testErroredEvents(),
|
||||
...$result->testFailedEvents(),
|
||||
...$result->testSkippedEvents(),
|
||||
...array_merge(...array_values($result->testConsideredRiskyEvents())),
|
||||
...$result->testMarkedIncompleteEvents(),
|
||||
];
|
||||
|
||||
$numberOfNotPassedTests = count(
|
||||
array_unique(
|
||||
array_map(
|
||||
function (BeforeFirstTestMethodErrored|Errored|Failed|Skipped|ConsideredRisky|MarkedIncomplete $event): string {
|
||||
if ($event instanceof BeforeFirstTestMethodErrored) {
|
||||
return $event->testClassName();
|
||||
}
|
||||
|
||||
return $this->getTestCaseLocation($event->test());
|
||||
},
|
||||
$events
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$numberOfPassedTests = $result->numberOfTestsRun() - $numberOfNotPassedTests;
|
||||
|
||||
return $this->stateGenerator->fromPhpUnitTestResult($numberOfPassedTests, $result);
|
||||
}
|
||||
}
|
||||
398
src/Logging/JUnit/JUnitLogger.php
Normal file
398
src/Logging/JUnit/JUnitLogger.php
Normal file
@ -0,0 +1,398 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Logging\JUnit;
|
||||
|
||||
use DOMDocument;
|
||||
use DOMElement;
|
||||
use Pest\Logging\JUnit\Subscriber\TestErroredSubscriber;
|
||||
use Pest\Logging\JUnit\Subscriber\TestFailedSubscriber;
|
||||
use Pest\Logging\JUnit\Subscriber\TestFinishedSubscriber;
|
||||
use Pest\Logging\JUnit\Subscriber\TestMarkedIncompleteSubscriber;
|
||||
use Pest\Logging\JUnit\Subscriber\TestPreparedSubscriber;
|
||||
use Pest\Logging\JUnit\Subscriber\TestRunnerExecutionFinishedSubscriber;
|
||||
use Pest\Logging\JUnit\Subscriber\TestSkippedSubscriber;
|
||||
use Pest\Logging\JUnit\Subscriber\TestSuiteFinishedSubscriber;
|
||||
use Pest\Logging\JUnit\Subscriber\TestSuiteStartedSubscriber;
|
||||
use Pest\TestSuite;
|
||||
use PHPUnit\Event\Code\TestMethod;
|
||||
use PHPUnit\Event\EventFacadeIsSealedException;
|
||||
use PHPUnit\Event\Facade;
|
||||
use PHPUnit\Event\InvalidArgumentException;
|
||||
use PHPUnit\Event\Telemetry\HRTime;
|
||||
use PHPUnit\Event\Telemetry\Info;
|
||||
use PHPUnit\Event\Test\Errored;
|
||||
use PHPUnit\Event\Test\Failed;
|
||||
use PHPUnit\Event\Test\Finished;
|
||||
use PHPUnit\Event\Test\MarkedIncomplete;
|
||||
use PHPUnit\Event\Test\Prepared;
|
||||
use PHPUnit\Event\Test\Skipped;
|
||||
use PHPUnit\Event\TestData\NoDataSetFromDataProviderException;
|
||||
use PHPUnit\Event\TestSuite\Started;
|
||||
use PHPUnit\Event\UnknownSubscriberTypeException;
|
||||
use PHPUnit\TextUI\Output\Printer;
|
||||
use PHPUnit\Util\Xml;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class JUnitLogger
|
||||
{
|
||||
|
||||
private DOMDocument $document;
|
||||
|
||||
private DOMElement $root;
|
||||
|
||||
/**
|
||||
* @var DOMElement[]
|
||||
*/
|
||||
private array $testSuites = [];
|
||||
|
||||
/**
|
||||
* @psalm-var array<int,int>
|
||||
*/
|
||||
private array $testSuiteTests = [0];
|
||||
|
||||
/**
|
||||
* @psalm-var array<int,int>
|
||||
*/
|
||||
private array $testSuiteAssertions = [0];
|
||||
|
||||
/**
|
||||
* @psalm-var array<int,int>
|
||||
*/
|
||||
private array $testSuiteErrors = [0];
|
||||
|
||||
/**
|
||||
* @psalm-var array<int,int>
|
||||
*/
|
||||
private array $testSuiteFailures = [0];
|
||||
|
||||
/**
|
||||
* @psalm-var array<int,int>
|
||||
*/
|
||||
private array $testSuiteSkipped = [0];
|
||||
|
||||
/**
|
||||
* @psalm-var array<int,int>
|
||||
*/
|
||||
private array $testSuiteTimes = [0];
|
||||
|
||||
private int $testSuiteLevel = 0;
|
||||
|
||||
private ?DOMElement $currentTestCase = null;
|
||||
|
||||
private ?HRTime $time = null;
|
||||
|
||||
private bool $prepared = false;
|
||||
|
||||
/**✅
|
||||
* @throws EventFacadeIsSealedException
|
||||
* @throws UnknownSubscriberTypeException
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly Printer $printer,
|
||||
private readonly OutputInterface $output,
|
||||
private readonly Converter $converter,
|
||||
) {
|
||||
$this->registerSubscribers();
|
||||
$this->createDocument();
|
||||
}
|
||||
|
||||
public function flush(): void
|
||||
{
|
||||
$this->printer->print($this->document->saveXML());
|
||||
|
||||
$this->printer->flush();
|
||||
|
||||
$this->output->writeln('Junit finished');
|
||||
}
|
||||
|
||||
public function testSuiteStarted(Started $event): void
|
||||
{
|
||||
$testSuite = $this->document->createElement('testsuite');
|
||||
$testSuite->setAttribute('name', $this->converter->getTestSuiteName($event->testSuite()));
|
||||
|
||||
if ($event->testSuite()->isForTestClass()) {
|
||||
$testSuite->setAttribute('file', $this->converter->getTestSuiteLocation($event->testSuite()) ?? '');
|
||||
}
|
||||
|
||||
if ($this->testSuiteLevel > 0) {
|
||||
$this->testSuites[$this->testSuiteLevel]->appendChild($testSuite);
|
||||
} else {
|
||||
$this->root->appendChild($testSuite);
|
||||
}
|
||||
|
||||
$this->testSuiteLevel++;
|
||||
$this->testSuites[$this->testSuiteLevel] = $testSuite;
|
||||
$this->testSuiteTests[$this->testSuiteLevel] = 0;
|
||||
$this->testSuiteAssertions[$this->testSuiteLevel] = 0;
|
||||
$this->testSuiteErrors[$this->testSuiteLevel] = 0;
|
||||
$this->testSuiteFailures[$this->testSuiteLevel] = 0;
|
||||
$this->testSuiteSkipped[$this->testSuiteLevel] = 0;
|
||||
$this->testSuiteTimes[$this->testSuiteLevel] = 0;
|
||||
}
|
||||
|
||||
public function testSuiteFinished(): void
|
||||
{
|
||||
$this->testSuites[$this->testSuiteLevel]->setAttribute(
|
||||
'tests',
|
||||
(string) $this->testSuiteTests[$this->testSuiteLevel],
|
||||
);
|
||||
|
||||
$this->testSuites[$this->testSuiteLevel]->setAttribute(
|
||||
'assertions',
|
||||
(string) $this->testSuiteAssertions[$this->testSuiteLevel],
|
||||
);
|
||||
|
||||
$this->testSuites[$this->testSuiteLevel]->setAttribute(
|
||||
'errors',
|
||||
(string) $this->testSuiteErrors[$this->testSuiteLevel],
|
||||
);
|
||||
|
||||
$this->testSuites[$this->testSuiteLevel]->setAttribute(
|
||||
'failures',
|
||||
(string) $this->testSuiteFailures[$this->testSuiteLevel],
|
||||
);
|
||||
|
||||
$this->testSuites[$this->testSuiteLevel]->setAttribute(
|
||||
'skipped',
|
||||
(string) $this->testSuiteSkipped[$this->testSuiteLevel],
|
||||
);
|
||||
|
||||
$this->testSuites[$this->testSuiteLevel]->setAttribute(
|
||||
'time',
|
||||
sprintf('%F', $this->testSuiteTimes[$this->testSuiteLevel]),
|
||||
);
|
||||
|
||||
if ($this->testSuiteLevel > 1) {
|
||||
$this->testSuiteTests[$this->testSuiteLevel - 1] += $this->testSuiteTests[$this->testSuiteLevel];
|
||||
$this->testSuiteAssertions[$this->testSuiteLevel - 1] += $this->testSuiteAssertions[$this->testSuiteLevel];
|
||||
$this->testSuiteErrors[$this->testSuiteLevel - 1] += $this->testSuiteErrors[$this->testSuiteLevel];
|
||||
$this->testSuiteFailures[$this->testSuiteLevel - 1] += $this->testSuiteFailures[$this->testSuiteLevel];
|
||||
$this->testSuiteSkipped[$this->testSuiteLevel - 1] += $this->testSuiteSkipped[$this->testSuiteLevel];
|
||||
$this->testSuiteTimes[$this->testSuiteLevel - 1] += $this->testSuiteTimes[$this->testSuiteLevel];
|
||||
}
|
||||
|
||||
$this->testSuiteLevel--;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
* @throws NoDataSetFromDataProviderException
|
||||
*/
|
||||
public function testPrepared(Prepared $event): void
|
||||
{
|
||||
$this->createTestCase($event);
|
||||
$this->prepared = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function testFinished(Finished $event): void
|
||||
{
|
||||
$this->handleFinish($event->telemetryInfo(), $event->numberOfAssertionsPerformed());
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
* @throws NoDataSetFromDataProviderException
|
||||
*/
|
||||
public function testMarkedIncomplete(MarkedIncomplete $event): void
|
||||
{
|
||||
$this->handleIncompleteOrSkipped($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
* @throws NoDataSetFromDataProviderException
|
||||
*/
|
||||
public function testSkipped(Skipped $event): void
|
||||
{
|
||||
$this->handleIncompleteOrSkipped($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
* @throws NoDataSetFromDataProviderException
|
||||
*/
|
||||
public function testErrored(Errored $event): void
|
||||
{
|
||||
$this->handleFault($event, 'error');
|
||||
|
||||
$this->testSuiteErrors[$this->testSuiteLevel]++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
* @throws NoDataSetFromDataProviderException
|
||||
*/
|
||||
public function testFailed(Failed $event): void
|
||||
{
|
||||
$this->handleFault($event, 'failure');
|
||||
|
||||
$this->testSuiteFailures[$this->testSuiteLevel]++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
private function handleFinish(Info $telemetryInfo, int $numberOfAssertionsPerformed): void
|
||||
{
|
||||
assert($this->currentTestCase !== null);
|
||||
assert($this->time !== null);
|
||||
|
||||
$time = $telemetryInfo->time()->duration($this->time)->asFloat();
|
||||
|
||||
$this->testSuiteAssertions[$this->testSuiteLevel] += $numberOfAssertionsPerformed;
|
||||
|
||||
$this->currentTestCase->setAttribute(
|
||||
'assertions',
|
||||
(string) $numberOfAssertionsPerformed,
|
||||
);
|
||||
|
||||
$this->currentTestCase->setAttribute(
|
||||
'time',
|
||||
sprintf('%F', $time),
|
||||
);
|
||||
|
||||
$this->testSuites[$this->testSuiteLevel]->appendChild(
|
||||
$this->currentTestCase,
|
||||
);
|
||||
|
||||
$this->testSuiteTests[$this->testSuiteLevel]++;
|
||||
$this->testSuiteTimes[$this->testSuiteLevel] += $time;
|
||||
|
||||
$this->currentTestCase = null;
|
||||
$this->time = null;
|
||||
$this->prepared = false;
|
||||
}
|
||||
|
||||
/**✅
|
||||
* @throws EventFacadeIsSealedException
|
||||
* @throws UnknownSubscriberTypeException
|
||||
*/
|
||||
private function registerSubscribers(): void
|
||||
{
|
||||
$subscribers = [
|
||||
new TestSuiteStartedSubscriber($this),
|
||||
new TestSuiteFinishedSubscriber($this),
|
||||
new TestPreparedSubscriber($this),
|
||||
new TestFinishedSubscriber($this),
|
||||
new TestErroredSubscriber($this),
|
||||
new TestFailedSubscriber($this),
|
||||
new TestMarkedIncompleteSubscriber($this),
|
||||
new TestSkippedSubscriber($this),
|
||||
new TestRunnerExecutionFinishedSubscriber($this),
|
||||
];
|
||||
|
||||
Facade::instance()->registerSubscribers(...$subscribers);
|
||||
}
|
||||
|
||||
private function createDocument(): void
|
||||
{
|
||||
$this->output->writeln('Start Junit');
|
||||
|
||||
$this->document = new DOMDocument('1.0', 'UTF-8');
|
||||
$this->document->formatOutput = true;
|
||||
|
||||
$this->root = $this->document->createElement('testsuites');
|
||||
$this->document->appendChild($this->root);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
* @throws NoDataSetFromDataProviderException
|
||||
*/
|
||||
private function handleFault(Errored|Failed $event, string $type): void
|
||||
{
|
||||
if (! $this->prepared) {
|
||||
$this->createTestCase($event);
|
||||
}
|
||||
|
||||
assert($this->currentTestCase !== null);
|
||||
|
||||
$testName = $this->converter->getTestCaseMethodName($event->test());
|
||||
// $message = $this->converter->getExceptionMessage($event->throwable());
|
||||
// $details = $this->converter->getExceptionDetails($event->throwable());
|
||||
|
||||
$buffer = $testName;
|
||||
|
||||
$throwable = $event->throwable();
|
||||
$buffer .= trim(
|
||||
$throwable->description().PHP_EOL.
|
||||
$throwable->stackTrace(),
|
||||
);
|
||||
|
||||
$fault = $this->document->createElement(
|
||||
$type,
|
||||
Xml::prepareString($buffer),
|
||||
);
|
||||
|
||||
$fault->setAttribute('type', $throwable->className());
|
||||
|
||||
$this->currentTestCase->appendChild($fault);
|
||||
|
||||
if (! $this->prepared) {
|
||||
$this->handleFinish($event->telemetryInfo(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
* @throws NoDataSetFromDataProviderException
|
||||
*/
|
||||
private function handleIncompleteOrSkipped(MarkedIncomplete|Skipped $event): void
|
||||
{
|
||||
if (! $this->prepared) {
|
||||
$this->createTestCase($event);
|
||||
}
|
||||
|
||||
assert($this->currentTestCase !== null);
|
||||
|
||||
$skipped = $this->document->createElement('skipped');
|
||||
|
||||
$this->currentTestCase->appendChild($skipped);
|
||||
|
||||
$this->testSuiteSkipped[$this->testSuiteLevel]++;
|
||||
|
||||
if (! $this->prepared) {
|
||||
$this->handleFinish($event->telemetryInfo(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
* @throws NoDataSetFromDataProviderException
|
||||
*
|
||||
* @psalm-assert !null $this->currentTestCase
|
||||
*/
|
||||
private function createTestCase(Errored|Failed|MarkedIncomplete|Prepared|Skipped $event): void
|
||||
{
|
||||
$testCase = $this->document->createElement('testcase');
|
||||
|
||||
$file = $this->converter->getTestCaseLocation($event->test());
|
||||
|
||||
$testCase->setAttribute('name', $this->converter->getTestCaseMethodName($event->test()));
|
||||
// $testCase->setAttribute('name', $event->test()->name());
|
||||
$testCase->setAttribute('file', $file);
|
||||
// $testCase->setAttribute('file', $event->test()->file());
|
||||
|
||||
if ($event->test()->isTestMethod()) {
|
||||
assert($event->test() instanceof TestMethod);
|
||||
|
||||
//dd(TestSuite::getInstance()->tests->get($file));
|
||||
// add classname, and line to this
|
||||
|
||||
$testCase->setAttribute('line', (string) $event->test()->line()); //@todo figure out how to get line number in original pest file
|
||||
$testCase->setAttribute('class', $event->test()->name());
|
||||
$testCase->setAttribute('classname', str_replace('\\', '.', $event->test()->name()));
|
||||
}
|
||||
|
||||
$this->currentTestCase = $testCase;
|
||||
$this->time = $event->telemetryInfo()->time();
|
||||
}
|
||||
}
|
||||
28
src/Logging/JUnit/Subscriber/Subscriber.php
Normal file
28
src/Logging/JUnit/Subscriber/Subscriber.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Logging\JUnit\Subscriber;
|
||||
|
||||
use Pest\Logging\JUnit\JUnitLogger;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
abstract class Subscriber
|
||||
{
|
||||
/**
|
||||
* Creates a new Subscriber instance.
|
||||
*/
|
||||
public function __construct(private readonly JUnitLogger $logger)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new JunitLogger instance.
|
||||
*/
|
||||
final protected function logger(): JUnitLogger
|
||||
{
|
||||
return $this->logger;
|
||||
}
|
||||
}
|
||||
19
src/Logging/JUnit/Subscriber/TestErroredSubscriber.php
Normal file
19
src/Logging/JUnit/Subscriber/TestErroredSubscriber.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Logging\JUnit\Subscriber;
|
||||
|
||||
use PHPUnit\Event\Test\Errored;
|
||||
use PHPUnit\Event\Test\ErroredSubscriber;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class TestErroredSubscriber extends Subscriber implements ErroredSubscriber
|
||||
{
|
||||
public function notify(Errored $event): void
|
||||
{
|
||||
$this->logger()->testErrored($event);
|
||||
}
|
||||
}
|
||||
19
src/Logging/JUnit/Subscriber/TestFailedSubscriber.php
Normal file
19
src/Logging/JUnit/Subscriber/TestFailedSubscriber.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Logging\JUnit\Subscriber;
|
||||
|
||||
use PHPUnit\Event\Test\Failed;
|
||||
use PHPUnit\Event\Test\FailedSubscriber;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class TestFailedSubscriber extends Subscriber implements FailedSubscriber
|
||||
{
|
||||
public function notify(Failed $event): void
|
||||
{
|
||||
$this->logger()->testFailed($event);
|
||||
}
|
||||
}
|
||||
19
src/Logging/JUnit/Subscriber/TestFinishedSubscriber.php
Normal file
19
src/Logging/JUnit/Subscriber/TestFinishedSubscriber.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Logging\JUnit\Subscriber;
|
||||
|
||||
use PHPUnit\Event\Test\Finished;
|
||||
use PHPUnit\Event\Test\FinishedSubscriber;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class TestFinishedSubscriber extends Subscriber implements FinishedSubscriber
|
||||
{
|
||||
public function notify(Finished $event): void
|
||||
{
|
||||
$this->logger()->testFinished($event);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Logging\JUnit\Subscriber;
|
||||
|
||||
use PHPUnit\Event\Test\MarkedIncomplete;
|
||||
use PHPUnit\Event\Test\MarkedIncompleteSubscriber;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class TestMarkedIncompleteSubscriber extends Subscriber implements MarkedIncompleteSubscriber
|
||||
{
|
||||
public function notify(MarkedIncomplete $event): void
|
||||
{
|
||||
$this->logger()->testMarkedIncomplete($event);
|
||||
}
|
||||
}
|
||||
19
src/Logging/JUnit/Subscriber/TestPreparedSubscriber.php
Normal file
19
src/Logging/JUnit/Subscriber/TestPreparedSubscriber.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Logging\JUnit\Subscriber;
|
||||
|
||||
use PHPUnit\Event\Test\Prepared;
|
||||
use PHPUnit\Event\Test\PreparedSubscriber;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class TestPreparedSubscriber extends Subscriber implements PreparedSubscriber
|
||||
{
|
||||
public function notify(Prepared $event): void
|
||||
{
|
||||
$this->logger()->testPrepared($event);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Logging\JUnit\Subscriber;
|
||||
|
||||
use PHPUnit\Event\TestRunner\ExecutionFinished;
|
||||
use PHPUnit\Event\TestRunner\ExecutionFinishedSubscriber;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class TestRunnerExecutionFinishedSubscriber extends Subscriber implements ExecutionFinishedSubscriber
|
||||
{
|
||||
public function notify(ExecutionFinished $event): void
|
||||
{
|
||||
$this->logger()->flush();
|
||||
}
|
||||
}
|
||||
19
src/Logging/JUnit/Subscriber/TestSkippedSubscriber.php
Normal file
19
src/Logging/JUnit/Subscriber/TestSkippedSubscriber.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Logging\JUnit\Subscriber;
|
||||
|
||||
use PHPUnit\Event\Test\Skipped;
|
||||
use PHPUnit\Event\Test\SkippedSubscriber;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class TestSkippedSubscriber extends Subscriber implements SkippedSubscriber
|
||||
{
|
||||
public function notify(Skipped $event): void
|
||||
{
|
||||
$this->logger()->testSkipped($event);
|
||||
}
|
||||
}
|
||||
19
src/Logging/JUnit/Subscriber/TestSuiteFinishedSubscriber.php
Normal file
19
src/Logging/JUnit/Subscriber/TestSuiteFinishedSubscriber.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Logging\JUnit\Subscriber;
|
||||
|
||||
use PHPUnit\Event\TestSuite\Finished;
|
||||
use PHPUnit\Event\TestSuite\FinishedSubscriber;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class TestSuiteFinishedSubscriber extends Subscriber implements FinishedSubscriber
|
||||
{
|
||||
public function notify(Finished $event): void
|
||||
{
|
||||
$this->logger()->testSuiteFinished();
|
||||
}
|
||||
}
|
||||
19
src/Logging/JUnit/Subscriber/TestSuiteStartedSubscriber.php
Normal file
19
src/Logging/JUnit/Subscriber/TestSuiteStartedSubscriber.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Logging\JUnit\Subscriber;
|
||||
|
||||
use PHPUnit\Event\TestSuite\Started;
|
||||
use PHPUnit\Event\TestSuite\StartedSubscriber;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class TestSuiteStartedSubscriber extends Subscriber implements StartedSubscriber
|
||||
{
|
||||
public function notify(Started $event): void
|
||||
{
|
||||
$this->logger()->testSuiteStarted($event);
|
||||
}
|
||||
}
|
||||
48
src/Subscribers/EnsureJunitEnabled.php
Normal file
48
src/Subscribers/EnsureJunitEnabled.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Subscribers;
|
||||
|
||||
use Pest\Logging\JUnit\JUnitLogger;
|
||||
use Pest\Logging\JUnit\Converter;
|
||||
use Pest\Support\Container;
|
||||
use Pest\TestSuite;
|
||||
use PHPUnit\Event\TestRunner\Configured;
|
||||
use PHPUnit\Event\TestRunner\ConfiguredSubscriber;
|
||||
use PHPUnit\TextUI\Configuration\Configuration;
|
||||
use PHPUnit\TextUI\Output\DefaultPrinter;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class EnsureJunitEnabled implements ConfiguredSubscriber
|
||||
{
|
||||
/**
|
||||
* Creates a new Configured Subscriber instance.
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly InputInterface $input,
|
||||
private readonly OutputInterface $output,
|
||||
private readonly TestSuite $testSuite,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the subscriber.
|
||||
*/
|
||||
public function notify(Configured $event): void
|
||||
{
|
||||
if (! $this->input->hasParameterOption('--log-junit')) {
|
||||
return;
|
||||
}
|
||||
|
||||
new JUnitLogger(
|
||||
DefaultPrinter::from(Container::getInstance()->get(Configuration::class)->logfileJunit()),
|
||||
$this->output,
|
||||
new Converter($this->testSuite->rootPath),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user