Chris@14: Chris@14: * Chris@14: * For the full copyright and license information, please view the LICENSE Chris@14: * file that was distributed with this source code. Chris@14: */ Chris@14: Chris@14: namespace SebastianBergmann\CodeCoverage\Report\Xml; Chris@14: Chris@14: use SebastianBergmann\CodeCoverage\CodeCoverage; Chris@14: use SebastianBergmann\CodeCoverage\Node\AbstractNode; Chris@14: use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; Chris@14: use SebastianBergmann\CodeCoverage\Node\File as FileNode; Chris@14: use SebastianBergmann\CodeCoverage\RuntimeException; Chris@14: use SebastianBergmann\CodeCoverage\Version; Chris@14: use SebastianBergmann\Environment\Runtime; Chris@14: Chris@14: class Facade Chris@14: { Chris@14: /** Chris@14: * @var string Chris@14: */ Chris@14: private $target; Chris@14: Chris@14: /** Chris@14: * @var Project Chris@14: */ Chris@14: private $project; Chris@14: Chris@14: /** Chris@14: * @var string Chris@14: */ Chris@14: private $phpUnitVersion; Chris@14: Chris@14: /** Chris@14: * @param string $version Chris@14: */ Chris@14: public function __construct($version) Chris@14: { Chris@14: $this->phpUnitVersion = $version; Chris@14: } Chris@14: Chris@14: /** Chris@14: * @param CodeCoverage $coverage Chris@14: * @param string $target Chris@14: * Chris@14: * @throws RuntimeException Chris@14: */ Chris@14: public function process(CodeCoverage $coverage, $target) Chris@14: { Chris@14: if (\substr($target, -1, 1) != DIRECTORY_SEPARATOR) { Chris@14: $target .= DIRECTORY_SEPARATOR; Chris@14: } Chris@14: Chris@14: $this->target = $target; Chris@14: $this->initTargetDirectory($target); Chris@14: Chris@14: $report = $coverage->getReport(); Chris@14: Chris@14: $this->project = new Project( Chris@14: $coverage->getReport()->getName() Chris@14: ); Chris@14: Chris@14: $this->setBuildInformation(); Chris@14: $this->processTests($coverage->getTests()); Chris@14: $this->processDirectory($report, $this->project); Chris@14: Chris@14: $this->saveDocument($this->project->asDom(), 'index'); Chris@14: } Chris@14: Chris@14: private function setBuildInformation() Chris@14: { Chris@14: $buildNode = $this->project->getBuildInformation(); Chris@14: $buildNode->setRuntimeInformation(new Runtime()); Chris@14: $buildNode->setBuildTime(\DateTime::createFromFormat('U', $_SERVER['REQUEST_TIME'])); Chris@14: $buildNode->setGeneratorVersions($this->phpUnitVersion, Version::id()); Chris@14: } Chris@14: Chris@14: /** Chris@14: * @param string $directory Chris@14: */ Chris@14: protected function initTargetDirectory($directory) Chris@14: { Chris@14: if (\file_exists($directory)) { Chris@14: if (!\is_dir($directory)) { Chris@14: throw new RuntimeException( Chris@14: "'$directory' exists but is not a directory." Chris@14: ); Chris@14: } Chris@14: Chris@14: if (!\is_writable($directory)) { Chris@14: throw new RuntimeException( Chris@14: "'$directory' exists but is not writable." Chris@14: ); Chris@14: } Chris@14: } elseif (!@\mkdir($directory, 0777, true)) { Chris@14: throw new RuntimeException( Chris@14: "'$directory' could not be created." Chris@14: ); Chris@14: } Chris@14: } Chris@14: Chris@14: private function processDirectory(DirectoryNode $directory, Node $context) Chris@14: { Chris@14: $dirname = $directory->getName(); Chris@14: if ($this->project->getProjectSourceDirectory() === $dirname) { Chris@14: $dirname = '/'; Chris@14: } Chris@14: $dirObject = $context->addDirectory($dirname); Chris@14: Chris@14: $this->setTotals($directory, $dirObject->getTotals()); Chris@14: Chris@14: foreach ($directory->getDirectories() as $node) { Chris@14: $this->processDirectory($node, $dirObject); Chris@14: } Chris@14: Chris@14: foreach ($directory->getFiles() as $node) { Chris@14: $this->processFile($node, $dirObject); Chris@14: } Chris@14: } Chris@14: Chris@14: private function processFile(FileNode $file, Directory $context) Chris@14: { Chris@14: $fileObject = $context->addFile( Chris@14: $file->getName(), Chris@14: $file->getId() . '.xml' Chris@14: ); Chris@14: Chris@14: $this->setTotals($file, $fileObject->getTotals()); Chris@14: Chris@14: $path = \substr( Chris@14: $file->getPath(), Chris@14: \strlen($this->project->getProjectSourceDirectory()) Chris@14: ); Chris@14: $fileReport = new Report($path); Chris@14: Chris@14: $this->setTotals($file, $fileReport->getTotals()); Chris@14: Chris@14: foreach ($file->getClassesAndTraits() as $unit) { Chris@14: $this->processUnit($unit, $fileReport); Chris@14: } Chris@14: Chris@14: foreach ($file->getFunctions() as $function) { Chris@14: $this->processFunction($function, $fileReport); Chris@14: } Chris@14: Chris@14: foreach ($file->getCoverageData() as $line => $tests) { Chris@14: if (!\is_array($tests) || \count($tests) === 0) { Chris@14: continue; Chris@14: } Chris@14: Chris@14: $coverage = $fileReport->getLineCoverage($line); Chris@14: Chris@14: foreach ($tests as $test) { Chris@14: $coverage->addTest($test); Chris@14: } Chris@14: Chris@14: $coverage->finalize(); Chris@14: } Chris@14: Chris@14: $fileReport->getSource()->setSourceCode( Chris@14: \file_get_contents($file->getPath()) Chris@14: ); Chris@14: Chris@14: $this->saveDocument($fileReport->asDom(), $file->getId()); Chris@14: } Chris@14: Chris@14: private function processUnit($unit, Report $report) Chris@14: { Chris@14: if (isset($unit['className'])) { Chris@14: $unitObject = $report->getClassObject($unit['className']); Chris@14: } else { Chris@14: $unitObject = $report->getTraitObject($unit['traitName']); Chris@14: } Chris@14: Chris@14: $unitObject->setLines( Chris@14: $unit['startLine'], Chris@14: $unit['executableLines'], Chris@14: $unit['executedLines'] Chris@14: ); Chris@14: Chris@14: $unitObject->setCrap($unit['crap']); Chris@14: Chris@14: $unitObject->setPackage( Chris@14: $unit['package']['fullPackage'], Chris@14: $unit['package']['package'], Chris@14: $unit['package']['subpackage'], Chris@14: $unit['package']['category'] Chris@14: ); Chris@14: Chris@14: $unitObject->setNamespace($unit['package']['namespace']); Chris@14: Chris@14: foreach ($unit['methods'] as $method) { Chris@14: $methodObject = $unitObject->addMethod($method['methodName']); Chris@14: $methodObject->setSignature($method['signature']); Chris@14: $methodObject->setLines($method['startLine'], $method['endLine']); Chris@14: $methodObject->setCrap($method['crap']); Chris@14: $methodObject->setTotals( Chris@14: $method['executableLines'], Chris@14: $method['executedLines'], Chris@14: $method['coverage'] Chris@14: ); Chris@14: } Chris@14: } Chris@14: Chris@14: private function processFunction($function, Report $report) Chris@14: { Chris@14: $functionObject = $report->getFunctionObject($function['functionName']); Chris@14: Chris@14: $functionObject->setSignature($function['signature']); Chris@14: $functionObject->setLines($function['startLine']); Chris@14: $functionObject->setCrap($function['crap']); Chris@14: $functionObject->setTotals($function['executableLines'], $function['executedLines'], $function['coverage']); Chris@14: } Chris@14: Chris@14: private function processTests(array $tests) Chris@14: { Chris@14: $testsObject = $this->project->getTests(); Chris@14: Chris@14: foreach ($tests as $test => $result) { Chris@14: if ($test == 'UNCOVERED_FILES_FROM_WHITELIST') { Chris@14: continue; Chris@14: } Chris@14: Chris@14: $testsObject->addTest($test, $result); Chris@14: } Chris@14: } Chris@14: Chris@14: private function setTotals(AbstractNode $node, Totals $totals) Chris@14: { Chris@14: $loc = $node->getLinesOfCode(); Chris@14: Chris@14: $totals->setNumLines( Chris@14: $loc['loc'], Chris@14: $loc['cloc'], Chris@14: $loc['ncloc'], Chris@14: $node->getNumExecutableLines(), Chris@14: $node->getNumExecutedLines() Chris@14: ); Chris@14: Chris@14: $totals->setNumClasses( Chris@14: $node->getNumClasses(), Chris@14: $node->getNumTestedClasses() Chris@14: ); Chris@14: Chris@14: $totals->setNumTraits( Chris@14: $node->getNumTraits(), Chris@14: $node->getNumTestedTraits() Chris@14: ); Chris@14: Chris@14: $totals->setNumMethods( Chris@14: $node->getNumMethods(), Chris@14: $node->getNumTestedMethods() Chris@14: ); Chris@14: Chris@14: $totals->setNumFunctions( Chris@14: $node->getNumFunctions(), Chris@14: $node->getNumTestedFunctions() Chris@14: ); Chris@14: } Chris@14: Chris@14: /** Chris@14: * @return string Chris@14: */ Chris@14: protected function getTargetDirectory() Chris@14: { Chris@14: return $this->target; Chris@14: } Chris@14: Chris@14: protected function saveDocument(\DOMDocument $document, $name) Chris@14: { Chris@14: $filename = \sprintf('%s/%s.xml', $this->getTargetDirectory(), $name); Chris@14: Chris@14: $document->formatOutput = true; Chris@14: $document->preserveWhiteSpace = false; Chris@14: $this->initTargetDirectory(\dirname($filename)); Chris@14: Chris@14: $document->save($filename); Chris@14: } Chris@14: }