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

Breadcrumb

  1. Drupal Core 11.1.x

TeamCityLogger.php

Namespace

PHPUnit\Logging\TeamCity

File

vendor/phpunit/phpunit/src/Logging/TeamCity/TeamCityLogger.php

View source
<?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\TeamCity;

use function assert;
use function getmypid;
use function ini_get;
use function is_a;
use function round;
use function sprintf;
use function str_replace;
use function stripos;
use PHPUnit\Event\Code\TestMethod;
use PHPUnit\Event\Code\Throwable;
use PHPUnit\Event\Event;
use PHPUnit\Event\EventFacadeIsSealedException;
use PHPUnit\Event\Facade;
use PHPUnit\Event\InvalidArgumentException;
use PHPUnit\Event\Telemetry\HRTime;
use PHPUnit\Event\Test\ConsideredRisky;
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\TestSuite\Finished as TestSuiteFinished;
use PHPUnit\Event\TestSuite\Started as TestSuiteStarted;
use PHPUnit\Event\TestSuite\TestSuiteForTestClass;
use PHPUnit\Event\TestSuite\TestSuiteForTestMethodWithDataProvider;
use PHPUnit\Event\UnknownSubscriberTypeException;
use PHPUnit\Framework\Exception as FrameworkException;
use PHPUnit\TextUI\Output\Printer;

/**
 * @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 TeamCityLogger {
    private readonly Printer $printer;
    private bool $isSummaryTestCountPrinted = false;
    private ?HRTime $time = null;
    private ?int $flowId;
    
    /**
     * @throws EventFacadeIsSealedException
     * @throws UnknownSubscriberTypeException
     */
    public function __construct(Printer $printer, Facade $facade) {
        $this->printer = $printer;
        $this->registerSubscribers($facade);
        $this->setFlowId();
    }
    public function testSuiteStarted(TestSuiteStarted $event) : void {
        $testSuite = $event->testSuite();
        if (!$this->isSummaryTestCountPrinted) {
            $this->isSummaryTestCountPrinted = true;
            $this->writeMessage('testCount', [
                'count' => $testSuite->count(),
            ]);
        }
        $parameters = [
            'name' => $testSuite->name(),
        ];
        if ($testSuite->isForTestClass()) {
            assert($testSuite instanceof TestSuiteForTestClass);
            $parameters['locationHint'] = sprintf('php_qn://%s::\\%s', $testSuite->file(), $testSuite->name());
        }
        elseif ($testSuite->isForTestMethodWithDataProvider()) {
            assert($testSuite instanceof TestSuiteForTestMethodWithDataProvider);
            $parameters['locationHint'] = sprintf('php_qn://%s::\\%s', $testSuite->file(), $testSuite->name());
            $parameters['name'] = $testSuite->methodName();
        }
        $this->writeMessage('testSuiteStarted', $parameters);
    }
    public function testSuiteFinished(TestSuiteFinished $event) : void {
        $testSuite = $event->testSuite();
        $parameters = [
            'name' => $testSuite->name(),
        ];
        if ($testSuite->isForTestMethodWithDataProvider()) {
            assert($testSuite instanceof TestSuiteForTestMethodWithDataProvider);
            $parameters['name'] = $testSuite->methodName();
        }
        $this->writeMessage('testSuiteFinished', $parameters);
    }
    public function testPrepared(Prepared $event) : void {
        $test = $event->test();
        $parameters = [
            'name' => $test->name(),
        ];
        if ($test->isTestMethod()) {
            assert($test instanceof TestMethod);
            $parameters['locationHint'] = sprintf('php_qn://%s::\\%s::%s', $test->file(), $test->className(), $test->name());
        }
        $this->writeMessage('testStarted', $parameters);
        $this->time = $event->telemetryInfo()
            ->time();
    }
    
    /**
     * @throws InvalidArgumentException
     */
    public function testMarkedIncomplete(MarkedIncomplete $event) : void {
        if ($this->time === null) {
            $this->time = $event->telemetryInfo()
                ->time();
        }
        $this->writeMessage('testIgnored', [
            'name' => $event->test()
                ->name(),
            'message' => $event->throwable()
                ->message(),
            'details' => $this->details($event->throwable()),
            'duration' => $this->duration($event),
        ]);
    }
    
    /**
     * @throws InvalidArgumentException
     */
    public function testSkipped(Skipped $event) : void {
        if ($this->time === null) {
            $this->time = $event->telemetryInfo()
                ->time();
        }
        $parameters = [
            'name' => $event->test()
                ->name(),
            'message' => $event->message(),
        ];
        $parameters['duration'] = $this->duration($event);
        $this->writeMessage('testIgnored', $parameters);
    }
    
    /**
     * @throws InvalidArgumentException
     */
    public function testErrored(Errored $event) : void {
        if ($this->time === null) {
            $this->time = $event->telemetryInfo()
                ->time();
        }
        $this->writeMessage('testFailed', [
            'name' => $event->test()
                ->name(),
            'message' => $this->message($event->throwable()),
            'details' => $this->details($event->throwable()),
            'duration' => $this->duration($event),
        ]);
    }
    
    /**
     * @throws InvalidArgumentException
     */
    public function testFailed(Failed $event) : void {
        if ($this->time === null) {
            $this->time = $event->telemetryInfo()
                ->time();
        }
        $parameters = [
            'name' => $event->test()
                ->name(),
            'message' => $this->message($event->throwable()),
            'details' => $this->details($event->throwable()),
            'duration' => $this->duration($event),
        ];
        if ($event->hasComparisonFailure()) {
            $parameters['type'] = 'comparisonFailure';
            $parameters['actual'] = $event->comparisonFailure()
                ->actual();
            $parameters['expected'] = $event->comparisonFailure()
                ->expected();
        }
        $this->writeMessage('testFailed', $parameters);
    }
    
    /**
     * @throws InvalidArgumentException
     */
    public function testConsideredRisky(ConsideredRisky $event) : void {
        if ($this->time === null) {
            $this->time = $event->telemetryInfo()
                ->time();
        }
        $this->writeMessage('testFailed', [
            'name' => $event->test()
                ->name(),
            'message' => $event->message(),
            'details' => '',
            'duration' => $this->duration($event),
        ]);
    }
    
    /**
     * @throws InvalidArgumentException
     */
    public function testFinished(Finished $event) : void {
        $this->writeMessage('testFinished', [
            'name' => $event->test()
                ->name(),
            'duration' => $this->duration($event),
        ]);
        $this->time = null;
    }
    public function flush() : void {
        $this->printer
            ->flush();
    }
    
    /**
     * @throws EventFacadeIsSealedException
     * @throws UnknownSubscriberTypeException
     */
    private function registerSubscribers(Facade $facade) : void {
        $facade->registerSubscribers(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 TestConsideredRiskySubscriber($this), new TestRunnerExecutionFinishedSubscriber($this));
    }
    private function setFlowId() : void {
        if (stripos(ini_get('disable_functions'), 'getmypid') === false) {
            $this->flowId = getmypid();
        }
    }
    private function writeMessage(string $eventName, array $parameters = []) : void {
        $this->printer
            ->print(sprintf('##teamcity[%s', $eventName));
        if ($this->flowId !== null) {
            $parameters['flowId'] = $this->flowId;
        }
        foreach ($parameters as $key => $value) {
            $this->printer
                ->print(sprintf(" %s='%s'", $key, $this->escape((string) $value)));
        }
        $this->printer
            ->print("]\n");
    }
    
    /**
     * @throws InvalidArgumentException
     */
    private function duration(Event $event) : int {
        if ($this->time === null) {
            return 0;
        }
        return (int) round($event->telemetryInfo()
            ->time()
            ->duration($this->time)
            ->asFloat() * 1000);
    }
    private function escape(string $string) : string {
        return str_replace([
            '|',
            "'",
            "\n",
            "\r",
            ']',
            '[',
        ], [
            '||',
            "|'",
            '|n',
            '|r',
            '|]',
            '|[',
        ], $string);
    }
    private function message(Throwable $throwable) : string {
        if (is_a($throwable->className(), FrameworkException::class, true)) {
            return $throwable->message();
        }
        $buffer = $throwable->className();
        if (!empty($throwable->message())) {
            $buffer .= ': ' . $throwable->message();
        }
        return $buffer;
    }
    private function details(Throwable $throwable) : string {
        $buffer = $throwable->stackTrace();
        while ($throwable->hasPrevious()) {
            $throwable = $throwable->previous();
            $buffer .= sprintf("\nCaused by\n%s\n%s", $throwable->description(), $throwable->stackTrace());
        }
        return $buffer;
    }

}

Classes

Title Deprecated Summary
TeamCityLogger @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
RSS feed
Powered by Drupal