| Current Path : /var/www/html/reddsis/vendor/phpunit/phpunit/src/Logging/OpenTestReporting/ |
| Current File : /var/www/html/reddsis/vendor/phpunit/phpunit/src/Logging/OpenTestReporting/OtrXmlLogger.php |
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Logging\OpenTestReporting;
use const PHP_VERSION;
use const ZEND_THREAD_SAFE;
use function array_pop;
use function assert;
use function count;
use function error_get_last;
use function str_replace;
use DateTimeImmutable;
use DateTimeZone;
use PHPUnit\Event\Code\Test;
use PHPUnit\Event\Code\TestMethod;
use PHPUnit\Event\Code\Throwable;
use PHPUnit\Event\Facade;
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\MarkedIncomplete;
use PHPUnit\Event\Test\PreparationErrored;
use PHPUnit\Event\Test\PreparationFailed;
use PHPUnit\Event\Test\Prepared as TestStarted;
use PHPUnit\Event\Test\Skipped;
use PHPUnit\Event\TestSuite\Skipped as TestSuiteSkipped;
use PHPUnit\Event\TestSuite\Started as TestSuiteStarted;
use PHPUnit\Event\TestSuite\TestSuiteForTestClass;
use XMLWriter;
/**
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
*
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class OtrXmlLogger
{
private readonly XMLWriter $writer;
/**
* @var non-negative-int
*/
private int $idSequence = 0;
/**
* @var ?positive-int
*/
private ?int $parentId = null;
/**
* @var list<positive-int>
*/
private array $parentIdStack = [];
/**
* @var ?positive-int
*/
private ?int $testId = null;
private ?Throwable $parentErrored = null;
private ?Throwable $parentFailed = null;
private bool $alreadyFinished = false;
private bool $includeGitInformation;
/**
* @param non-empty-string $uri
*
* @throws CannotOpenUriForWritingException
*/
public function __construct(Facade $facade, string $uri, bool $includeGitInformation)
{
$this->writer = new XMLWriter;
if (@$this->writer->openUri($uri) === false) {
throw new CannotOpenUriForWritingException(
str_replace('XMLWriter::openUri(): ', '', error_get_last()['message']),
);
}
$this->writer->setIndent(true);
$this->registerSubscribers($facade);
$this->includeGitInformation = $includeGitInformation;
}
public function testRunnerStarted(): void
{
$infrastructure = new InfrastructureInformationProvider;
$gitInformation = false;
if ($this->includeGitInformation) {
$gitInformation = $infrastructure->gitInformation();
}
$this->writer->startDocument();
$this->writer->startElement('e:events');
$this->writer->writeAttribute('xmlns', 'https://schemas.opentest4j.org/reporting/core/0.2.0');
$this->writer->writeAttribute('xmlns:e', 'https://schemas.opentest4j.org/reporting/events/0.2.0');
if ($gitInformation !== false) {
$this->writer->writeAttribute('xmlns:git', 'https://schemas.opentest4j.org/reporting/git/0.2.0');
}
$this->writer->writeAttribute('xmlns:php', 'https://schema.phpunit.de/otr/php/0.0.1');
$this->writer->writeAttribute('xmlns:phpunit', 'https://schema.phpunit.de/otr/phpunit/0.0.1');
$this->writer->startElement('infrastructure');
$this->writer->writeElement('hostName', $infrastructure->hostName());
$this->writer->writeElement('userName', $infrastructure->userName());
$this->writer->writeElement('operatingSystem', $infrastructure->operatingSystem());
$this->writer->writeElement('php:phpVersion', PHP_VERSION);
$this->writer->writeElement('php:threadModel', ZEND_THREAD_SAFE ? 'ZTS' : 'NTS');
if ($gitInformation !== false) {
$this->writer->startElement('git:repository');
$this->writer->writeAttribute('originUrl', $gitInformation['originUrl']);
$this->writer->endElement();
$this->writer->writeElement('git:branch', $gitInformation['branch']);
$this->writer->writeElement('git:commit', $gitInformation['commit']);
$this->writer->startElement('git:status');
$this->writer->writeAttribute('clean', $gitInformation['clean'] === true ? 'true' : 'false');
$this->writer->writeCdata($gitInformation['status']);
$this->writer->endElement();
}
$this->writer->endElement();
$this->writer->flush();
}
public function testRunnerFinished(): void
{
$this->writer->endDocument();
$this->writer->flush();
}
public function testSuiteStarted(TestSuiteStarted $event): void
{
$id = $this->nextId();
$this->writer->startElement('e:started');
$this->writer->writeAttribute('id', (string) $id);
if ($this->parentId !== null) {
$this->writer->writeAttribute('parentId', (string) $this->parentId);
}
$testSuite = $event->testSuite();
$this->writer->writeAttribute('name', $testSuite->name());
$this->writer->writeAttribute('time', $this->timestamp());
if ($testSuite->isForTestClass()) {
assert($testSuite instanceof TestSuiteForTestClass);
$this->writer->startElement('sources');
$this->writer->startElement('fileSource');
$this->writer->writeAttribute('path', $testSuite->file());
$this->writer->startElement('filePosition');
$this->writer->writeAttribute('line', (string) $testSuite->line());
$this->writer->endElement();
$this->writer->endElement();
$this->writer->startElement('phpunit:classSource');
$this->writer->writeAttribute('className', $testSuite->className());
$this->writer->endElement();
$this->writer->endElement();
}
$this->writer->endElement();
$this->writer->flush();
$this->parentId = $id;
$this->parentIdStack[] = $id;
}
public function testSuiteSkipped(TestSuiteSkipped $event): void
{
$this->writer->startElement('e:finished');
$this->writer->writeAttribute('id', (string) $this->parentId);
$this->writer->writeAttribute('time', $this->timestamp());
$this->writer->startElement('result');
$this->writer->writeAttribute('status', 'SKIPPED');
$this->writer->writeElement('reason', $event->message());
$this->writer->endElement();
$this->writer->endElement();
$this->writer->flush();
$this->reduceTestSuiteLevel();
}
public function testSuiteFinished(): void
{
$this->writer->startElement('e:finished');
$this->writer->writeAttribute('id', (string) $this->parentId);
$this->writer->writeAttribute('time', $this->timestamp());
if ($this->parentErrored !== null) {
$this->writer->startElement('result');
$this->writer->writeAttribute('status', 'ERRORED');
$this->writer->writeElement('reason', $this->parentErrored->message());
$this->writeThrowable($this->parentErrored, false);
$this->writer->endElement();
} elseif ($this->parentFailed !== null) {
$this->writer->startElement('result');
$this->writer->writeAttribute('status', 'FAILED');
$this->writer->writeElement('reason', $this->parentFailed->message());
$this->writeThrowable($this->parentFailed, true);
$this->writer->endElement();
}
$this->writer->endElement();
$this->writer->flush();
$this->parentErrored = null;
$this->parentFailed = null;
$this->reduceTestSuiteLevel();
}
public function testPrepared(PreparationErrored|PreparationFailed|TestStarted $event): void
{
$this->testId = $this->nextId();
$this->writeTestStarted(
$event->test(),
$this->testId,
$this->parentId,
);
}
public function testFinished(): void
{
if (!$this->alreadyFinished) {
$this->writer->startElement('e:finished');
$this->writer->writeAttribute('id', (string) $this->testId);
$this->writer->writeAttribute('time', $this->timestamp());
$this->writer->startElement('result');
$this->writer->writeAttribute('status', Status::Successful->value);
$this->writer->endElement();
$this->writer->endElement();
}
$this->alreadyFinished = false;
$this->testId = null;
}
public function testFailed(Failed $event): void
{
$this->writer->startElement('e:finished');
$this->writer->writeAttribute('id', (string) $this->testId);
$this->writer->writeAttribute('time', $this->timestamp());
$this->writer->startElement('result');
$this->writer->writeAttribute('status', Status::Failed->value);
$this->writer->writeElement('reason', $event->throwable()->message());
$this->writeThrowable($event->throwable(), true);
$this->writer->endElement();
$this->writer->endElement();
$this->writer->flush();
$this->alreadyFinished = true;
}
public function testErrored(Errored $event): void
{
$this->writer->startElement('e:finished');
$this->writer->writeAttribute('id', (string) $this->testId);
$this->writer->writeAttribute('time', $this->timestamp());
$this->writer->startElement('result');
$this->writer->writeAttribute('status', Status::Errored->value);
$this->writer->writeElement('reason', $event->throwable()->message());
$this->writeThrowable($event->throwable(), false);
$this->writer->endElement();
$this->writer->endElement();
$this->writer->flush();
$this->alreadyFinished = true;
}
public function testSkipped(Skipped $event): void
{
if ($this->testId === null) {
$this->testId = $this->nextId();
$this->writeTestStarted(
$event->test(),
$this->testId,
$this->parentId,
);
}
$this->writer->startElement('e:finished');
$this->writer->writeAttribute('id', (string) $this->testId);
$this->writer->writeAttribute('time', $this->timestamp());
$this->writer->startElement('result');
$this->writer->writeAttribute('status', Status::Skipped->value);
$this->writer->writeElement('reason', $event->message());
$this->writer->endElement();
$this->writer->endElement();
$this->writer->flush();
$this->alreadyFinished = true;
}
public function markTestIncomplete(MarkedIncomplete $event): void
{
$this->writer->startElement('e:finished');
$this->writer->writeAttribute('id', (string) $this->testId);
$this->writer->writeAttribute('time', $this->timestamp());
$this->writer->startElement('result');
$this->writer->writeAttribute('status', Status::Aborted->value);
$this->writer->writeElement('reason', $event->throwable()->message());
$this->writeThrowable($event->throwable(), false);
$this->writer->endElement();
$this->writer->endElement();
$this->writer->flush();
$this->alreadyFinished = true;
}
public function parentErrored(AfterLastTestMethodErrored|BeforeFirstTestMethodErrored $event): void
{
$this->parentErrored = $event->throwable();
}
public function parentFailed(AfterLastTestMethodFailed|BeforeFirstTestMethodFailed $event): void
{
$this->parentFailed = $event->throwable();
}
private function registerSubscribers(Facade $facade): void
{
$facade->registerSubscribers(
new TestRunnerStartedSubscriber($this),
new TestSuiteStartedSubscriber($this),
new TestSuiteSkippedSubscriber($this),
new BeforeFirstTestMethodErroredSubscriber($this),
new BeforeFirstTestMethodFailedSubscriber($this),
new AfterLastTestMethodErroredSubscriber($this),
new AfterLastTestMethodFailedSubscriber($this),
new TestPreparationErroredSubscriber($this),
new TestPreparationFailedSubscriber($this),
new TestPreparedSubscriber($this),
new TestAbortedSubscriber($this),
new TestErroredSubscriber($this),
new TestFailedSubscriber($this),
new TestSkippedSubscriber($this),
new TestFinishedSubscriber($this),
new TestSuiteFinishedSubscriber($this),
new TestRunnerFinishedSubscriber($this),
);
}
/**
* @param positive-int $id
* @param ?positive-int $parentId
*/
private function writeTestStarted(Test $test, int $id, ?int $parentId): void
{
$this->writer->startElement('e:started');
$this->writer->writeAttribute('id', (string) $id);
if ($parentId !== null) {
$this->writer->writeAttribute('parentId', (string) $parentId);
}
$this->writer->writeAttribute('name', $test->name());
$this->writer->writeAttribute('time', $this->timestamp());
$this->writer->startElement('sources');
$this->writer->startElement('fileSource');
$this->writer->writeAttribute('path', $test->file());
if ($test->isTestMethod()) {
assert($test instanceof TestMethod);
$this->writer->startElement('filePosition');
$this->writer->writeAttribute('line', (string) $test->line());
$this->writer->endElement();
}
$this->writer->endElement();
if ($test->isTestMethod()) {
assert($test instanceof TestMethod);
$this->writer->startElement('phpunit:methodSource');
$this->writer->writeAttribute('className', $test->className());
$this->writer->writeAttribute('methodName', $test->methodName());
$this->writer->endElement();
}
$this->writer->endElement();
$this->writer->endElement();
$this->writer->flush();
}
private function writeThrowable(Throwable $throwable, bool $assertionError): void
{
$this->writer->startElement('phpunit:throwable');
$this->writer->writeAttribute('type', $throwable->className());
$this->writer->writeAttribute('assertionError', $assertionError ? 'true' : 'false');
$this->writer->writeCdata($throwable->asString());
$this->writer->endElement();
}
/**
* @return non-empty-string
*/
private function timestamp(): string
{
return (new DateTimeImmutable('now', new DateTimeZone('UTC')))->format('Y-m-d\TH:i:s.u\Z');
}
/**
* @return positive-int
*/
private function nextId(): int
{
return ++$this->idSequence;
}
private function reduceTestSuiteLevel(): void
{
array_pop($this->parentIdStack);
if ($this->parentIdStack !== []) {
$this->parentId = $this->parentIdStack[count($this->parentIdStack) - 1];
return;
}
$this->parentId = null;
}
}