summaryrefslogtreecommitdiff
path: root/vendor/phpunit/phpunit/src/Util/Log/TeamCity.php
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/phpunit/phpunit/src/Util/Log/TeamCity.php')
-rw-r--r--vendor/phpunit/phpunit/src/Util/Log/TeamCity.php383
1 files changed, 383 insertions, 0 deletions
diff --git a/vendor/phpunit/phpunit/src/Util/Log/TeamCity.php b/vendor/phpunit/phpunit/src/Util/Log/TeamCity.php
new file mode 100644
index 000000000..f800ba72d
--- /dev/null
+++ b/vendor/phpunit/phpunit/src/Util/Log/TeamCity.php
@@ -0,0 +1,383 @@
+<?php declare(strict_types=1);
+/*
+ * This file is part of PHPUnit.
+ *
+ * (c) Sebastian Bergmann <[email protected]>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+namespace PHPUnit\Util\Log;
+
+use function class_exists;
+use function count;
+use function explode;
+use function get_class;
+use function getmypid;
+use function ini_get;
+use function is_bool;
+use function is_scalar;
+use function method_exists;
+use function print_r;
+use function round;
+use function str_replace;
+use function stripos;
+use PHPUnit\Framework\AssertionFailedError;
+use PHPUnit\Framework\ExceptionWrapper;
+use PHPUnit\Framework\ExpectationFailedException;
+use PHPUnit\Framework\Test;
+use PHPUnit\Framework\TestCase;
+use PHPUnit\Framework\TestFailure;
+use PHPUnit\Framework\TestResult;
+use PHPUnit\Framework\TestSuite;
+use PHPUnit\Framework\Warning;
+use PHPUnit\TextUI\DefaultResultPrinter;
+use PHPUnit\Util\Exception;
+use PHPUnit\Util\Filter;
+use ReflectionClass;
+use ReflectionException;
+use SebastianBergmann\Comparator\ComparisonFailure;
+use Throwable;
+
+/**
+ * @internal This class is not covered by the backward compatibility promise for PHPUnit
+ */
+final class TeamCity extends DefaultResultPrinter
+{
+ /**
+ * @var bool
+ */
+ private $isSummaryTestCountPrinted = false;
+
+ /**
+ * @var string
+ */
+ private $startedTestName;
+
+ /**
+ * @var false|int
+ */
+ private $flowId;
+
+ public function printResult(TestResult $result): void
+ {
+ $this->printHeader($result);
+ $this->printFooter($result);
+ }
+
+ /**
+ * An error occurred.
+ */
+ public function addError(Test $test, Throwable $t, float $time): void
+ {
+ $this->printEvent(
+ 'testFailed',
+ [
+ 'name' => $test->getName(),
+ 'message' => self::getMessage($t),
+ 'details' => self::getDetails($t),
+ 'duration' => self::toMilliseconds($time),
+ ]
+ );
+ }
+
+ /**
+ * A warning occurred.
+ */
+ public function addWarning(Test $test, Warning $e, float $time): void
+ {
+ $this->write(self::getMessage($e) . PHP_EOL);
+ }
+
+ /**
+ * A failure occurred.
+ */
+ public function addFailure(Test $test, AssertionFailedError $e, float $time): void
+ {
+ $parameters = [
+ 'name' => $test->getName(),
+ 'message' => self::getMessage($e),
+ 'details' => self::getDetails($e),
+ 'duration' => self::toMilliseconds($time),
+ ];
+
+ if ($e instanceof ExpectationFailedException) {
+ $comparisonFailure = $e->getComparisonFailure();
+
+ if ($comparisonFailure instanceof ComparisonFailure) {
+ $expectedString = $comparisonFailure->getExpectedAsString();
+
+ if ($expectedString === null || empty($expectedString)) {
+ $expectedString = self::getPrimitiveValueAsString($comparisonFailure->getExpected());
+ }
+
+ $actualString = $comparisonFailure->getActualAsString();
+
+ if ($actualString === null || empty($actualString)) {
+ $actualString = self::getPrimitiveValueAsString($comparisonFailure->getActual());
+ }
+
+ if ($actualString !== null && $expectedString !== null) {
+ $parameters['type'] = 'comparisonFailure';
+ $parameters['actual'] = $actualString;
+ $parameters['expected'] = $expectedString;
+ }
+ }
+ }
+
+ $this->printEvent('testFailed', $parameters);
+ }
+
+ /**
+ * Incomplete test.
+ */
+ public function addIncompleteTest(Test $test, Throwable $t, float $time): void
+ {
+ $this->printIgnoredTest($test->getName(), $t, $time);
+ }
+
+ /**
+ * Risky test.
+ */
+ public function addRiskyTest(Test $test, Throwable $t, float $time): void
+ {
+ $this->addError($test, $t, $time);
+ }
+
+ /**
+ * Skipped test.
+ */
+ public function addSkippedTest(Test $test, Throwable $t, float $time): void
+ {
+ $testName = $test->getName();
+
+ if ($this->startedTestName !== $testName) {
+ $this->startTest($test);
+ $this->printIgnoredTest($testName, $t, $time);
+ $this->endTest($test, $time);
+ } else {
+ $this->printIgnoredTest($testName, $t, $time);
+ }
+ }
+
+ public function printIgnoredTest(string $testName, Throwable $t, float $time): void
+ {
+ $this->printEvent(
+ 'testIgnored',
+ [
+ 'name' => $testName,
+ 'message' => self::getMessage($t),
+ 'details' => self::getDetails($t),
+ 'duration' => self::toMilliseconds($time),
+ ]
+ );
+ }
+
+ /**
+ * A testsuite started.
+ */
+ public function startTestSuite(TestSuite $suite): void
+ {
+ if (stripos(ini_get('disable_functions'), 'getmypid') === false) {
+ $this->flowId = getmypid();
+ } else {
+ $this->flowId = false;
+ }
+
+ if (!$this->isSummaryTestCountPrinted) {
+ $this->isSummaryTestCountPrinted = true;
+
+ $this->printEvent(
+ 'testCount',
+ ['count' => count($suite)]
+ );
+ }
+
+ $suiteName = $suite->getName();
+
+ if (empty($suiteName)) {
+ return;
+ }
+
+ $parameters = ['name' => $suiteName];
+
+ if (class_exists($suiteName, false)) {
+ $fileName = self::getFileName($suiteName);
+ $parameters['locationHint'] = "php_qn://{$fileName}::\\{$suiteName}";
+ } else {
+ $split = explode('::', $suiteName);
+
+ if (count($split) === 2 && class_exists($split[0]) && method_exists($split[0], $split[1])) {
+ $fileName = self::getFileName($split[0]);
+ $parameters['locationHint'] = "php_qn://{$fileName}::\\{$suiteName}";
+ $parameters['name'] = $split[1];
+ }
+ }
+
+ $this->printEvent('testSuiteStarted', $parameters);
+ }
+
+ /**
+ * A testsuite ended.
+ */
+ public function endTestSuite(TestSuite $suite): void
+ {
+ $suiteName = $suite->getName();
+
+ if (empty($suiteName)) {
+ return;
+ }
+
+ $parameters = ['name' => $suiteName];
+
+ if (!class_exists($suiteName, false)) {
+ $split = explode('::', $suiteName);
+
+ if (count($split) === 2 && class_exists($split[0]) && method_exists($split[0], $split[1])) {
+ $parameters['name'] = $split[1];
+ }
+ }
+
+ $this->printEvent('testSuiteFinished', $parameters);
+ }
+
+ /**
+ * A test started.
+ */
+ public function startTest(Test $test): void
+ {
+ $testName = $test->getName();
+ $this->startedTestName = $testName;
+ $params = ['name' => $testName];
+
+ if ($test instanceof TestCase) {
+ $className = get_class($test);
+ $fileName = self::getFileName($className);
+ $params['locationHint'] = "php_qn://{$fileName}::\\{$className}::{$testName}";
+ }
+
+ $this->printEvent('testStarted', $params);
+ }
+
+ /**
+ * A test ended.
+ */
+ public function endTest(Test $test, float $time): void
+ {
+ parent::endTest($test, $time);
+
+ $this->printEvent(
+ 'testFinished',
+ [
+ 'name' => $test->getName(),
+ 'duration' => self::toMilliseconds($time),
+ ]
+ );
+ }
+
+ protected function writeProgress(string $progress): void
+ {
+ }
+
+ private function printEvent(string $eventName, array $params = []): void
+ {
+ $this->write("\n##teamcity[{$eventName}");
+
+ if ($this->flowId) {
+ $params['flowId'] = $this->flowId;
+ }
+
+ foreach ($params as $key => $value) {
+ $escapedValue = self::escapeValue((string) $value);
+ $this->write(" {$key}='{$escapedValue}'");
+ }
+
+ $this->write("]\n");
+ }
+
+ private static function getMessage(Throwable $t): string
+ {
+ $message = '';
+
+ if ($t instanceof ExceptionWrapper) {
+ if ($t->getClassName() !== '') {
+ $message .= $t->getClassName();
+ }
+
+ if ($message !== '' && $t->getMessage() !== '') {
+ $message .= ' : ';
+ }
+ }
+
+ return $message . $t->getMessage();
+ }
+
+ private static function getDetails(Throwable $t): string
+ {
+ $stackTrace = Filter::getFilteredStacktrace($t);
+ $previous = $t instanceof ExceptionWrapper ? $t->getPreviousWrapped() : $t->getPrevious();
+
+ while ($previous) {
+ $stackTrace .= "\nCaused by\n" .
+ TestFailure::exceptionToString($previous) . "\n" .
+ Filter::getFilteredStacktrace($previous);
+
+ $previous = $previous instanceof ExceptionWrapper ?
+ $previous->getPreviousWrapped() : $previous->getPrevious();
+ }
+
+ return ' ' . str_replace("\n", "\n ", $stackTrace);
+ }
+
+ private static function getPrimitiveValueAsString($value): ?string
+ {
+ if ($value === null) {
+ return 'null';
+ }
+
+ if (is_bool($value)) {
+ return $value ? 'true' : 'false';
+ }
+
+ if (is_scalar($value)) {
+ return print_r($value, true);
+ }
+
+ return null;
+ }
+
+ private static function escapeValue(string $text): string
+ {
+ return str_replace(
+ ['|', "'", "\n", "\r", ']', '['],
+ ['||', "|'", '|n', '|r', '|]', '|['],
+ $text
+ );
+ }
+
+ /**
+ * @param string $className
+ */
+ private static function getFileName($className): string
+ {
+ try {
+ return (new ReflectionClass($className))->getFileName();
+ // @codeCoverageIgnoreStart
+ } catch (ReflectionException $e) {
+ throw new Exception(
+ $e->getMessage(),
+ (int) $e->getCode(),
+ $e
+ );
+ }
+ // @codeCoverageIgnoreEnd
+ }
+
+ /**
+ * @param float $time microseconds
+ */
+ private static function toMilliseconds(float $time): int
+ {
+ return (int) round($time * 1000);
+ }
+}