Перенес код

This commit is contained in:
2024-09-03 20:16:34 +03:00
parent 88d85865a0
commit 5344b31f97
1716 changed files with 146261 additions and 6896 deletions

View File

@ -0,0 +1,82 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (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 SebastianBergmann\CodeCoverage\Report\Xml;
use function assert;
use function phpversion;
use DateTimeImmutable;
use DOMElement;
use SebastianBergmann\Environment\Runtime;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final class BuildInformation
{
private readonly DOMElement $contextNode;
public function __construct(DOMElement $contextNode)
{
$this->contextNode = $contextNode;
}
public function setRuntimeInformation(Runtime $runtime): void
{
$runtimeNode = $this->nodeByName('runtime');
$runtimeNode->setAttribute('name', $runtime->getName());
$runtimeNode->setAttribute('version', $runtime->getVersion());
$runtimeNode->setAttribute('url', $runtime->getVendorUrl());
$driverNode = $this->nodeByName('driver');
if ($runtime->hasXdebug()) {
$driverNode->setAttribute('name', 'xdebug');
$driverNode->setAttribute('version', phpversion('xdebug'));
}
if ($runtime->hasPCOV()) {
$driverNode->setAttribute('name', 'pcov');
$driverNode->setAttribute('version', phpversion('pcov'));
}
}
public function setBuildTime(DateTimeImmutable $date): void
{
$this->contextNode->setAttribute('time', $date->format('D M j G:i:s T Y'));
}
public function setGeneratorVersions(string $phpUnitVersion, string $coverageVersion): void
{
$this->contextNode->setAttribute('phpunit', $phpUnitVersion);
$this->contextNode->setAttribute('coverage', $coverageVersion);
}
private function nodeByName(string $name): DOMElement
{
$node = $this->contextNode->getElementsByTagNameNS(
'https://schema.phpunit.de/coverage/1.0',
$name,
)->item(0);
if (!$node) {
$node = $this->contextNode->appendChild(
$this->contextNode->ownerDocument->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
$name,
),
);
}
assert($node instanceof DOMElement);
return $node;
}
}

View File

@ -0,0 +1,63 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (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 SebastianBergmann\CodeCoverage\Report\Xml;
use DOMElement;
use SebastianBergmann\CodeCoverage\ReportAlreadyFinalizedException;
use XMLWriter;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final class Coverage
{
private readonly XMLWriter $writer;
private readonly DOMElement $contextNode;
private bool $finalized = false;
public function __construct(DOMElement $context, string $line)
{
$this->contextNode = $context;
$this->writer = new XMLWriter;
$this->writer->openMemory();
$this->writer->startElementNS(null, $context->nodeName, 'https://schema.phpunit.de/coverage/1.0');
$this->writer->writeAttribute('nr', $line);
}
/**
* @throws ReportAlreadyFinalizedException
*/
public function addTest(string $test): void
{
if ($this->finalized) {
throw new ReportAlreadyFinalizedException;
}
$this->writer->startElement('covered');
$this->writer->writeAttribute('by', $test);
$this->writer->endElement();
}
public function finalize(): void
{
$this->writer->endElement();
$fragment = $this->contextNode->ownerDocument->createDocumentFragment();
$fragment->appendXML($this->writer->outputMemory());
$this->contextNode->parentNode->replaceChild(
$fragment,
$this->contextNode,
);
$this->finalized = true;
}
}

View File

@ -0,0 +1,17 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (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 SebastianBergmann\CodeCoverage\Report\Xml;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final class Directory extends Node
{
}

View File

@ -0,0 +1,304 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (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 SebastianBergmann\CodeCoverage\Report\Xml;
use const DIRECTORY_SEPARATOR;
use const PHP_EOL;
use function count;
use function dirname;
use function file_get_contents;
use function file_put_contents;
use function is_array;
use function is_dir;
use function is_file;
use function is_writable;
use function libxml_clear_errors;
use function libxml_get_errors;
use function libxml_use_internal_errors;
use function sprintf;
use function strlen;
use function substr;
use DateTimeImmutable;
use DOMDocument;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Driver\PathExistsButIsNotDirectoryException;
use SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException;
use SebastianBergmann\CodeCoverage\Node\AbstractNode;
use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode;
use SebastianBergmann\CodeCoverage\Node\File as FileNode;
use SebastianBergmann\CodeCoverage\Util\Filesystem as DirectoryUtil;
use SebastianBergmann\CodeCoverage\Version;
use SebastianBergmann\CodeCoverage\XmlException;
use SebastianBergmann\Environment\Runtime;
final class Facade
{
private string $target;
private Project $project;
private readonly string $phpUnitVersion;
public function __construct(string $version)
{
$this->phpUnitVersion = $version;
}
/**
* @throws XmlException
*/
public function process(CodeCoverage $coverage, string $target): void
{
if (substr($target, -1, 1) !== DIRECTORY_SEPARATOR) {
$target .= DIRECTORY_SEPARATOR;
}
$this->target = $target;
$this->initTargetDirectory($target);
$report = $coverage->getReport();
$this->project = new Project(
$coverage->getReport()->name(),
);
$this->setBuildInformation();
$this->processTests($coverage->getTests());
$this->processDirectory($report, $this->project);
$this->saveDocument($this->project->asDom(), 'index');
}
private function setBuildInformation(): void
{
$buildNode = $this->project->buildInformation();
$buildNode->setRuntimeInformation(new Runtime);
$buildNode->setBuildTime(new DateTimeImmutable);
$buildNode->setGeneratorVersions($this->phpUnitVersion, Version::id());
}
/**
* @throws PathExistsButIsNotDirectoryException
* @throws WriteOperationFailedException
*/
private function initTargetDirectory(string $directory): void
{
if (is_file($directory)) {
if (!is_dir($directory)) {
throw new PathExistsButIsNotDirectoryException($directory);
}
if (!is_writable($directory)) {
throw new WriteOperationFailedException($directory);
}
}
DirectoryUtil::createDirectory($directory);
}
/**
* @throws XmlException
*/
private function processDirectory(DirectoryNode $directory, Node $context): void
{
$directoryName = $directory->name();
if ($this->project->projectSourceDirectory() === $directoryName) {
$directoryName = '/';
}
$directoryObject = $context->addDirectory($directoryName);
$this->setTotals($directory, $directoryObject->totals());
foreach ($directory->directories() as $node) {
$this->processDirectory($node, $directoryObject);
}
foreach ($directory->files() as $node) {
$this->processFile($node, $directoryObject);
}
}
/**
* @throws XmlException
*/
private function processFile(FileNode $file, Directory $context): void
{
$fileObject = $context->addFile(
$file->name(),
$file->id() . '.xml',
);
$this->setTotals($file, $fileObject->totals());
$path = substr(
$file->pathAsString(),
strlen($this->project->projectSourceDirectory()),
);
$fileReport = new Report($path);
$this->setTotals($file, $fileReport->totals());
foreach ($file->classesAndTraits() as $unit) {
$this->processUnit($unit, $fileReport);
}
foreach ($file->functions() as $function) {
$this->processFunction($function, $fileReport);
}
foreach ($file->lineCoverageData() as $line => $tests) {
if (!is_array($tests) || count($tests) === 0) {
continue;
}
$coverage = $fileReport->lineCoverage((string) $line);
foreach ($tests as $test) {
$coverage->addTest($test);
}
$coverage->finalize();
}
$fileReport->source()->setSourceCode(
file_get_contents($file->pathAsString()),
);
$this->saveDocument($fileReport->asDom(), $file->id());
}
private function processUnit(array $unit, Report $report): void
{
if (isset($unit['className'])) {
$unitObject = $report->classObject($unit['className']);
} else {
$unitObject = $report->traitObject($unit['traitName']);
}
$unitObject->setLines(
$unit['startLine'],
$unit['executableLines'],
$unit['executedLines'],
);
$unitObject->setCrap((float) $unit['crap']);
$unitObject->setNamespace($unit['namespace']);
foreach ($unit['methods'] as $method) {
$methodObject = $unitObject->addMethod($method['methodName']);
$methodObject->setSignature($method['signature']);
$methodObject->setLines((string) $method['startLine'], (string) $method['endLine']);
$methodObject->setCrap($method['crap']);
$methodObject->setTotals(
(string) $method['executableLines'],
(string) $method['executedLines'],
(string) $method['coverage'],
);
}
}
private function processFunction(array $function, Report $report): void
{
$functionObject = $report->functionObject($function['functionName']);
$functionObject->setSignature($function['signature']);
$functionObject->setLines((string) $function['startLine']);
$functionObject->setCrap($function['crap']);
$functionObject->setTotals((string) $function['executableLines'], (string) $function['executedLines'], (string) $function['coverage']);
}
private function processTests(array $tests): void
{
$testsObject = $this->project->tests();
foreach ($tests as $test => $result) {
$testsObject->addTest($test, $result);
}
}
private function setTotals(AbstractNode $node, Totals $totals): void
{
$loc = $node->linesOfCode();
$totals->setNumLines(
$loc['linesOfCode'],
$loc['commentLinesOfCode'],
$loc['nonCommentLinesOfCode'],
$node->numberOfExecutableLines(),
$node->numberOfExecutedLines(),
);
$totals->setNumClasses(
$node->numberOfClasses(),
$node->numberOfTestedClasses(),
);
$totals->setNumTraits(
$node->numberOfTraits(),
$node->numberOfTestedTraits(),
);
$totals->setNumMethods(
$node->numberOfMethods(),
$node->numberOfTestedMethods(),
);
$totals->setNumFunctions(
$node->numberOfFunctions(),
$node->numberOfTestedFunctions(),
);
}
private function targetDirectory(): string
{
return $this->target;
}
/**
* @throws XmlException
*/
private function saveDocument(DOMDocument $document, string $name): void
{
$filename = sprintf('%s/%s.xml', $this->targetDirectory(), $name);
$document->formatOutput = true;
$document->preserveWhiteSpace = false;
$this->initTargetDirectory(dirname($filename));
file_put_contents($filename, $this->documentAsString($document));
}
/**
* @throws XmlException
*
* @see https://bugs.php.net/bug.php?id=79191
*/
private function documentAsString(DOMDocument $document): string
{
$xmlErrorHandling = libxml_use_internal_errors(true);
$xml = $document->saveXML();
if ($xml === false) {
$message = 'Unable to generate the XML';
foreach (libxml_get_errors() as $error) {
$message .= PHP_EOL . $error->message;
}
throw new XmlException($message);
}
libxml_clear_errors();
libxml_use_internal_errors($xmlErrorHandling);
return $xml;
}
}

View File

@ -0,0 +1,80 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (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 SebastianBergmann\CodeCoverage\Report\Xml;
use DOMDocument;
use DOMElement;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
class File
{
private readonly DOMDocument $dom;
private readonly DOMElement $contextNode;
public function __construct(DOMElement $context)
{
$this->dom = $context->ownerDocument;
$this->contextNode = $context;
}
public function totals(): Totals
{
$totalsContainer = $this->contextNode->firstChild;
if (!$totalsContainer) {
$totalsContainer = $this->contextNode->appendChild(
$this->dom->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'totals',
),
);
}
return new Totals($totalsContainer);
}
public function lineCoverage(string $line): Coverage
{
$coverage = $this->contextNode->getElementsByTagNameNS(
'https://schema.phpunit.de/coverage/1.0',
'coverage',
)->item(0);
if (!$coverage) {
$coverage = $this->contextNode->appendChild(
$this->dom->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'coverage',
),
);
}
$lineNode = $coverage->appendChild(
$this->dom->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'line',
),
);
return new Coverage($lineNode, $line);
}
protected function contextNode(): DOMElement
{
return $this->contextNode;
}
protected function dom(): DOMDocument
{
return $this->dom;
}
}

View File

@ -0,0 +1,58 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (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 SebastianBergmann\CodeCoverage\Report\Xml;
use DOMElement;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final class Method
{
private readonly DOMElement $contextNode;
public function __construct(DOMElement $context, string $name)
{
$this->contextNode = $context;
$this->setName($name);
}
public function setSignature(string $signature): void
{
$this->contextNode->setAttribute('signature', $signature);
}
public function setLines(string $start, ?string $end = null): void
{
$this->contextNode->setAttribute('start', $start);
if ($end !== null) {
$this->contextNode->setAttribute('end', $end);
}
}
public function setTotals(string $executable, string $executed, string $coverage): void
{
$this->contextNode->setAttribute('executable', $executable);
$this->contextNode->setAttribute('executed', $executed);
$this->contextNode->setAttribute('coverage', $coverage);
}
public function setCrap(string $crap): void
{
$this->contextNode->setAttribute('crap', $crap);
}
private function setName(string $name): void
{
$this->contextNode->setAttribute('name', $name);
}
}

View File

@ -0,0 +1,86 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (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 SebastianBergmann\CodeCoverage\Report\Xml;
use DOMDocument;
use DOMElement;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
abstract class Node
{
private DOMDocument $dom;
private DOMElement $contextNode;
public function __construct(DOMElement $context)
{
$this->setContextNode($context);
}
public function dom(): DOMDocument
{
return $this->dom;
}
public function totals(): Totals
{
$totalsContainer = $this->contextNode()->firstChild;
if (!$totalsContainer) {
$totalsContainer = $this->contextNode()->appendChild(
$this->dom->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'totals',
),
);
}
return new Totals($totalsContainer);
}
public function addDirectory(string $name): Directory
{
$dirNode = $this->dom()->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'directory',
);
$dirNode->setAttribute('name', $name);
$this->contextNode()->appendChild($dirNode);
return new Directory($dirNode);
}
public function addFile(string $name, string $href): File
{
$fileNode = $this->dom()->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'file',
);
$fileNode->setAttribute('name', $name);
$fileNode->setAttribute('href', $href);
$this->contextNode()->appendChild($fileNode);
return new File($fileNode);
}
protected function setContextNode(DOMElement $context): void
{
$this->dom = $context->ownerDocument;
$this->contextNode = $context;
}
protected function contextNode(): DOMElement
{
return $this->contextNode;
}
}

View File

@ -0,0 +1,90 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (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 SebastianBergmann\CodeCoverage\Report\Xml;
use DOMDocument;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final class Project extends Node
{
public function __construct(string $directory)
{
$this->init();
$this->setProjectSourceDirectory($directory);
}
public function projectSourceDirectory(): string
{
return $this->contextNode()->getAttribute('source');
}
public function buildInformation(): BuildInformation
{
$buildNode = $this->dom()->getElementsByTagNameNS(
'https://schema.phpunit.de/coverage/1.0',
'build',
)->item(0);
if (!$buildNode) {
$buildNode = $this->dom()->documentElement->appendChild(
$this->dom()->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'build',
),
);
}
return new BuildInformation($buildNode);
}
public function tests(): Tests
{
$testsNode = $this->contextNode()->getElementsByTagNameNS(
'https://schema.phpunit.de/coverage/1.0',
'tests',
)->item(0);
if (!$testsNode) {
$testsNode = $this->contextNode()->appendChild(
$this->dom()->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'tests',
),
);
}
return new Tests($testsNode);
}
public function asDom(): DOMDocument
{
return $this->dom();
}
private function init(): void
{
$dom = new DOMDocument;
$dom->loadXML('<?xml version="1.0" ?><phpunit xmlns="https://schema.phpunit.de/coverage/1.0"><build/><project/></phpunit>');
$this->setContextNode(
$dom->getElementsByTagNameNS(
'https://schema.phpunit.de/coverage/1.0',
'project',
)->item(0),
);
}
private function setProjectSourceDirectory(string $name): void
{
$this->contextNode()->setAttribute('source', $name);
}
}

View File

@ -0,0 +1,99 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (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 SebastianBergmann\CodeCoverage\Report\Xml;
use function basename;
use function dirname;
use DOMDocument;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final class Report extends File
{
public function __construct(string $name)
{
$dom = new DOMDocument;
$dom->loadXML('<?xml version="1.0" ?><phpunit xmlns="https://schema.phpunit.de/coverage/1.0"><file /></phpunit>');
$contextNode = $dom->getElementsByTagNameNS(
'https://schema.phpunit.de/coverage/1.0',
'file',
)->item(0);
parent::__construct($contextNode);
$this->setName($name);
}
public function asDom(): DOMDocument
{
return $this->dom();
}
public function functionObject($name): Method
{
$node = $this->contextNode()->appendChild(
$this->dom()->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'function',
),
);
return new Method($node, $name);
}
public function classObject($name): Unit
{
return $this->unitObject('class', $name);
}
public function traitObject($name): Unit
{
return $this->unitObject('trait', $name);
}
public function source(): Source
{
$source = $this->contextNode()->getElementsByTagNameNS(
'https://schema.phpunit.de/coverage/1.0',
'source',
)->item(0);
if (!$source) {
$source = $this->contextNode()->appendChild(
$this->dom()->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'source',
),
);
}
return new Source($source);
}
private function setName(string $name): void
{
$this->contextNode()->setAttribute('name', basename($name));
$this->contextNode()->setAttribute('path', dirname($name));
}
private function unitObject(string $tagName, $name): Unit
{
$node = $this->contextNode()->appendChild(
$this->dom()->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
$tagName,
),
);
return new Unit($node, $name);
}
}

View File

@ -0,0 +1,41 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (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 SebastianBergmann\CodeCoverage\Report\Xml;
use DOMElement;
use TheSeer\Tokenizer\NamespaceUri;
use TheSeer\Tokenizer\Tokenizer;
use TheSeer\Tokenizer\XMLSerializer;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final class Source
{
private readonly DOMElement $context;
public function __construct(DOMElement $context)
{
$this->context = $context;
}
public function setSourceCode(string $source): void
{
$context = $this->context;
$tokens = (new Tokenizer)->parse($source);
$srcDom = (new XMLSerializer(new NamespaceUri($context->namespaceURI)))->toDom($tokens);
$context->parentNode->replaceChild(
$context->ownerDocument->importNode($srcDom->documentElement, true),
$context,
);
}
}

View File

@ -0,0 +1,47 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (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 SebastianBergmann\CodeCoverage\Report\Xml;
use function assert;
use DOMElement;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*
* @phpstan-import-type TestType from \SebastianBergmann\CodeCoverage\CodeCoverage
*/
final class Tests
{
private readonly DOMElement $contextNode;
public function __construct(DOMElement $context)
{
$this->contextNode = $context;
}
/**
* @param TestType $result
*/
public function addTest(string $test, array $result): void
{
$node = $this->contextNode->appendChild(
$this->contextNode->ownerDocument->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'test',
),
);
assert($node instanceof DOMElement);
$node->setAttribute('name', $test);
$node->setAttribute('size', $result['size']);
$node->setAttribute('status', $result['status']);
}
}

View File

@ -0,0 +1,123 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (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 SebastianBergmann\CodeCoverage\Report\Xml;
use function sprintf;
use DOMElement;
use DOMNode;
use SebastianBergmann\CodeCoverage\Util\Percentage;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final class Totals
{
private readonly DOMNode $container;
private readonly DOMElement $linesNode;
private readonly DOMElement $methodsNode;
private readonly DOMElement $functionsNode;
private readonly DOMElement $classesNode;
private readonly DOMElement $traitsNode;
public function __construct(DOMElement $container)
{
$this->container = $container;
$dom = $container->ownerDocument;
$this->linesNode = $dom->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'lines',
);
$this->methodsNode = $dom->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'methods',
);
$this->functionsNode = $dom->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'functions',
);
$this->classesNode = $dom->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'classes',
);
$this->traitsNode = $dom->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'traits',
);
$container->appendChild($this->linesNode);
$container->appendChild($this->methodsNode);
$container->appendChild($this->functionsNode);
$container->appendChild($this->classesNode);
$container->appendChild($this->traitsNode);
}
public function container(): DOMNode
{
return $this->container;
}
public function setNumLines(int $loc, int $cloc, int $ncloc, int $executable, int $executed): void
{
$this->linesNode->setAttribute('total', (string) $loc);
$this->linesNode->setAttribute('comments', (string) $cloc);
$this->linesNode->setAttribute('code', (string) $ncloc);
$this->linesNode->setAttribute('executable', (string) $executable);
$this->linesNode->setAttribute('executed', (string) $executed);
$this->linesNode->setAttribute(
'percent',
$executable === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($executed, $executable)->asFloat()),
);
}
public function setNumClasses(int $count, int $tested): void
{
$this->classesNode->setAttribute('count', (string) $count);
$this->classesNode->setAttribute('tested', (string) $tested);
$this->classesNode->setAttribute(
'percent',
$count === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat()),
);
}
public function setNumTraits(int $count, int $tested): void
{
$this->traitsNode->setAttribute('count', (string) $count);
$this->traitsNode->setAttribute('tested', (string) $tested);
$this->traitsNode->setAttribute(
'percent',
$count === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat()),
);
}
public function setNumMethods(int $count, int $tested): void
{
$this->methodsNode->setAttribute('count', (string) $count);
$this->methodsNode->setAttribute('tested', (string) $tested);
$this->methodsNode->setAttribute(
'percent',
$count === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat()),
);
}
public function setNumFunctions(int $count, int $tested): void
{
$this->functionsNode->setAttribute('count', (string) $count);
$this->functionsNode->setAttribute('tested', (string) $tested);
$this->functionsNode->setAttribute(
'percent',
$count === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat()),
);
}
}

View File

@ -0,0 +1,78 @@
<?php declare(strict_types=1);
/*
* This file is part of phpunit/php-code-coverage.
*
* (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 SebastianBergmann\CodeCoverage\Report\Xml;
use function assert;
use DOMElement;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final class Unit
{
private readonly DOMElement $contextNode;
public function __construct(DOMElement $context, string $name)
{
$this->contextNode = $context;
$this->setName($name);
}
public function setLines(int $start, int $executable, int $executed): void
{
$this->contextNode->setAttribute('start', (string) $start);
$this->contextNode->setAttribute('executable', (string) $executable);
$this->contextNode->setAttribute('executed', (string) $executed);
}
public function setCrap(float $crap): void
{
$this->contextNode->setAttribute('crap', (string) $crap);
}
public function setNamespace(string $namespace): void
{
$node = $this->contextNode->getElementsByTagNameNS(
'https://schema.phpunit.de/coverage/1.0',
'namespace',
)->item(0);
if (!$node) {
$node = $this->contextNode->appendChild(
$this->contextNode->ownerDocument->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'namespace',
),
);
}
assert($node instanceof DOMElement);
$node->setAttribute('name', $namespace);
}
public function addMethod(string $name): Method
{
$node = $this->contextNode->appendChild(
$this->contextNode->ownerDocument->createElementNS(
'https://schema.phpunit.de/coverage/1.0',
'method',
),
);
return new Method($node, $name);
}
private function setName(string $name): void
{
$this->contextNode->setAttribute('name', $name);
}
}