You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
262 lines
7.1 KiB
262 lines
7.1 KiB
<?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\Util\TestDox;
|
|
|
|
use function array_filter;
|
|
use function get_class;
|
|
use function implode;
|
|
use function strpos;
|
|
use DOMDocument;
|
|
use DOMElement;
|
|
use PHPUnit\Framework\AssertionFailedError;
|
|
use PHPUnit\Framework\Exception;
|
|
use PHPUnit\Framework\Test;
|
|
use PHPUnit\Framework\TestCase;
|
|
use PHPUnit\Framework\TestListener;
|
|
use PHPUnit\Framework\TestSuite;
|
|
use PHPUnit\Framework\Warning;
|
|
use PHPUnit\Framework\WarningTestCase;
|
|
use PHPUnit\Util\Printer;
|
|
use PHPUnit\Util\Test as TestUtil;
|
|
use ReflectionClass;
|
|
use ReflectionException;
|
|
use Throwable;
|
|
|
|
/**
|
|
* @internal This class is not covered by the backward compatibility promise for PHPUnit
|
|
*/
|
|
final class XmlResultPrinter extends Printer implements TestListener
|
|
{
|
|
/**
|
|
* @var DOMDocument
|
|
*/
|
|
private $document;
|
|
|
|
/**
|
|
* @var DOMElement
|
|
*/
|
|
private $root;
|
|
|
|
/**
|
|
* @var NamePrettifier
|
|
*/
|
|
private $prettifier;
|
|
|
|
/**
|
|
* @var null|Throwable
|
|
*/
|
|
private $exception;
|
|
|
|
/**
|
|
* @param resource|string $out
|
|
*
|
|
* @throws Exception
|
|
*/
|
|
public function __construct($out = null)
|
|
{
|
|
$this->document = new DOMDocument('1.0', 'UTF-8');
|
|
$this->document->formatOutput = true;
|
|
|
|
$this->root = $this->document->createElement('tests');
|
|
$this->document->appendChild($this->root);
|
|
|
|
$this->prettifier = new NamePrettifier;
|
|
|
|
parent::__construct($out);
|
|
}
|
|
|
|
/**
|
|
* Flush buffer and close output.
|
|
*/
|
|
public function flush(): void
|
|
{
|
|
$this->write($this->document->saveXML());
|
|
|
|
parent::flush();
|
|
}
|
|
|
|
/**
|
|
* An error occurred.
|
|
*/
|
|
public function addError(Test $test, Throwable $t, float $time): void
|
|
{
|
|
$this->exception = $t;
|
|
}
|
|
|
|
/**
|
|
* A warning occurred.
|
|
*/
|
|
public function addWarning(Test $test, Warning $e, float $time): void
|
|
{
|
|
}
|
|
|
|
/**
|
|
* A failure occurred.
|
|
*/
|
|
public function addFailure(Test $test, AssertionFailedError $e, float $time): void
|
|
{
|
|
$this->exception = $e;
|
|
}
|
|
|
|
/**
|
|
* Incomplete test.
|
|
*/
|
|
public function addIncompleteTest(Test $test, Throwable $t, float $time): void
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Risky test.
|
|
*/
|
|
public function addRiskyTest(Test $test, Throwable $t, float $time): void
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Skipped test.
|
|
*/
|
|
public function addSkippedTest(Test $test, Throwable $t, float $time): void
|
|
{
|
|
}
|
|
|
|
/**
|
|
* A test suite started.
|
|
*/
|
|
public function startTestSuite(TestSuite $suite): void
|
|
{
|
|
}
|
|
|
|
/**
|
|
* A test suite ended.
|
|
*/
|
|
public function endTestSuite(TestSuite $suite): void
|
|
{
|
|
}
|
|
|
|
/**
|
|
* A test started.
|
|
*/
|
|
public function startTest(Test $test): void
|
|
{
|
|
$this->exception = null;
|
|
}
|
|
|
|
/**
|
|
* A test ended.
|
|
*
|
|
* @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
|
|
*/
|
|
public function endTest(Test $test, float $time): void
|
|
{
|
|
if (!$test instanceof TestCase || $test instanceof WarningTestCase) {
|
|
return;
|
|
}
|
|
|
|
$groups = array_filter(
|
|
$test->getGroups(),
|
|
static function ($group)
|
|
{
|
|
return !($group === 'small' || $group === 'medium' || $group === 'large' || strpos($group, '__phpunit_') === 0);
|
|
},
|
|
);
|
|
|
|
$testNode = $this->document->createElement('test');
|
|
|
|
$testNode->setAttribute('className', get_class($test));
|
|
$testNode->setAttribute('methodName', $test->getName());
|
|
$testNode->setAttribute('prettifiedClassName', $this->prettifier->prettifyTestClass(get_class($test)));
|
|
$testNode->setAttribute('prettifiedMethodName', $this->prettifier->prettifyTestCase($test));
|
|
$testNode->setAttribute('status', (string) $test->getStatus());
|
|
$testNode->setAttribute('time', (string) $time);
|
|
$testNode->setAttribute('size', (string) $test->getSize());
|
|
$testNode->setAttribute('groups', implode(',', $groups));
|
|
|
|
foreach ($groups as $group) {
|
|
$groupNode = $this->document->createElement('group');
|
|
|
|
$groupNode->setAttribute('name', $group);
|
|
|
|
$testNode->appendChild($groupNode);
|
|
}
|
|
|
|
$annotations = TestUtil::parseTestMethodAnnotations(
|
|
get_class($test),
|
|
$test->getName(false),
|
|
);
|
|
|
|
foreach (['class', 'method'] as $type) {
|
|
foreach ($annotations[$type] as $annotation => $values) {
|
|
if ($annotation !== 'covers' && $annotation !== 'uses') {
|
|
continue;
|
|
}
|
|
|
|
foreach ($values as $value) {
|
|
$coversNode = $this->document->createElement($annotation);
|
|
|
|
$coversNode->setAttribute('target', $value);
|
|
|
|
$testNode->appendChild($coversNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach ($test->doubledTypes() as $doubledType) {
|
|
$testDoubleNode = $this->document->createElement('testDouble');
|
|
|
|
$testDoubleNode->setAttribute('type', $doubledType);
|
|
|
|
$testNode->appendChild($testDoubleNode);
|
|
}
|
|
|
|
$inlineAnnotations = \PHPUnit\Util\Test::getInlineAnnotations(get_class($test), $test->getName(false));
|
|
|
|
if (isset($inlineAnnotations['given'], $inlineAnnotations['when'], $inlineAnnotations['then'])) {
|
|
$testNode->setAttribute('given', $inlineAnnotations['given']['value']);
|
|
$testNode->setAttribute('givenStartLine', (string) $inlineAnnotations['given']['line']);
|
|
$testNode->setAttribute('when', $inlineAnnotations['when']['value']);
|
|
$testNode->setAttribute('whenStartLine', (string) $inlineAnnotations['when']['line']);
|
|
$testNode->setAttribute('then', $inlineAnnotations['then']['value']);
|
|
$testNode->setAttribute('thenStartLine', (string) $inlineAnnotations['then']['line']);
|
|
}
|
|
|
|
if ($this->exception !== null) {
|
|
if ($this->exception instanceof Exception) {
|
|
$steps = $this->exception->getSerializableTrace();
|
|
} else {
|
|
$steps = $this->exception->getTrace();
|
|
}
|
|
|
|
try {
|
|
$file = (new ReflectionClass($test))->getFileName();
|
|
// @codeCoverageIgnoreStart
|
|
} catch (ReflectionException $e) {
|
|
throw new Exception(
|
|
$e->getMessage(),
|
|
$e->getCode(),
|
|
$e,
|
|
);
|
|
}
|
|
// @codeCoverageIgnoreEnd
|
|
|
|
foreach ($steps as $step) {
|
|
if (isset($step['file']) && $step['file'] === $file) {
|
|
$testNode->setAttribute('exceptionLine', (string) $step['line']);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
$testNode->setAttribute('exceptionMessage', $this->exception->getMessage());
|
|
}
|
|
|
|
$this->root->appendChild($testNode);
|
|
}
|
|
}
|