/** * Returns the link to this node. * * @param boolean $full * @return string */ public function getLink($full) { if (substr($this->name, -1) == DIRECTORY_SEPARATOR) { $name = substr($this->name, 0, -1); } else { $name = $this->name; } $cleanId = PHPUnit_Util_Filesystem::getSafeFilename($this->getId()); if ($full) { if ($this->parent !== NULL) { $parent = $this->parent->getLink(TRUE) . DIRECTORY_SEPARATOR; } else { $parent = ''; } return sprintf('%s<a href="%s.html">%s</a>', $parent, $cleanId, $name); } else { return sprintf('<a href="%s.html">%s</a>', $cleanId, $name); } }
/** * Returns this node's link. * * @param boolean $full * @return string * @access public */ public function getLink($full = FALSE) { if ($full && $this->parent !== NULL) { return sprintf('%s / <a href="%s-test.html">%s</a>', $this->parent->getLink(TRUE), PHPUnit_Util_Filesystem::getSafeFilename($this->getName()), $this->getName()); } else { return sprintf('<a href="%s-test.html">%s</a>', PHPUnit_Util_Filesystem::getSafeFilename($this->getName()), $this->getName()); } }
/** * Renders this node. * * @param string $target * @param string $title * @param string $charset * @param boolean $highlight * @param integer $lowUpperBound * @param integer $highLowerBound * @access public */ public function render($target, $title, $charset = 'ISO-8859-1', $highlight = FALSE, $lowUpperBound = 35, $highLowerBound = 70) { if ($this->yui) { $template = new PHPUnit_Util_Template(PHPUnit_Util_Report::$templatePath . 'file.html'); $yuiTemplate = new PHPUnit_Util_Template(PHPUnit_Util_Report::$templatePath . 'yui_item.js'); } else { $template = new PHPUnit_Util_Template(PHPUnit_Util_Report::$templatePath . 'file_no_yui.html'); } $i = 1; $lines = ''; $ignore = FALSE; foreach ($this->codeLines as $line) { if (strpos($line, '@codeCoverageIgnore') !== FALSE) { if (strpos($line, '@codeCoverageIgnoreStart') !== FALSE) { $ignore = TRUE; } else { if (strpos($line, '@codeCoverageIgnoreEnd') !== FALSE) { $ignore = FALSE; } } } $css = ''; if (!$ignore && isset($this->executedLines[$i])) { $count = ''; // Array: Line is executable and was executed. // count(Array) = Number of tests that hit this line. if (is_array($this->executedLines[$i])) { $color = 'lineCov'; $numTests = count($this->executedLines[$i]); $count = sprintf('%8d', $numTests); if ($this->yui) { $buffer = ''; foreach ($this->executedLines[$i] as $test) { if (!isset($test->__liHtml)) { $test->__liHtml = ''; if ($test instanceof PHPUnit_Framework_SelfDescribing) { $testName = $test->toString(); if ($test instanceof PHPUnit_Framework_TestCase) { switch ($test->getStatus()) { case PHPUnit_Runner_BaseTestRunner::STATUS_PASSED: $testCSS = ' class=\\"testPassed\\"'; break; case PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE: $testCSS = ' class=\\"testFailure\\"'; break; case PHPUnit_Runner_BaseTestRunner::STATUS_ERROR: $testCSS = ' class=\\"testError\\"'; break; case PHPUnit_Runner_BaseTestRunner::STATUS_INCOMPLETE: case PHPUnit_Runner_BaseTestRunner::STATUS_SKIPPED: $testCSS = ' class=\\"testIncomplete\\"'; break; default: $testCSS = ''; } } } $test->__liHtml .= sprintf('<li%s>%s</li>', $testCSS, $testName); } $buffer .= $test->__liHtml; } if ($numTests > 1) { $header = $numTests . ' tests cover'; } else { $header = '1 test covers'; } $header .= ' line ' . $i; $yuiTemplate->setVar(array('line' => $i, 'header' => $header, 'tests' => $buffer), FALSE); $this->yuiPanelJS .= $yuiTemplate->render(); } } else { if ($this->executedLines[$i] == -1) { $color = 'lineNoCov'; $count = sprintf('%8d', 0); } else { $color = 'lineDeadCode'; $count = ' '; } } $css = sprintf('<span class="%s"> %s : ', $color, $count); } $fillup = array_shift($this->codeLinesFillup); if ($fillup > 0) { $line .= str_repeat(' ', $fillup); } $lines .= sprintf('<span class="lineNum" id="container%d"><a name="%d"></a><a href="#%d" id="line%d">%8d</a> </span>%s%s%s' . "\n", $i, $i, $i, $i, $i, !empty($css) ? $css : ' : ', !$this->highlight ? htmlspecialchars($line) : $line, !empty($css) ? '</span>' : ''); $i++; } $items = ''; foreach ($this->classes as $className => $classData) { $numCalledClasses = $classData['executedLines'] > 0 ? 1 : 0; $calledClassesPercent = $numCalledClasses == 1 ? 100 : 0; $numCalledMethods = 0; $numMethods = count($classData['methods']); foreach ($classData['methods'] as $method) { if ($method['executedLines'] > 0) { $numCalledMethods++; } } $items .= $this->doRenderItem(array('name' => sprintf('<b><a href="#%d">%s</a></b>', $classData['startLine'], $className), 'numClasses' => 1, 'numCalledClasses' => $numCalledClasses, 'calledClassesPercent' => sprintf('%01.2f', $calledClassesPercent), 'numMethods' => $numMethods, 'numCalledMethods' => $numCalledMethods, 'calledMethodsPercent' => $this->calculatePercent($numCalledMethods, $numMethods), 'numExecutableLines' => $classData['executableLines'], 'numExecutedLines' => $classData['executedLines'], 'executedLinesPercent' => $this->calculatePercent($classData['executedLines'], $classData['executableLines'])), $lowUpperBound, $highLowerBound); foreach ($classData['methods'] as $methodName => $methodData) { $numCalledMethods = $methodData['executedLines'] > 0 ? 1 : 0; $calledMethodsPercent = $numCalledMethods == 1 ? 100 : 0; $items .= $this->doRenderItem(array('name' => sprintf(' <a href="#%d">%s</a>', $methodData['startLine'], PHPUnit_Util_Class::getMethodSignature(new ReflectionMethod($className, $methodName))), 'numClasses' => '', 'numCalledClasses' => '', 'calledClassesPercent' => '', 'numMethods' => 1, 'numCalledMethods' => $numCalledMethods, 'calledMethodsPercent' => sprintf('%01.2f', $calledMethodsPercent), 'numExecutableLines' => $methodData['executableLines'], 'numExecutedLines' => $methodData['executedLines'], 'executedLinesPercent' => $this->calculatePercent($methodData['executedLines'], $methodData['executableLines'])), $lowUpperBound, $highLowerBound, 'method_item.html'); } } $this->setTemplateVars($template, $title, $charset); $template->setVar(array('lines' => $lines, 'total_item' => $this->renderTotalItem($lowUpperBound, $highLowerBound, FALSE), 'items' => $items, 'yuiPanelJS' => $this->yuiPanelJS)); $cleanId = PHPUnit_Util_Filesystem::getSafeFilename($this->getId()); $template->renderTo($target . $cleanId . '.html'); }
/** * @param PHPUnit_Framework_Test $test * @param string $color * @access private */ private function addTestNode(PHPUnit_Framework_Test $test, $color) { $name = PHPUnit_Util_Test::describe($test, FALSE); $this->graphs[$this->testSuiteLevel]->addNode($name[1], array('color' => $color, 'URL' => sprintf('%s-test.html#%s', PHPUnit_Util_Filesystem::getSafeFilename($name[0]), $name[1])), $this->testSuites[$this->testSuiteLevel]); $this->graphs[$this->testSuiteLevel]->addEdge(array($this->testSuites[$this->testSuiteLevel] => $name[1])); }
/** * @param string $target * @param string $title * @param string $charset * @param boolean $highlight * @param integer $lowUpperBound * @param integer $highLowerBound * @access protected */ protected function doRender($target, $title, $charset, $highlight, $lowUpperBound, $highLowerBound) { $cleanId = PHPUnit_Util_Filesystem::getSafeFilename($this->getId()); $file = $target . $cleanId . '.html'; $template = new PHPUnit_Util_Template(PHPUnit_Util_Report::$templatePath . 'directory.html'); $this->setTemplateVars($template, $title, $charset); $totalClassesPercent = $this->getCalledClassesPercent(); list($totalClassesColor, $totalClassesLevel) = $this->getColorLevel($totalClassesPercent, $lowUpperBound, $highLowerBound); $totalMethodsPercent = $this->getCalledMethodsPercent(); list($totalMethodsColor, $totalMethodsLevel) = $this->getColorLevel($totalMethodsPercent, $lowUpperBound, $highLowerBound); $totalLinesPercent = $this->getLineExecutedPercent(); list($totalLinesColor, $totalLinesLevel) = $this->getColorLevel($totalLinesPercent, $lowUpperBound, $highLowerBound); $template->setVar(array('total_item' => $this->renderTotalItem($lowUpperBound, $highLowerBound), 'items' => $this->renderItems($lowUpperBound, $highLowerBound), 'low_upper_bound' => $lowUpperBound, 'high_lower_bound' => $highLowerBound)); $template->renderTo($file); }
/** * @param array $items * @param boolean $includeDetails * @return string * @access protected */ protected function doRenderItems(array $items, $includeDetails) { $result = ''; foreach ($items as $item) { $itemTemplate = new PHPUnit_Util_Template(PHPUnit_Util_Report::getTemplatePath() . 'coverage_item.html'); $details = ''; if ($includeDetails) { foreach ($item->getCoveringTests() as $suite => $tests) { $detailsHeaderTemplate = new PHPUnit_Util_Template(PHPUnit_Util_Report::getTemplatePath() . 'coverage_item_details_header.html'); $detailsHeaderTemplate->setVar('link', sprintf('<a href="%s-test.html">%s</a>', PHPUnit_Util_Filesystem::getSafeFilename($suite), $suite)); $details .= $detailsHeaderTemplate->render(); foreach ($tests as $test => $_test) { $detailsTemplate = new PHPUnit_Util_Template(PHPUnit_Util_Report::getTemplatePath() . 'coverage_item_details.html'); if ($_test['object']->getResult() !== PHPUnit_Runner_BaseTestRunner::STATUS_PASSED) { $failure = sprintf('<br /><pre>%s</pre>', htmlspecialchars($_test['object']->getResult()->exceptionMessage())); } else { $failure = ''; } $detailsTemplate->setVar(array('item', 'executed_percent', 'executed_lines', 'executable_lines'), array($test . $failure, sprintf('%01.2f', $_test['numLinesExecuted'] / $item->getNumExecutableLines() * 100), $_test['numLinesExecuted'], $item->getNumExecutableLines())); $details .= $detailsTemplate->render(); } } } $floorPercent = floor($item->getExecutedPercent()); if ($floorPercent < self::LOW_UPPER_BOUND) { $color = 'scarlet_red'; $level = 'Lo'; } else { if ($floorPercent >= self::LOW_UPPER_BOUND && $floorPercent < self::HIGH_LOWER_BOUND) { $color = 'butter'; $level = 'Med'; } else { $color = 'chameleon'; $level = 'Hi'; } } $itemTemplate->setVar(array('link', 'color', 'level', 'executed_width', 'executed_percent', 'not_executed_width', 'executable_lines', 'executed_lines', 'details'), array($item->getLink(FALSE, FALSE), $color, $level, floor($item->getExecutedPercent()), $item->getExecutedPercent(), 100 - floor($item->getExecutedPercent()), $item->getNumExecutableLines(), $item->getNumExecutedLines(), $details)); $result .= $itemTemplate->render(); } return $result; }
/** * @param string $target * @param string $title * @param string $charset * @param integer $lowUpperBound * @param integer $highLowerBound */ protected function doRender($target, $title, $charset, $lowUpperBound, $highLowerBound) { $cleanId = PHPUnit_Util_Filesystem::getSafeFilename($this->getId()); $file = $target . $cleanId . '.html'; $template = new PHPUnit_Util_Template(PHPUnit_Util_Report::$templatePath . 'directory.html'); $this->setTemplateVars($template, $title, $charset); $template->setVar(array('total_item' => $this->renderTotalItem($lowUpperBound, $highLowerBound), 'items' => $this->renderItems($lowUpperBound, $highLowerBound), 'low_upper_bound' => $lowUpperBound, 'high_lower_bound' => $highLowerBound)); $template->renderTo($file); $this->directories = array(); $this->files = array(); }
/** * @param PHPUnit_Framework_TestResult $result */ public function process(PHPUnit_Framework_TestResult $result) { $sutData = $result->getCodeCoverageInformation(); $sutFiles = PHPUnit_Util_CodeCoverage::getSummary($sutData, TRUE); $allData = $result->getCodeCoverageInformation(FALSE); $allFiles = PHPUnit_Util_CodeCoverage::getSummary($allData, TRUE); $testFiles = array_diff(array_keys($allFiles), array_keys($sutFiles)); foreach (array_keys($allFiles) as $key) { if (!@in_array($key, $testFiles)) { unset($allFiles[$key]); } } $allCommonPath = PHPUnit_Util_Filesystem::reducePaths($allFiles); $sutCommonPath = PHPUnit_Util_Filesystem::reducePaths($sutFiles); $testFiles = $allFiles; unset($allData); unset($allFiles); unset($sutData); $testToCoveredLinesMap = array(); $time = time(); foreach ($sutFiles as $filename => $data) { $fullPath = $sutCommonPath . DIRECTORY_SEPARATOR . $filename; if (file_exists($fullPath)) { $fullPath = realpath($fullPath); $document = new DOMDocument('1.0', 'UTF-8'); $document->formatOutput = TRUE; $coveredFile = $document->createElement('coveredFile'); $coveredFile->setAttribute('fullPath', $fullPath); $coveredFile->setAttribute('shortenedPath', $filename); $coveredFile->setAttribute('generated', $time); $coveredFile->setAttribute('phpunit', PHPUnit_Runner_Version::id()); $document->appendChild($coveredFile); $lines = file($fullPath, FILE_IGNORE_NEW_LINES); $lineNum = 1; foreach ($lines as $line) { if (isset($data[$lineNum])) { if (is_array($data[$lineNum])) { $count = count($data[$lineNum]); } else { $count = $data[$lineNum]; } } else { $count = -3; } $xmlLine = $coveredFile->appendChild($document->createElement('line')); $xmlLine->setAttribute('lineNumber', $lineNum); $xmlLine->setAttribute('executed', $count); $xmlLine->appendChild($document->createElement('body', htmlspecialchars(PHPUnit_Util_XML::convertToUtf8($line), ENT_COMPAT, 'UTF-8'))); if (isset($data[$lineNum]) && is_array($data[$lineNum])) { $xmlTests = $document->createElement('tests'); $xmlLine->appendChild($xmlTests); foreach ($data[$lineNum] as $test) { $xmlTest = $xmlTests->appendChild($document->createElement('test')); if ($test instanceof PHPUnit_Framework_TestCase) { $xmlTest->setAttribute('name', $test->getName()); $xmlTest->setAttribute('status', $test->getStatus()); if ($test->hasFailed()) { $xmlTest->appendChild($document->createElement('message', htmlspecialchars(PHPUnit_Util_XML::convertToUtf8($test->getStatusMessage()), ENT_COMPAT, 'UTF-8'))); } $class = new ReflectionClass($test); $testFullPath = $class->getFileName(); $testShortenedPath = str_replace($allCommonPath, '', $testFullPath); $methodName = $test->getName(FALSE); if ($class->hasMethod($methodName)) { $method = $class->getMethod($methodName); $startLine = $method->getStartLine(); $xmlTest->setAttribute('class', $class->getName()); $xmlTest->setAttribute('fullPath', $testFullPath); $xmlTest->setAttribute('shortenedPath', $testShortenedPath); $xmlTest->setAttribute('line', $startLine); if (!isset($testToCoveredLinesMap[$testFullPath][$startLine])) { $testToCoveredLinesMap[$testFullPath][$startLine] = array(); } if (!isset($testToCoveredLinesMap[$testFullPath][$startLine][$fullPath])) { $testToCoveredLinesMap[$testFullPath][$startLine][$fullPath] = array('coveredLines' => array($lineNum), 'shortenedPath' => $filename); } else { $testToCoveredLinesMap[$testFullPath][$startLine][$fullPath]['coveredLines'][] = $lineNum; } } } } } $lineNum++; } $document->save(sprintf('%s%s.xml', $this->directory, PHPUnit_Util_Filesystem::getSafeFilename(str_replace(DIRECTORY_SEPARATOR, '_', $filename)))); } } foreach ($testFiles as $filename => $data) { $fullPath = $allCommonPath . DIRECTORY_SEPARATOR . $filename; if (file_exists($fullPath)) { $document = new DOMDocument('1.0', 'UTF-8'); $document->formatOutput = TRUE; $testFile = $document->createElement('testFile'); $testFile->setAttribute('fullPath', $fullPath); $testFile->setAttribute('shortenedPath', $filename); $testFile->setAttribute('generated', $time); $testFile->setAttribute('phpunit', PHPUnit_Runner_Version::id()); $document->appendChild($testFile); $lines = file($fullPath, FILE_IGNORE_NEW_LINES); $lineNum = 1; foreach ($lines as $line) { $xmlLine = $testFile->appendChild($document->createElement('line')); $xmlLine->setAttribute('lineNumber', $lineNum); $xmlLine->appendChild($document->createElement('body', htmlspecialchars(PHPUnit_Util_XML::convertToUtf8($line), ENT_COMPAT, 'UTF-8'))); if (isset($testToCoveredLinesMap[$fullPath][$lineNum])) { $xmlCoveredFiles = $xmlLine->appendChild($document->createElement('coveredFiles')); foreach ($testToCoveredLinesMap[$fullPath][$lineNum] as $coveredFileFullPath => $coveredFileData) { $xmlCoveredFile = $xmlCoveredFiles->appendChild($document->createElement('coveredFile')); $xmlCoveredFile->setAttribute('fullPath', $fullPath); $xmlCoveredFile->setAttribute('shortenedPath', $coveredFileData['shortenedPath']); foreach ($coveredFileData['coveredLines'] as $coveredLineNum) { $xmlCoveredLine = $xmlCoveredFile->appendChild($document->createElement('coveredLine', $coveredLineNum)); } } } $lineNum++; } $document->save(sprintf('%s%s.xml', $this->directory, PHPUnit_Util_Filesystem::getSafeFilename(str_replace(DIRECTORY_SEPARATOR, '_', $filename)))); } } }
/** * @param PHPUnit_Util_Template $template * @param string $target * @access public */ protected function setGraphVizTemplateVars(PHPUnit_Util_Template $template, $target) { $testmap = ''; $testmap_image = 'snow.png'; $safeName = PHPUnit_Util_Filesystem::getSafeFilename($this->getName()); $dotFile = $target . $safeName . '.dot'; if (file_exists($dotFile) && class_exists('Image_GraphViz', FALSE)) { $pngFile = $target . $safeName . '.png'; $mapFile = $target . $safeName . '.map'; shell_exec(sprintf('neato -T png -o %s -T cmapx -o %s %s', $pngFile, $mapFile, $dotFile)); if (file_exists($pngFile)) { $testmap_image = basename($pngFile); } if (file_exists($mapFile)) { $testmap = file_get_contents($mapFile); unlink($mapFile); } unlink($dotFile); } $template->setVar(array('testmap', 'testmap_image'), array($testmap, $testmap_image)); }
/** * Renders this node. * * @param string $target * @param string $title * @access public */ public function render($target, $title) { $template = new PHPUnit_Util_Template(PHPUnit_Util_Report::getTemplatePath() . 'coverage_file.html'); $i = 1; $lines = ''; foreach ($this->codeLines as $line) { $css = ''; if (isset($this->executedLines[$i])) { $count = ''; // Array: Line is executable and was executed. // count(Array) = Number of tests that hit this line. if (is_array($this->executedLines[$i])) { $color = 'lineCov'; $count = sprintf('%8d', count($this->executedLines[$i])); } else { if ($this->executedLines[$i] == -1) { $color = 'lineNoCov'; $count = sprintf('%8d', 0); } else { $color = 'lineDeadCode'; $count = ' '; } } $css = sprintf('<span class="%s"> %s : ', $color, $count); } $lines .= sprintf('<span class="lineNum"><a name="%d"></a><a href="#%d">%8d</a> </span>%s%s%s' . "\n", $i, $i, $i, !empty($css) ? $css : ' : ', $line . str_repeat(' ', array_shift($this->codeLinesFillup)), !empty($css) ? '</span>' : ''); $i++; } $this->setTemplateVars($template, $title); $template->setVar('lines', $lines); $cleanId = PHPUnit_Util_Filesystem::getSafeFilename($this->getId()); $template->renderTo($target . $cleanId . '.html'); }