initial html coverage reports
This commit is contained in:
parent
a57c690dc5
commit
3c47c48488
@ -40,6 +40,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @author Tommy Montgomery
|
||||
* @since 1.0
|
||||
* @version 1.0
|
||||
* @uses getOption()
|
||||
* @uses CoverageReporter::createConsoleReport()
|
||||
* @uses CoverageReporter::createHtmlReport()
|
||||
*/
|
||||
protected function postRun() {
|
||||
$html = $this->getOption('coverage-html');
|
||||
$console = $this->getOption('coverage-console');
|
||||
|
@ -66,10 +66,19 @@
|
||||
throw new LogicException('The class "' . $class . '" is final and cannot be mocked');
|
||||
}
|
||||
|
||||
$constructor = $refClass->getConstructor();
|
||||
if ($constructor === null) {
|
||||
$constructor = '__construct';
|
||||
$callParent = false;
|
||||
} else {
|
||||
$constructor = $constructor->getName();
|
||||
}
|
||||
|
||||
$this->referenceObject = $refClass;
|
||||
|
||||
$this->methods = array(
|
||||
'default' => array(
|
||||
$this->referenceObject->getConstructor()->getName() => array(
|
||||
$constructor => array(
|
||||
'body' => '',
|
||||
'call_parent' => (bool)$callParent
|
||||
)
|
||||
|
@ -2,8 +2,10 @@
|
||||
|
||||
class CoverageReporter {
|
||||
|
||||
const UNUSED = -1;
|
||||
const DEAD = -2;
|
||||
const UNUSED = -1;
|
||||
const DEAD = -2;
|
||||
|
||||
const TEMPLATE_DIR = 'template';
|
||||
|
||||
private function __construct() {}
|
||||
|
||||
@ -45,24 +47,130 @@
|
||||
fwrite(STDOUT, " Executable: $totloc (" . round($totcloc / $totloc * 100, 2) . "%)\n");
|
||||
}
|
||||
|
||||
public static function createXmlReport($file, array $coverageData) {
|
||||
|
||||
}
|
||||
|
||||
public static function createHtmlReport($dir, array $coverageData) {
|
||||
if (!is_dir($dir)) {
|
||||
throw new TUnitException($dir . ' is not a directory');
|
||||
}
|
||||
|
||||
|
||||
|
||||
$coverageData = CoverageFilter::filter($coverageData);
|
||||
foreach ($coverageData as $file => $data) {
|
||||
foreach ($data as $unitsCovered) {
|
||||
|
||||
$baseDir = array();
|
||||
foreach ($coverageData as $file => $data) {
|
||||
$dirs = explode(DIRECTORY_SEPARATOR, dirname($file));
|
||||
if (empty($baseDir)) {
|
||||
$baseDir = $dirs;
|
||||
} else {
|
||||
for ($i = 0, $len = count($dirs); $i < $len; $i++) {
|
||||
if (!isset($baseDir[$i]) || $baseDir[$i] !== $dirs[$i]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$baseDir = array_slice($dirs, 0, $i);
|
||||
}
|
||||
}
|
||||
|
||||
$baseDir = implode(DIRECTORY_SEPARATOR, $baseDir) . DIRECTORY_SEPARATOR;
|
||||
|
||||
$totalData = array();
|
||||
foreach ($coverageData as $file => $data) {
|
||||
$totalData[$file] = array(
|
||||
'loc' => 0,
|
||||
'dloc' => 0,
|
||||
'cloc' => 0
|
||||
);
|
||||
|
||||
foreach ($data as $line => $unitsCovered) {
|
||||
$totalData[$file]['loc']++;
|
||||
if ($unitsCovered > 0) {
|
||||
$totalData[$file]['cloc']++;
|
||||
} else if ($unitsCovered === self::DEAD) {
|
||||
$totalData[$file]['dloc']++;
|
||||
}
|
||||
}
|
||||
|
||||
self::writeHtmlFile($file, $baseDir, $dir, $data);
|
||||
}
|
||||
|
||||
//copy css over
|
||||
$template = dirname(__FILE__) . DIRECTORY_SEPARATOR . self::TEMPLATE_DIR . DIRECTORY_SEPARATOR;
|
||||
copy($template . 'style.css', $dir . DIRECTORY_SEPARATOR . 'style.css');
|
||||
}
|
||||
|
||||
private static function writeHtmlFile($sourceFile, $baseDir, $coverageDir, array $data) {
|
||||
$lines = file($sourceFile, FILE_IGNORE_NEW_LINES);
|
||||
$code = '';
|
||||
$lineNumbers = '';
|
||||
for ($i = 1, $len = count($lines); $i <= $len; $i++) {
|
||||
$lineNumbers .= '<div><a href="#line-' . $i . '">' . $i . '</a></div>';
|
||||
$code .= '<div';
|
||||
if (isset($data[$i])) {
|
||||
$code .= ' class="';
|
||||
if ($data[$i] > 0) {
|
||||
$code .= 'covered';
|
||||
} else if ($data[$i] === self::DEAD) {
|
||||
$code .= 'dead';
|
||||
} else if ($data[$i] === self::UNUSED) {
|
||||
$code .= 'uncovered';
|
||||
}
|
||||
|
||||
$code .= '">';
|
||||
} else {
|
||||
$code .= '>';
|
||||
}
|
||||
|
||||
if (empty($lines[$i])) {
|
||||
$lines[$i] = ' ';
|
||||
}
|
||||
|
||||
$code .= htmlentities(str_replace("\t", ' ', $lines[$i - 1]), ENT_QUOTES) ."</div>\n";
|
||||
}
|
||||
|
||||
unset($lines);
|
||||
|
||||
$fileName = str_replace(array($baseDir, DIRECTORY_SEPARATOR), array('', '_'), $sourceFile);
|
||||
$newFile = $coverageDir . DIRECTORY_SEPARATOR . $fileName . '.html';
|
||||
|
||||
$link = '<a href="./index.html">' . $baseDir . '</a>';
|
||||
$dirs = preg_split('@\\' . DIRECTORY_SEPARATOR . '@', str_replace($baseDir, '', dirname($sourceFile) . DIRECTORY_SEPARATOR), -1, PREG_SPLIT_NO_EMPTY);
|
||||
$path = '';
|
||||
foreach ($dirs as $dir) {
|
||||
$path = ltrim($path . '_' . $dir, '_');
|
||||
$link .= '<a href="./' . $path . '.html">' . $dir . '</a>' . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
$template = file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . self::TEMPLATE_DIR . DIRECTORY_SEPARATOR . 'file.html');
|
||||
$template = preg_replace(
|
||||
array(
|
||||
'/\$\{title\}/',
|
||||
'/\$\{file\.name\}/',
|
||||
'/\$\{line.numbers\}/',
|
||||
'/\$\{code\}/',
|
||||
'/\$\{timestamp\}/',
|
||||
'/\$\{product\.name\}/',
|
||||
'/\$\{product\.version\}/',
|
||||
'/\$\{product\.website\}/',
|
||||
'/\$\{product\.author\}/'
|
||||
),
|
||||
array(
|
||||
Product::NAME . ' - Coverage Report',
|
||||
$link . basename($sourceFile),
|
||||
$lineNumbers,
|
||||
$code,
|
||||
date('Y-m-d H:i:s'),
|
||||
Product::NAME,
|
||||
Product::VERSION,
|
||||
Product::WEBSITE,
|
||||
Product::AUTHOR
|
||||
),
|
||||
$template
|
||||
);
|
||||
|
||||
return file_put_contents($newFile, $template);
|
||||
}
|
||||
|
||||
private static function writeHtmlDir(array $data) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
39
src/TUnit/framework/reporting/template/file.html
Normal file
39
src/TUnit/framework/reporting/template/file.html
Normal file
@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
|
||||
<head>
|
||||
<title>${title}</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
|
||||
<link rel="stylesheet" type="text/css" href="./style.css"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="wrapper">
|
||||
|
||||
<div id="header">
|
||||
<h1>${file.name}</h1>
|
||||
</div>
|
||||
|
||||
<div id="code-wrapper">
|
||||
<div id="line-numbers">
|
||||
${line.numbers}
|
||||
</div>
|
||||
<div id="code">
|
||||
${code}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
<p>
|
||||
Generated on ${timestamp} by <a href="${product.website}">${product.name} ${product.version}</a>
|
||||
<br />
|
||||
© 2009 ${product.author}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
46
src/TUnit/framework/reporting/template/style.css
Normal file
46
src/TUnit/framework/reporting/template/style.css
Normal file
@ -0,0 +1,46 @@
|
||||
html {
|
||||
overflow: auto;
|
||||
}
|
||||
body {
|
||||
background-color: #FFFFCC;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
#code-wrapper {
|
||||
background-color: #FFFFFF;
|
||||
font-family: Consolas, "Courier New", monospace;
|
||||
font-size: 16px;
|
||||
border: 2px solid #000000;
|
||||
}
|
||||
#line-numbers {
|
||||
background-color: #CCCCCC;
|
||||
float: left;
|
||||
text-align: right;
|
||||
padding: 0 5px;
|
||||
border-right: 2px solid #000000;
|
||||
}
|
||||
#line-numbers a {
|
||||
color: #000000;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
}
|
||||
#line-numbers div {
|
||||
white-space: pre;
|
||||
}
|
||||
#code {
|
||||
padding-left: 10px;
|
||||
overflow: auto;
|
||||
}
|
||||
#code div {
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.covered {
|
||||
background-color: #66FF66;
|
||||
}
|
||||
.uncovered {
|
||||
background-color: #FF6666;
|
||||
}
|
||||
.dead {
|
||||
background-color: #999999;
|
||||
}
|
@ -48,7 +48,7 @@
|
||||
->addSwitch(new CliSwitch('usage', null, false, null, 'Display this help message'))
|
||||
->addSwitch(new CliSwitch('recursive', null, false, null, 'Recurse into subdirectories'))
|
||||
->addSwitch(new CliSwitch('bootstrap', 'b', false, 'file', 'File to include before tests are run'))
|
||||
->addSwitch(new CliSwitch('coverage-xml', null, false, 'file', 'Generate code coverage report in XML clover format (requires xdebug)'))
|
||||
//->addSwitch(new CliSwitch('coverage-xml', null, false, 'file', 'Generate code coverage report in XML clover format (requires xdebug)'))
|
||||
->addSwitch(new CliSwitch('coverage-html', null, false, 'dir', 'Generate code coverage report in HTML (requires xdebug, optionally uses ezComponents)'))
|
||||
->addSwitch(new CliSwitch('coverage-console', null, false, null, 'Generate code coverage report suitable for console viewing'));
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user