Skip to main content
Drupal API
User account menu
  • Log in

Breadcrumb

  1. Drupal Core 11.1.x
  2. JunitXmlLogger.php

class JunitXmlLogger

@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

Hierarchy

  • class \PHPUnit\Logging\JUnit\JunitXmlLogger

Expanded class hierarchy of JunitXmlLogger

1 file declares its use of JunitXmlLogger
Application.php in vendor/phpunit/phpunit/src/TextUI/Application.php

File

vendor/phpunit/phpunit/src/Logging/JUnit/JunitXmlLogger.php, line 45

Namespace

PHPUnit\Logging\JUnit
View source
final class JunitXmlLogger {
    private readonly Printer $printer;
    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;
    private bool $preparationFailed = false;
    
    /**
     * @throws EventFacadeIsSealedException
     * @throws UnknownSubscriberTypeException
     */
    public function __construct(Printer $printer, Facade $facade) {
        $this->printer = $printer;
        $this->registerSubscribers($facade);
        $this->createDocument();
    }
    public function flush() : void {
        $this->printer
            ->print($this->document
            ->saveXML());
        $this->printer
            ->flush();
    }
    public function testSuiteStarted(Started $event) : void {
        $testSuite = $this->document
            ->createElement('testsuite');
        $testSuite->setAttribute('name', $event->testSuite()
            ->name());
        if ($event->testSuite()
            ->isForTestClass()) {
            $testSuite->setAttribute('file', $event->testSuite()
                ->file());
        }
        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
     */
    public function testPreparationStarted(PreparationStarted $event) : void {
        $this->createTestCase($event);
    }
    
    /**
     * @throws InvalidArgumentException
     */
    public function testPreparationFailed() : void {
        $this->preparationFailed = true;
    }
    
    /**
     * @throws InvalidArgumentException
     */
    public function testPrepared() : void {
        $this->prepared = true;
    }
    
    /**
     * @throws InvalidArgumentException
     */
    public function testFinished(Finished $event) : void {
        if (!$this->prepared || $this->preparationFailed) {
            return;
        }
        $this->handleFinish($event->telemetryInfo(), $event->numberOfAssertionsPerformed());
    }
    
    /**
     * @throws InvalidArgumentException
     */
    public function testMarkedIncomplete(MarkedIncomplete $event) : void {
        $this->handleIncompleteOrSkipped($event);
    }
    
    /**
     * @throws InvalidArgumentException
     */
    public function testSkipped(Skipped $event) : void {
        $this->handleIncompleteOrSkipped($event);
    }
    
    /**
     * @throws InvalidArgumentException
     */
    public function testErrored(Errored $event) : void {
        $this->handleFault($event, 'error');
        $this->testSuiteErrors[$this->testSuiteLevel]++;
    }
    
    /**
     * @throws InvalidArgumentException
     */
    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(Facade $facade) : void {
        $facade->registerSubscribers(new TestSuiteStartedSubscriber($this), new TestSuiteFinishedSubscriber($this), new TestPreparationStartedSubscriber($this), new TestPreparationFailedSubscriber($this), new TestPreparedSubscriber($this), new TestFinishedSubscriber($this), new TestErroredSubscriber($this), new TestFailedSubscriber($this), new TestMarkedIncompleteSubscriber($this), new TestSkippedSubscriber($this), new TestRunnerExecutionFinishedSubscriber($this));
    }
    private function createDocument() : void {
        $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
     */
    private function handleFault(Errored|Failed $event, string $type) : void {
        if (!$this->prepared) {
            $this->createTestCase($event);
        }
        assert($this->currentTestCase !== null);
        $buffer = $this->testAsString($event->test());
        $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
     */
    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
     */
    private function testAsString(Test $test) : string {
        if ($test->isPhpt()) {
            return basename($test->file());
        }
        assert($test instanceof TestMethod);
        return sprintf('%s::%s%s', $test->className(), $this->name($test), PHP_EOL);
    }
    
    /**
     * @throws InvalidArgumentException
     */
    private function name(Test $test) : string {
        if ($test->isPhpt()) {
            return basename($test->file());
        }
        assert($test instanceof TestMethod);
        if (!$test->testData()
            ->hasDataFromDataProvider()) {
            return $test->methodName();
        }
        $dataSetName = $test->testData()
            ->dataFromDataProvider()
            ->dataSetName();
        if (is_int($dataSetName)) {
            return sprintf('%s with data set #%d', $test->methodName(), $dataSetName);
        }
        return sprintf('%s with data set "%s"', $test->methodName(), $dataSetName);
    }
    
    /**
     * @throws InvalidArgumentException
     *
     * @psalm-assert !null $this->currentTestCase
     */
    private function createTestCase(Errored|Failed|MarkedIncomplete|PreparationStarted|Prepared|Skipped $event) : void {
        $testCase = $this->document
            ->createElement('testcase');
        $test = $event->test();
        $testCase->setAttribute('name', $this->name($test));
        $testCase->setAttribute('file', $test->file());
        if ($test->isTestMethod()) {
            assert($test instanceof TestMethod);
            $testCase->setAttribute('line', (string) $test->line());
            $testCase->setAttribute('class', $test->className());
            $testCase->setAttribute('classname', str_replace('\\', '.', $test->className()));
        }
        $this->currentTestCase = $testCase;
        $this->time = $event->telemetryInfo()
            ->time();
    }

}

Members

Title Sort descending Modifiers Object type Summary
JunitXmlLogger::$currentTestCase private property
JunitXmlLogger::$document private property
JunitXmlLogger::$preparationFailed private property
JunitXmlLogger::$prepared private property
JunitXmlLogger::$printer private property
JunitXmlLogger::$root private property
JunitXmlLogger::$testSuiteAssertions private property @psalm-var array&lt;int,int&gt;
JunitXmlLogger::$testSuiteErrors private property @psalm-var array&lt;int,int&gt;
JunitXmlLogger::$testSuiteFailures private property @psalm-var array&lt;int,int&gt;
JunitXmlLogger::$testSuiteLevel private property
JunitXmlLogger::$testSuites private property
JunitXmlLogger::$testSuiteSkipped private property @psalm-var array&lt;int,int&gt;
JunitXmlLogger::$testSuiteTests private property @psalm-var array&lt;int,int&gt;
JunitXmlLogger::$testSuiteTimes private property @psalm-var array&lt;int,int&gt;
JunitXmlLogger::$time private property
JunitXmlLogger::createDocument private function
JunitXmlLogger::createTestCase private function @psalm-assert !null $this-&gt;currentTestCase
JunitXmlLogger::flush public function
JunitXmlLogger::handleFault private function
JunitXmlLogger::handleFinish private function
JunitXmlLogger::handleIncompleteOrSkipped private function
JunitXmlLogger::name private function
JunitXmlLogger::registerSubscribers private function
JunitXmlLogger::testAsString private function
JunitXmlLogger::testErrored public function
JunitXmlLogger::testFailed public function
JunitXmlLogger::testFinished public function
JunitXmlLogger::testMarkedIncomplete public function
JunitXmlLogger::testPreparationFailed public function
JunitXmlLogger::testPreparationStarted public function
JunitXmlLogger::testPrepared public function
JunitXmlLogger::testSkipped public function
JunitXmlLogger::testSuiteFinished public function
JunitXmlLogger::testSuiteStarted public function
JunitXmlLogger::__construct public function

API Navigation

  • Drupal Core 11.1.x
  • Topics
  • Classes
  • Functions
  • Constants
  • Globals
  • Files
  • Namespaces
  • Deprecated
  • Services
RSS feed
Powered by Drupal