| 1 |
lars |
1 |
<?php
|
|
|
2 |
|
|
|
3 |
if (!defined('T_ML_COMMENT'))
|
|
|
4 |
define('T_ML_COMMENT', T_COMMENT);
|
|
|
5 |
else
|
|
|
6 |
define('T_DOC_COMMENT', T_ML_COMMENT);
|
|
|
7 |
|
|
|
8 |
class HtmlReporterWithCoverage extends HtmlReporter
|
|
|
9 |
{
|
|
|
10 |
protected $coverage = array();
|
|
|
11 |
|
|
|
12 |
protected $painter;
|
|
|
13 |
|
|
|
14 |
protected $base_dir;
|
|
|
15 |
|
|
|
16 |
function __construct($painter = 'index.php', $base_dir)
|
|
|
17 |
{
|
|
|
18 |
$this->painter = $painter;
|
|
|
19 |
$this->base_dir = $base_dir;
|
|
|
20 |
}
|
|
|
21 |
|
|
|
22 |
function paintHeader($test_name, $charset="UTF-8")
|
|
|
23 |
{
|
|
|
24 |
$this->sendNoCacheHeaders();
|
|
|
25 |
header('Content-Type: text/html; Charset='.$charset);
|
|
|
26 |
print "<html>\n<head>\n<title>$test_name</title>\n";
|
|
|
27 |
print "<meta http-equiv=\"Content-Type\" content=\"text/html; charset={$charset}\"/>";
|
|
|
28 |
print "<style type=\"text/css\">\n";
|
|
|
29 |
print $this->_getCss() . "\n";
|
|
|
30 |
print "</style>\n";
|
|
|
31 |
print "</head>\n<body>\n";
|
|
|
32 |
print "<h1>$test_name</h1>\n";
|
|
|
33 |
flush();
|
|
|
34 |
|
|
|
35 |
if (extension_loaded('xdebug'))
|
|
|
36 |
xdebug_start_code_coverage(XDEBUG_CC_UNUSED);
|
|
|
37 |
|
|
|
38 |
}
|
|
|
39 |
|
|
|
40 |
/**
|
|
|
41 |
*
|
|
|
42 |
*/
|
|
|
43 |
function _getCss()
|
|
|
44 |
{
|
|
|
45 |
$contents = parent::_getCss()."\n ";
|
|
|
46 |
$contents .= '
|
|
|
47 |
.bar { float: left; display: inline; border: 1px solid #eee; width: 300px; white-space: nowrap;}
|
|
|
48 |
.percentage { float: left; background-color: #eef; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 0.65em; padding: 5px; margin-right: }
|
|
|
49 |
.coverage {margin: 0.4em; }
|
|
|
50 |
.coverage a {
|
|
|
51 |
padding-left: 0.5em;
|
|
|
52 |
}
|
|
|
53 |
.coverage:after {
|
|
|
54 |
content: ".";
|
|
|
55 |
display: block;
|
|
|
56 |
height: 0;
|
|
|
57 |
clear: both;
|
|
|
58 |
visibility: hidden;
|
|
|
59 |
}
|
|
|
60 |
.coverage {display: inline-block;}
|
|
|
61 |
/* Hides from IE-mac \*/
|
|
|
62 |
* html .coverage {height: 1%;}
|
|
|
63 |
.coverage {display: block;}
|
|
|
64 |
/* End hide from IE-mac */
|
|
|
65 |
';
|
|
|
66 |
Return $contents;
|
|
|
67 |
}
|
|
|
68 |
|
|
|
69 |
function paintFooter($test_name)
|
|
|
70 |
{
|
|
|
71 |
if (extension_loaded('xdebug'))
|
|
|
72 |
{
|
|
|
73 |
$this->coverage = xdebug_get_code_coverage();
|
|
|
74 |
xdebug_stop_code_coverage();
|
|
|
75 |
}
|
|
|
76 |
|
|
|
77 |
$colour = ($this->getFailCount() + $this->getExceptionCount() > 0 ? "red" : "green");
|
|
|
78 |
print "<div style=\"";
|
|
|
79 |
print "padding: 8px; margin-top: 1em; background-color: $colour; color: white;";
|
|
|
80 |
print "\">";
|
|
|
81 |
print $this->getTestCaseProgress() . "/" . $this->getTestCaseCount();
|
|
|
82 |
print " test cases complete:\n";
|
|
|
83 |
print "<strong>" . $this->getPassCount() . "</strong> passes, ";
|
|
|
84 |
print "<strong>" . $this->getFailCount() . "</strong> fails and ";
|
|
|
85 |
print "<strong>" . $this->getExceptionCount() . "</strong> exceptions.";
|
|
|
86 |
print "</div>\n";
|
|
|
87 |
$this->paintCoverage();
|
|
|
88 |
print "</body>\n</html>\n";
|
|
|
89 |
}
|
|
|
90 |
|
|
|
91 |
function paintCoverage()
|
|
|
92 |
{
|
|
|
93 |
$dir = dirname(__FILE__);
|
|
|
94 |
if(count($this->coverage) > 0)
|
|
|
95 |
print '<h2>Code Coverage</h2>';
|
|
|
96 |
|
|
|
97 |
|
|
|
98 |
ksort($this->coverage);
|
|
|
99 |
|
|
|
100 |
$details = array();
|
|
|
101 |
foreach($this->coverage as $file => $coverage)
|
|
|
102 |
{
|
|
|
103 |
if(is_int(strpos($file, $dir)) == false
|
|
|
104 |
&& is_int(strpos($file, 'simpletest')) == false
|
|
|
105 |
&& is_int(strpos($file, $this->base_dir)))
|
|
|
106 |
{
|
|
|
107 |
$total = HTMLCoverageReport::codelines($file);
|
|
|
108 |
$executed = count($coverage);
|
|
|
109 |
$percentage = sprintf('%01d',$executed/$total*100);
|
|
|
110 |
$width = $percentage * 3;
|
|
|
111 |
$filename = str_replace($this->base_dir, '',$file);
|
|
|
112 |
$link = $this->constructURL($filename, $coverage);
|
|
|
113 |
|
|
|
114 |
$detail['total'] = $total;
|
|
|
115 |
$detail['executed'] = $executed;
|
|
|
116 |
$detail['width'] = $width;
|
|
|
117 |
$detail['filename'] = $filename;
|
|
|
118 |
$detail['link'] = $link;
|
|
|
119 |
$details[$percentage][] = $detail;
|
|
|
120 |
}
|
|
|
121 |
}
|
|
|
122 |
krsort($details);
|
|
|
123 |
foreach($details as $percentage => $files)
|
|
|
124 |
{
|
|
|
125 |
foreach($files as $detail)
|
|
|
126 |
{
|
|
|
127 |
$total = $detail['total'];
|
|
|
128 |
$executed = $detail['executed'];
|
|
|
129 |
$width = $detail['width'];
|
|
|
130 |
$filename = $detail['filename'];
|
|
|
131 |
$link = $detail['link'];
|
|
|
132 |
|
|
|
133 |
print "<div class=\"coverage\">";
|
|
|
134 |
print "<span class=\"bar\">";
|
|
|
135 |
print "<span class=\"percentage\" style=\"width:{$width}px\">";
|
|
|
136 |
print "$executed/$total\n";
|
|
|
137 |
print "$percentage%</span></span>\n";
|
|
|
138 |
print "<a href=\"{$link}\">{$filename}</a>\n";
|
|
|
139 |
print "</div>\n";
|
|
|
140 |
}
|
|
|
141 |
}
|
|
|
142 |
}
|
|
|
143 |
|
|
|
144 |
function constructURL($file, $coverage)
|
|
|
145 |
{
|
|
|
146 |
$file = rawurlencode($file);
|
|
|
147 |
$lines = implode(',', array_keys($coverage));
|
|
|
148 |
return $this->painter.'?file='.$file.'&lines='.$lines;
|
|
|
149 |
}
|
|
|
150 |
}
|
|
|
151 |
|
|
|
152 |
|
|
|
153 |
class HTMLCoverageReport extends HtmlReporter
|
|
|
154 |
{
|
|
|
155 |
protected $file;
|
|
|
156 |
protected $lines;
|
|
|
157 |
protected $name;
|
|
|
158 |
|
|
|
159 |
function __construct($file, $name, $lines)
|
|
|
160 |
{
|
|
|
161 |
$this->file = $file;
|
|
|
162 |
$this->lines = $lines;
|
|
|
163 |
$this->name = $name;
|
|
|
164 |
}
|
|
|
165 |
|
|
|
166 |
function show()
|
|
|
167 |
{
|
|
|
168 |
$this->paintHeader($this->name);
|
|
|
169 |
|
|
|
170 |
$contents = file($this->file);
|
|
|
171 |
foreach($contents as $count => $line)
|
|
|
172 |
{
|
|
|
173 |
$num = ($count+1);
|
|
|
174 |
$line = preg_replace("/\\n|\\r/",'',$line);
|
|
|
175 |
$line = htmlspecialchars($line);
|
|
|
176 |
$line = str_replace(' ',' ',$line);
|
|
|
177 |
$line = str_replace("\t",' ',$line);
|
|
|
178 |
if(in_array($count+1, $this->lines))
|
|
|
179 |
echo "<div class=\"highlight\"><tt>$num $line</tt></div>\n";
|
|
|
180 |
else
|
|
|
181 |
echo "<tt>$num $line</tt><br />\n";
|
|
|
182 |
}
|
|
|
183 |
|
|
|
184 |
$this->paintFooter();
|
|
|
185 |
}
|
|
|
186 |
|
|
|
187 |
function paintHeader($file, $charset="UTF-8")
|
|
|
188 |
{
|
|
|
189 |
$total = $this->codelines($this->file);
|
|
|
190 |
$executed = count($this->lines);
|
|
|
191 |
$percentage = sprintf('%01.2f',$executed/$total*100);
|
|
|
192 |
|
|
|
193 |
$this->sendNoCacheHeaders();
|
|
|
194 |
header('Content-Type: text/html Charset='.$charset);
|
|
|
195 |
print "<html>\n<head>\n<title>Code Coverage: $file</title>\n";
|
|
|
196 |
print "<meta http-equiv=\"Content-Type\" content=\"text/html; charset={$charset}\"/>";
|
|
|
197 |
print "<style type=\"text/css\">\n";
|
|
|
198 |
print $this->_getCss() . "\n";
|
|
|
199 |
print ".highlight { background-color: #eef; } \n";
|
|
|
200 |
print ".filename { margin-bottom: 2em; } \n";
|
|
|
201 |
print "</style>\n";
|
|
|
202 |
print "</head>\n<body>\n";
|
|
|
203 |
print "<h1>Code Coverage</h1>\n";
|
|
|
204 |
print "<div class=\"filename\"><strong>$file</strong></div>";
|
|
|
205 |
print "<div class=\"filename\"><tt> Total code lines: {$total} <br /> Total lines executed: {$executed} ({$percentage}%)</tt></div>";
|
|
|
206 |
flush();
|
|
|
207 |
}
|
|
|
208 |
|
|
|
209 |
function paintFooter($test_name)
|
|
|
210 |
{
|
|
|
211 |
print "</body>\n</html>\n";
|
|
|
212 |
}
|
|
|
213 |
|
|
|
214 |
static function codelines($file)
|
|
|
215 |
{
|
|
|
216 |
$source = file_get_contents($file);
|
|
|
217 |
$tokens = @token_get_all($source);
|
|
|
218 |
|
|
|
219 |
$lines = '';
|
|
|
220 |
|
|
|
221 |
foreach ($tokens as $token)
|
|
|
222 |
{
|
|
|
223 |
if (is_string($token))
|
|
|
224 |
{
|
|
|
225 |
// simple 1-character token
|
|
|
226 |
$lines .= $token;
|
|
|
227 |
}
|
|
|
228 |
else
|
|
|
229 |
{
|
|
|
230 |
// token array
|
|
|
231 |
list($id, $text) = $token;
|
|
|
232 |
|
|
|
233 |
switch ($id)
|
|
|
234 |
{
|
|
|
235 |
case T_COMMENT:
|
|
|
236 |
case T_ML_COMMENT: // we've defined this
|
|
|
237 |
case T_DOC_COMMENT: // and this
|
|
|
238 |
// no action on comments
|
|
|
239 |
break;
|
|
|
240 |
|
|
|
241 |
default:
|
|
|
242 |
// anything else -> output "as is"
|
|
|
243 |
//echo $text;
|
|
|
244 |
$lines .= $text;
|
|
|
245 |
break;
|
|
|
246 |
}
|
|
|
247 |
}
|
|
|
248 |
}
|
|
|
249 |
|
|
|
250 |
$lines = preg_replace('/\\n\s*$/m',"",$lines);
|
|
|
251 |
$codelines = explode("\n",$lines);
|
|
|
252 |
$count = 0;
|
|
|
253 |
$patterns[] = '^\s*{\s*$';
|
|
|
254 |
$patterns[] = '<\?';
|
|
|
255 |
$patterns[] = '^\s*(private|protected|public)\s+\$';
|
|
|
256 |
$pattern = '/'.implode('|', $patterns).'/';
|
|
|
257 |
foreach($codelines as $line)
|
|
|
258 |
{
|
|
|
259 |
if(!preg_match($pattern, $line))
|
|
|
260 |
$count++;
|
|
|
261 |
}
|
|
|
262 |
return $count;
|
|
|
263 |
//var_dump($codelines);
|
|
|
264 |
//return count($codelines);
|
|
|
265 |
}
|
|
|
266 |
}
|
|
|
267 |
|
|
|
268 |
?>
|