Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
13532 anikendra 1
<?php
2
/**
3
 * Generates code coverage reports in HTML from data obtained from PHPUnit
4
 *
5
 * PHP5
6
 *
7
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
8
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
9
 *
10
 * Licensed under The MIT License
11
 * For full copyright and license information, please see the LICENSE.txt
12
 * Redistributions of files must retain the above copyright notice.
13
 *
14
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
15
 * @link          http://cakephp.org CakePHP(tm) Project
16
 * @package       Cake.TestSuite.Coverage
17
 * @since         CakePHP(tm) v 2.0
18
 * @license       http://www.opensource.org/licenses/mit-license.php MIT License
19
 */
20
 
21
App::uses('BaseCoverageReport', 'TestSuite/Coverage');
22
 
23
/**
24
 * Generates code coverage reports in HTML from data obtained from PHPUnit
25
 *
26
 * @package       Cake.TestSuite.Coverage
27
 */
28
class HtmlCoverageReport extends BaseCoverageReport {
29
 
30
/**
31
 * Generates report html to display.
32
 *
33
 * @return string compiled html report.
34
 */
35
	public function report() {
36
		$pathFilter = $this->getPathFilter();
37
		$coverageData = $this->filterCoverageDataByPath($pathFilter);
38
		if (empty($coverageData)) {
39
			return '<h3>No files to generate coverage for</h3>';
40
		}
41
		$output = $this->coverageScript();
42
		$output .= <<<HTML
43
		<h3>Code coverage results
44
		<a href="#" onclick="coverage_toggle_all()" class="coverage-toggle">Toggle all files</a>
45
		</h3>
46
HTML;
47
		foreach ($coverageData as $file => $coverageData) {
48
			$fileData = file($file);
49
			$output .= $this->generateDiff($file, $fileData, $coverageData);
50
		}
51
		return $output;
52
	}
53
 
54
/**
55
 * Generates an HTML diff for $file based on $coverageData.
56
 *
57
 * Handles both PHPUnit3.5 and 3.6 formats.
58
 *
59
 * 3.5 uses -1 for uncovered, and -2 for dead.
60
 * 3.6 uses array() for uncovered and null for dead.
61
 *
62
 * @param string $filename Name of the file having coverage generated
63
 * @param array $fileLines File data as an array. See file() for how to get one of these.
64
 * @param array $coverageData Array of coverage data to use to generate HTML diffs with
65
 * @return string HTML diff.
66
 */
67
	public function generateDiff($filename, $fileLines, $coverageData) {
68
		$output = '';
69
		$diff = array();
70
 
71
		list($covered, $total) = $this->_calculateCoveredLines($fileLines, $coverageData);
72
 
73
		//shift line numbers forward one;
74
		array_unshift($fileLines, ' ');
75
		unset($fileLines[0]);
76
 
77
		foreach ($fileLines as $lineno => $line) {
78
			$class = 'ignored';
79
			$coveringTests = array();
80
			if (!empty($coverageData[$lineno]) && is_array($coverageData[$lineno])) {
81
				$coveringTests = array();
82
				foreach ($coverageData[$lineno] as $test) {
83
					$class = (is_array($test) && isset($test['id'])) ? $test['id'] : $test;
84
					$testReflection = new ReflectionClass(current(explode('::', $class)));
85
					$this->_testNames[] = $this->_guessSubjectName($testReflection);
86
					$coveringTests[] = $class;
87
				}
88
				$class = 'covered';
89
			} elseif (isset($coverageData[$lineno]) && ($coverageData[$lineno] === -1 || $coverageData[$lineno] === array())) {
90
				$class = 'uncovered';
91
			} elseif (array_key_exists($lineno, $coverageData) && ($coverageData[$lineno] === -2 || $coverageData[$lineno] === null)) {
92
				$class .= ' dead';
93
			}
94
			$diff[] = $this->_paintLine($line, $lineno, $class, $coveringTests);
95
		}
96
 
97
		$percentCovered = 100;
98
		if ($total > 0) {
99
			$percentCovered = round(100 * $covered / $total, 2);
100
		}
101
		$output .= $this->coverageHeader($filename, $percentCovered);
102
		$output .= implode("", $diff);
103
		$output .= $this->coverageFooter();
104
		return $output;
105
	}
106
 
107
/**
108
 * Guess the class name the test was for based on the test case filename.
109
 *
110
 * @param ReflectionClass $testReflection.
111
 * @return string Possible test subject name.
112
 */
113
	protected function _guessSubjectName($testReflection) {
114
		$basename = basename($testReflection->getFilename());
115
		if (strpos($basename, '.test') !== false) {
116
			list($subject, ) = explode('.', $basename, 2);
117
			return $subject;
118
		}
119
		$subject = str_replace('Test.php', '', $basename);
120
		return $subject;
121
	}
122
 
123
/**
124
 * Renders the html for a single line in the html diff.
125
 *
126
 * @param string $line
127
 * @param integer $linenumber
128
 * @param string $class
129
 * @param array $coveringTests
130
 * @return void
131
 */
132
	protected function _paintLine($line, $linenumber, $class, $coveringTests) {
133
		$coveredBy = '';
134
		if (!empty($coveringTests)) {
135
			$coveredBy = "Covered by:\n";
136
			foreach ($coveringTests as $test) {
137
				$coveredBy .= $test . "\n";
138
			}
139
		}
140
 
141
		return sprintf(
142
			'<div class="code-line %s" title="%s"><span class="line-num">%s</span><span class="content">%s</span></div>',
143
			$class,
144
			$coveredBy,
145
			$linenumber,
146
			htmlspecialchars($line)
147
		);
148
	}
149
 
150
/**
151
 * generate some javascript for the coverage report.
152
 *
153
 * @return void
154
 */
155
	public function coverageScript() {
156
		return <<<HTML
157
		<script type="text/javascript">
158
		function coverage_show_hide(selector) {
159
			var element = document.getElementById(selector);
160
			element.style.display = (element.style.display === 'none') ? '' : 'none';
161
		}
162
		function coverage_toggle_all() {
163
			var divs = document.querySelectorAll('div.coverage-container');
164
			var i = divs.length;
165
			while (i--) {
166
				if (divs[i] && divs[i].className.indexOf('primary') == -1) {
167
					divs[i].style.display = (divs[i].style.display === 'none') ? '' : 'none';
168
				}
169
			}
170
		}
171
		</script>
172
HTML;
173
	}
174
 
175
/**
176
 * Generate an HTML snippet for coverage headers
177
 *
178
 * @param string $filename
179
 * @param string $percent
180
 * @return void
181
 */
182
	public function coverageHeader($filename, $percent) {
183
		$filename = basename($filename);
184
		list($file, $ext) = explode('.', $filename);
185
		$display = in_array($file, $this->_testNames) ? 'block' : 'none';
186
		$primary = $display === 'block' ? 'primary' : '';
187
		return <<<HTML
188
	<div class="coverage-container $primary" style="display:$display;">
189
	<h4>
190
		<a href="#coverage-$filename" onclick="coverage_show_hide('coverage-$filename');">
191
			$filename Code coverage: $percent%
192
		</a>
193
	</h4>
194
	<div class="code-coverage-results" id="coverage-$filename" style="display:none;">
195
	<pre>
196
HTML;
197
	}
198
 
199
/**
200
 * Generate an HTML snippet for coverage footers
201
 *
202
 * @return void
203
 */
204
	public function coverageFooter() {
205
		return "</pre></div></div>";
206
	}
207
 
208
}