Subversion-Projekte lars-tiefland.cakephp

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/* SVN FILE: $Id: code_coverage_manager.php 7945 2008-12-19 02:16:01Z gwoo $ */
3
/**
4
 * A class to manage all aspects for Code Coverage Analysis
5
 *
6
 * This class
7
 *
8
 * PHP versions 4 and 5
9
 *
10
 * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite>
11
 * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
12
 *
13
 *  Licensed under The Open Group Test Suite License
14
 *  Redistributions of files must retain the above copyright notice.
15
 *
16
 * @filesource
17
 * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
18
 * @link          https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests
19
 * @package       cake
20
 * @subpackage    cake.cake.tests.lib
21
 * @since         CakePHP(tm) v 1.2.0.4433
22
 * @version       $Revision: 7945 $
23
 * @modifiedby    $LastChangedBy: gwoo $
24
 * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
25
 * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
26
 */
27
App::import('Core', 'Folder');
28
/**
29
 * Short description for class.
30
 *
31
 * @package       cake
32
 * @subpackage    cake.cake.tests.lib
33
 */
34
class CodeCoverageManager {
35
/**
36
 * Is this an app test case?
37
 *
38
 * @var string
39
 */
40
	var $appTest = false;
41
/**
42
 * Is this an app test case?
43
 *
44
 * @var string
45
 */
46
	var $pluginTest = false;
47
/**
48
 * Is this a grouptest?
49
 *
50
 * @var string
51
 * @access public
52
 */
53
	var $groupTest = false;
54
/**
55
 * The test case file to analyze
56
 *
57
 * @var string
58
 */
59
	var $testCaseFile = '';
60
/**
61
 * The currently used CakeTestReporter
62
 *
63
 * @var string
64
 */
65
	var $reporter = '';
66
/**
67
 * undocumented variable
68
 *
69
 * @var string
70
 */
71
	var $numDiffContextLines = 7;
72
/**
73
 * Returns a singleton instance
74
 *
75
 * @return object
76
 * @access public
77
 */
78
	function &getInstance() {
79
		static $instance = array();
80
		if (!$instance) {
81
			$instance[0] =& new CodeCoverageManager();
82
		}
83
		return $instance[0];
84
	}
85
/**
86
 * Starts a new Coverage Analyzation for a given test case file
87
 * @TODO: Works with $_GET now within the function body, which will make it hard when we do code coverage reports for CLI
88
 *
89
 * @param string $testCaseFile
90
 * @param string $reporter
91
 * @return void
92
 */
93
	function start($testCaseFile, &$reporter) {
94
		$manager =& CodeCoverageManager::getInstance();
95
		$manager->reporter = $reporter;
96
		$testCaseFile = str_replace(DS . DS, DS, $testCaseFile);
97
		$thisFile = str_replace('.php', '.test.php', basename(__FILE__));
98
 
99
		if (strpos($testCaseFile, $thisFile) !== false) {
100
			trigger_error('Xdebug supports no parallel coverage analysis - so this is not possible.', E_USER_ERROR);
101
		}
102
 
103
		if (isset($_GET['app'])) {
104
			$manager->appTest = true;
105
		}
106
 
107
		if (isset($_GET['group'])) {
108
			$manager->groupTest = true;
109
		}
110
 
111
		if (isset($_GET['plugin'])) {
112
			$manager->pluginTest = Inflector::underscore($_GET['plugin']);
113
		}
114
		$manager->testCaseFile = $testCaseFile;
115
		xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);
116
	}
117
/**
118
 * Stops the current code coverage analyzation and dumps a nice report depending on the reporter that was passed to start()
119
 *
120
 * @return void
121
 */
122
	function report($output = true) {
123
		$manager =& CodeCoverageManager::getInstance();
124
 
125
		if (!$manager->groupTest) {
126
			$testObjectFile = $manager->__testObjectFileFromCaseFile($manager->testCaseFile, $manager->appTest);
127
 
128
			if (!file_exists($testObjectFile)) {
129
				trigger_error('This test object file is invalid: ' . $testObjectFile);
130
				return ;
131
			}
132
			$dump = xdebug_get_code_coverage();
133
			xdebug_stop_code_coverage();
134
			$coverageData = array();
135
 
136
			foreach ($dump as $file => $data) {
137
				if ($file == $testObjectFile) {
138
					$coverageData = $data;
139
					break;
140
				}
141
			}
142
 
143
			if (empty($coverageData) && $output) {
144
				echo 'The test object file is never loaded.';
145
			}
146
			$execCodeLines = $manager->__getExecutableLines(file_get_contents($testObjectFile));
147
			$result = '';
148
 
149
			switch (get_class($manager->reporter)) {
150
				case 'CakeHtmlReporter':
151
					$result = $manager->reportCaseHtmlDiff(@file($testObjectFile), $coverageData, $execCodeLines, $manager->numDiffContextLines);
152
					break;
153
				case 'CLIReporter':
154
					$result = $manager->reportCaseCli(@file($testObjectFile), $coverageData, $execCodeLines, $manager->numDiffContextLines);
155
					break;
156
				default:
157
					trigger_error('Currently only HTML and CLI reporting is supported for code coverage analysis.');
158
					break;
159
			}
160
		} else {
161
			$testObjectFiles = $manager->__testObjectFilesFromGroupFile($manager->testCaseFile, $manager->appTest);
162
 
163
			foreach ($testObjectFiles as $file) {
164
				if (!file_exists($file)) {
165
					trigger_error('This test object file is invalid: ' . $file);
166
					return ;
167
				}
168
			}
169
			$dump = xdebug_get_code_coverage();
170
			xdebug_stop_code_coverage();
171
			$coverageData = array();
172
			foreach ($dump as $file => $data) {
173
				if (in_array($file, $testObjectFiles)) {
174
					$coverageData[$file] = $data;
175
				}
176
			}
177
 
178
			if (empty($coverageData) && $output) {
179
				echo 'The test object files are never loaded.';
180
			}
181
			$execCodeLines = $manager->__getExecutableLines($testObjectFiles);
182
			$result = '';
183
 
184
			switch (get_class($manager->reporter)) {
185
				case 'CakeHtmlReporter':
186
					$result = $manager->reportGroupHtml($testObjectFiles, $coverageData, $execCodeLines, $manager->numDiffContextLines);
187
					break;
188
				case 'CLIReporter':
189
					$result = $manager->reportGroupCli($testObjectFiles, $coverageData, $execCodeLines, $manager->numDiffContextLines);
190
					break;
191
				default:
192
					trigger_error('Currently only HTML and CLI reporting is supported for code coverage analysis.');
193
					break;
194
			}
195
		}
196
 
197
		if ($output) {
198
			echo $result;
199
		}
200
	}
201
/**
202
 * Html reporting
203
 *
204
 * @param string $testObjectFile
205
 * @param string $coverageData
206
 * @param string $execCodeLines
207
 * @param string $output
208
 * @return void
209
 */
210
	function reportCaseHtml($testObjectFile, $coverageData, $execCodeLines) {
211
		$manager = CodeCoverageManager::getInstance();
212
		$lineCount = $coveredCount = 0;
213
		$report = '';
214
 
215
		foreach ($testObjectFile as $num => $line) {
216
			$num++;
217
			$foundByManualFinder = isset($execCodeLines[$num]) && trim($execCodeLines[$num]) != '';
218
			$foundByXdebug = isset($coverageData[$num]) && $coverageData[$num] !== -2;
219
 
220
			// xdebug does not find all executable lines (zend engine fault)
221
			if ($foundByManualFinder && $foundByXdebug) {
222
				$class = 'uncovered';
223
				$lineCount++;
224
 
225
				if ($coverageData[$num] > 0) {
226
					$class = 'covered';
227
					$coveredCount++;
228
				}
229
			} else {
230
				$class = 'ignored';
231
			}
232
			$report .= $manager->__paintCodeline($class, $num, $line);;
233
		}
234
		return $manager->__paintHeader($lineCount, $coveredCount, $report);
235
	}
236
/**
237
 * Diff reporting
238
 *
239
 * @param string $testObjectFile
240
 * @param string $coverageData
241
 * @param string $execCodeLines
242
 * @param string $output
243
 * @return void
244
 */
245
	function reportCaseHtmlDiff($testObjectFile, $coverageData, $execCodeLines, $numContextLines) {
246
		$manager = CodeCoverageManager::getInstance();
247
		$total = count($testObjectFile);
248
		$lines = array();
249
 
250
		for ($i = 1; $i < $total + 1; $i++) {
251
			$foundByManualFinder = isset($execCodeLines[$i]) && trim($execCodeLines[$i]) != '';
252
			$foundByXdebug = isset($coverageData[$i]);
253
 
254
			if (!$foundByManualFinder || !$foundByXdebug || $coverageData[$i] === -2) {
255
				if (isset($lines[$i])) {
256
					$lines[$i] = 'ignored ' . $lines[$i];
257
				} else {
258
					$lines[$i] = 'ignored';
259
				}
260
				continue;
261
			}
262
 
263
			if ($coverageData[$i] !== -1) {
264
				if (isset($lines[$i])) {
265
					$lines[$i] = 'covered ' . $lines[$i];
266
				} else {
267
					$lines[$i] = 'covered';
268
				}
269
				continue;
270
			}
271
			$lines[$i] = 'uncovered show';
272
			$foundEndBlockInContextSearch = false;
273
 
274
			for ($j = 1; $j <= $numContextLines; $j++) {
275
				$key = $i - $j;
276
 
277
				if ($key > 0 && isset($lines[$key])) {
278
					if (strpos($lines[$key], 'end') !== false) {
279
						$foundEndBlockInContextSearch = true;
280
						if ($j < $numContextLines) {
281
							$lines[$key] = str_replace('end', '', $lines[$key-1]);
282
						}
283
					}
284
 
285
					if (strpos($lines[$key], 'uncovered') === false) {
286
						if (strpos($lines[$key], 'covered') !== false) {
287
							$lines[$key] .= ' show';
288
						} else {
289
							$lines[$key] = 'ignored show';
290
						}
291
					}
292
 
293
					if ($j == $numContextLines) {
294
						$lineBeforeIsEndBlock = strpos($lines[$key-1], 'end') !== false;
295
						$lineBeforeIsShown = strpos($lines[$key-1], 'show') !== false;
296
						$lineBeforeIsUncovered = strpos($lines[$key-1], 'uncovered') !== false;
297
 
298
						if (!$foundEndBlockInContextSearch && !$lineBeforeIsUncovered && ($lineBeforeIsEndBlock)) {
299
							$lines[$key-1] = str_replace('end', '', $lines[$key-1]);
300
						}
301
 
302
						if (!$lineBeforeIsShown && !$lineBeforeIsUncovered) {
303
							$lines[$key] .= ' start';
304
						}
305
					}
306
				}
307
				$key = $i + $j;
308
 
309
				if ($key < $total) {
310
					$lines[$key] = 'show';
311
 
312
					if ($j == $numContextLines) {
313
						$lines[$key] .= ' end';
314
					}
315
				}
316
			}
317
		}
318
 
319
		// find the last "uncovered" or "show"n line and "end" its block
320
		$lastShownLine = $manager->__array_strpos($lines, 'show', true);
321
		if (isset($lines[$lastShownLine])) {
322
			$lines[$lastShownLine] .= ' end';
323
		}
324
 
325
		// give the first start line another class so we can control the top padding of the entire results
326
		$firstShownLine = $manager->__array_strpos($lines, 'show');
327
		if (isset($lines[$firstShownLine])) {
328
			$lines[$firstShownLine] .= ' realstart';
329
		}
330
 
331
		// get the output
332
		$lineCount = $coveredCount = 0;
333
		$report = '';
334
		foreach ($testObjectFile as $num => $line) {
335
			// start line count at 1
336
			$num++;
337
			$class = $lines[$num];
338
 
339
			if (strpos($class, 'ignored') === false) {
340
				$lineCount++;
341
 
342
				if (strpos($class, 'covered') !== false && strpos($class, 'uncovered') === false) {
343
					$coveredCount++;
344
				}
345
			}
346
 
347
			if (strpos($class, 'show') !== false) {
348
				$report .= $manager->__paintCodeline($class, $num, $line);
349
			}
350
		}
351
		return $manager->__paintHeader($lineCount, $coveredCount, $report);
352
	}
353
/**
354
 * CLI reporting
355
 *
356
 * @param string $testObjectFile
357
 * @param string $coverageData
358
 * @param string $execCodeLines
359
 * @param string $output
360
 * @return void
361
 */
362
	function reportCaseCli($testObjectFile, $coverageData, $execCodeLines) {
363
		$manager = CodeCoverageManager::getInstance();
364
		$lineCount = $coveredCount = 0;
365
		$report = '';
366
 
367
		foreach ($testObjectFile as $num => $line) {
368
			$num++;
369
			$foundByManualFinder = isset($execCodeLines[$num]) && trim($execCodeLines[$num]) != '';
370
			$foundByXdebug = isset($coverageData[$num]) && $coverageData[$num] !== -2;
371
 
372
			if ($foundByManualFinder && $foundByXdebug) {
373
				$lineCount++;
374
 
375
				if ($coverageData[$num] > 0) {
376
					$coveredCount++;
377
				}
378
			}
379
		}
380
		return $manager->__paintHeaderCli($lineCount, $coveredCount, $report);
381
	}
382
/**
383
 * Diff reporting
384
 *
385
 * @param string $testObjectFile
386
 * @param string $coverageData
387
 * @param string $execCodeLines
388
 * @param string $output
389
 * @return void
390
 */
391
	function reportGroupHtml($testObjectFiles, $coverageData, $execCodeLines, $numContextLines) {
392
		$manager = CodeCoverageManager::getInstance();
393
		$report = '';
394
 
395
		foreach ($testObjectFiles as $testObjectFile) {
396
			$lineCount = $coveredCount = 0;
397
			$objFilename = $testObjectFile;
398
			$testObjectFile = file($testObjectFile);
399
 
400
			foreach ($testObjectFile as $num => $line) {
401
				$num++;
402
				$foundByManualFinder = isset($execCodeLines[$objFilename][$num]) && trim($execCodeLines[$objFilename][$num]) != '';
403
				$foundByXdebug = isset($coverageData[$objFilename][$num]) && $coverageData[$objFilename][$num] !== -2;
404
 
405
				if ($foundByManualFinder && $foundByXdebug) {
406
					$class = 'uncovered';
407
					$lineCount++;
408
 
409
					if ($coverageData[$objFilename][$num] > 0) {
410
						$class = 'covered';
411
						$coveredCount++;
412
					}
413
				} else {
414
					$class = 'ignored';
415
				}
416
			}
417
			$report .= $manager->__paintGroupResultLine($objFilename, $lineCount, $coveredCount);
418
		}
419
		return $manager->__paintGroupResultHeader($report);
420
	}
421
/**
422
 * CLI reporting
423
 *
424
 * @param string $testObjectFile
425
 * @param string $coverageData
426
 * @param string $execCodeLines
427
 * @param string $output
428
 * @return void
429
 */
430
	function reportGroupCli($testObjectFiles, $coverageData, $execCodeLines) {
431
		$manager = CodeCoverageManager::getInstance();
432
		$report = '';
433
 
434
		foreach ($testObjectFiles as $testObjectFile) {
435
			$lineCount = $coveredCount = 0;
436
			$objFilename = $testObjectFile;
437
			$testObjectFile = file($testObjectFile);
438
 
439
			foreach ($testObjectFile as $num => $line) {
440
				$num++;
441
				$foundByManualFinder = isset($execCodeLines[$objFilename][$num]) && trim($execCodeLines[$objFilename][$num]) != '';
442
				$foundByXdebug = isset($coverageData[$objFilename][$num]) && $coverageData[$objFilename][$num] !== -2;
443
 
444
				if ($foundByManualFinder && $foundByXdebug) {
445
					$lineCount++;
446
 
447
					if ($coverageData[$objFilename][$num] > 0) {
448
						$coveredCount++;
449
					}
450
				}
451
			}
452
			$report .= $manager->__paintGroupResultLineCli($objFilename, $lineCount, $coveredCount);
453
		}
454
		return $report;
455
	}
456
/**
457
 * Returns the name of the test object file based on a given test case file name
458
 *
459
 * @param string $file
460
 * @param string $isApp
461
 * @return string name of the test object file
462
 * @access private
463
 */
464
	function __testObjectFileFromCaseFile($file, $isApp = true) {
465
		$manager = CodeCoverageManager::getInstance();
466
		$path = $manager->__getTestFilesPath($isApp);
467
		$folderPrefixMap = array(
468
			'behaviors' => 'models',
469
			'components' => 'controllers',
470
			'helpers' => 'views'
471
		);
472
 
473
		foreach ($folderPrefixMap as $dir => $prefix) {
474
			if (strpos($file, $dir) === 0) {
475
				$path .= $prefix . DS;
476
				break;
477
			}
478
		}
479
		$testManager =& new TestManager();
480
		$testFile = str_replace(array('/', $testManager->_testExtension), array(DS, '.php'), $file);
481
 
482
		$folder =& new Folder();
483
		$folder->cd(ROOT . DS . CAKE_TESTS_LIB);
484
		$contents = $folder->ls();
485
 
486
		if (in_array(basename($testFile), $contents[1])) {
487
			$testFile = basename($testFile);
488
			$path = ROOT . DS . CAKE_TESTS_LIB;
489
		}
490
		$path .= $testFile;
491
		$realpath = realpath($path);
492
 
493
		if ($realpath) {
494
			return $realpath;
495
		}
496
		return $path;
497
	}
498
/**
499
 * Returns an array of names of the test object files based on a given test group file name
500
 *
501
 * @param array $files
502
 * @param string $isApp
503
 * @return array names of the test object files
504
 * @access private
505
 */
506
	function __testObjectFilesFromGroupFile($groupFile, $isApp = true) {
507
		$manager = CodeCoverageManager::getInstance();
508
		$testManager =& new TestManager();
509
 
510
		$path = TESTS . 'groups';
511
 
512
		if (!$isApp) {
513
			$path = ROOT . DS . 'cake' . DS . 'tests' . DS . 'groups';
514
		}
515
 
516
		if (!!$manager->pluginTest) {
517
			$path = APP . 'plugins' . DS . $manager->pluginTest . DS . 'tests' . DS . 'groups';
518
 
519
			$pluginPaths = Configure::read('pluginPaths');
520
			foreach ($pluginPaths as $pluginPath) {
521
				$tmpPath = $pluginPath . $manager->pluginTest . DS . 'tests' . DS. 'groups';
522
				if (file_exists($tmpPath)) {
523
					$path = $tmpPath;
524
					break;
525
				}
526
			}
527
		}
528
		$path .= DS . $groupFile . $testManager->_groupExtension;
529
 
530
		if (!file_exists($path)) {
531
			trigger_error('This group file does not exist!');
532
			return array();
533
		}
534
 
535
		$result = array();
536
		$groupContent = file_get_contents($path);
537
		$ds = '\s*\.\s*DS\s*\.\s*';
538
		$pluginTest = 'APP\.\'plugins\'' . $ds . '\'' . $manager->pluginTest . '\'' . $ds . '\'tests\'' . $ds . '\'cases\'';
539
		$pattern = '/\s*TestManager::addTestFile\(\s*\$this,\s*(' . $pluginTest . '|APP_TEST_CASES|CORE_TEST_CASES)' . $ds . '(.*?)\)/i';
540
		preg_match_all($pattern, $groupContent, $matches);
541
 
542
		foreach ($matches[2] as $file) {
543
			$patterns = array(
544
				'/\s*\.\s*DS\s*\.\s*/',
545
				'/\s*APP_TEST_CASES\s*/',
546
				'/\s*CORE_TEST_CASES\s*/',
547
			);
548
 
549
			$replacements = array(DS, '', '');
550
			$file = preg_replace($patterns, $replacements, $file);
551
			$file = str_replace("'", '', $file);
552
			$result[] = $manager->__testObjectFileFromCaseFile($file, $isApp) . '.php';
553
		}
554
		return $result;
555
	}
556
/**
557
 * Parses a given code string into an array of lines and replaces some non-executable code lines with the needed
558
 * amount of new lines in order for the code line numbers to stay in sync
559
 *
560
 * @param string $content
561
 * @return array array of lines
562
 * @access private
563
 */
564
	function __getExecutableLines($content) {
565
		if (is_array($content)) {
566
			$manager = CodeCoverageManager::getInstance();
567
			$result = array();
568
			foreach ($content as $file) {
569
				$result[$file] = $manager->__getExecutableLines(file_get_contents($file));
570
			}
571
			return $result;
572
		}
573
		$content = h($content);
574
		// arrays are 0-indexed, but we want 1-indexed stuff now as we are talking code lines mind you (**)
575
		$content = "\n" . $content;
576
		// // strip unwanted lines
577
		$content = preg_replace_callback("/(@codeCoverageIgnoreStart.*?@codeCoverageIgnoreEnd)/is", array('CodeCoverageManager', '__replaceWithNewlines'), $content);
578
		// strip php | ?\> tag only lines
579
		$content = preg_replace('/[ |\t]*[&lt;\?php|\?&gt;]+[ |\t]*/', '', $content);
580
 
581
		// strip lines that contain only braces and parenthesis
582
		$content = preg_replace('/[ |\t]*[{|}|\(|\)]+[ |\t]*/', '', $content);
583
		$result = explode("\n", $content);
584
		// unset the zero line again to get the original line numbers, but starting at 1, see (**)
585
		unset($result[0]);
586
		return $result;
587
	}
588
/**
589
 * Replaces a given arg with the number of newlines in it
590
 *
591
 * @return string the number of newlines in a given arg
592
 * @access private
593
 */
594
	function __replaceWithNewlines() {
595
		$args = func_get_args();
596
		$numLineBreaks = count(explode("\n", $args[0][0]));
597
		return str_pad('', $numLineBreaks - 1, "\n");
598
	}
599
/**
600
 * Paints the headline for code coverage analysis
601
 *
602
 * @param string $codeCoverage
603
 * @param string $report
604
 * @return void
605
 * @access private
606
 */
607
	function __paintHeader($lineCount, $coveredCount, $report) {
608
		$manager =& CodeCoverageManager::getInstance();
609
		$codeCoverage = $manager->__calcCoverage($lineCount, $coveredCount);
610
		return $report = '<h2>Code Coverage: ' . $codeCoverage . '%</h2>
611
						<div class="code-coverage-results"><pre>' . $report . '</pre></div>';
612
	}
613
/**
614
 * Displays a notification concerning group test results
615
 *
616
 * @return void
617
 * @access public
618
 */
619
	function __paintGroupResultHeader($report) {
620
		return '<div class="code-coverage-results"><p class="note">Please keep in mind that the coverage can vary a little bit depending on how much the different tests in the group interfere. If for example, TEST A calls a line from TEST OBJECT B, the coverage for TEST OBJECT B will be a little greater than if you were running the corresponding test case for TEST OBJECT B alone.</p><pre>' . $report . '</pre></div>';
621
	}
622
/**
623
 * Paints the headline for code coverage analysis
624
 *
625
 * @param string $codeCoverage
626
 * @param string $report
627
 * @return void
628
 * @access private
629
 */
630
	function __paintGroupResultLine($file, $lineCount, $coveredCount) {
631
		$manager =& CodeCoverageManager::getInstance();
632
		$codeCoverage = $manager->__calcCoverage($lineCount, $coveredCount);
633
		$class = 'result-bad';
634
 
635
		if ($codeCoverage > 50) {
636
			$class = 'result-ok';
637
		}
638
		if ($codeCoverage > 80) {
639
			$class = 'result-good';
640
		}
641
		return '<p>Code Coverage for ' . $file . ': <span class="' . $class . '">' . $codeCoverage . '%</span></p>';
642
	}
643
/**
644
 * Paints the headline for code coverage analysis
645
 *
646
 * @param string $codeCoverage
647
 * @param string $report
648
 * @return void
649
 * @access private
650
 */
651
	function __paintGroupResultLineCli($file, $lineCount, $coveredCount) {
652
		$manager =& CodeCoverageManager::getInstance();
653
		$codeCoverage = $manager->__calcCoverage($lineCount, $coveredCount);
654
		$class = 'bad';
655
 
656
		if ($codeCoverage > 50) {
657
			$class = 'ok';
658
		}
659
		if ($codeCoverage > 80) {
660
			$class = 'good';
661
		}
662
		return "\n" . 'Code Coverage for ' . $file . ': ' . $codeCoverage . '% (' . $class . ')' . "\n";
663
	}
664
/**
665
 * Paints the headline for code coverage analysis in the CLI
666
 *
667
 * @param string $codeCoverage
668
 * @param string $report
669
 * @return void
670
 * @access private
671
 */
672
	function __paintHeaderCli($lineCount, $coveredCount, $report) {
673
		$manager =& CodeCoverageManager::getInstance();
674
		$codeCoverage = $manager->__calcCoverage($lineCount, $coveredCount);
675
		return $report = 'Code Coverage: ' . $codeCoverage . '%';
676
	}
677
/**
678
 * Paints a code line for html output
679
 *
680
 * @package       default
681
 * @access private
682
 */
683
	function __paintCodeline($class, $num, $line) {
684
		$line = h($line);
685
 
686
		if (trim($line) == '') {
687
			$line = '&nbsp;'; // Win IE fix
688
		}
689
		return '<div class="code-line ' . trim($class) . '"><span class="line-num">' . $num . '</span><span class="content">' . $line . '</span></div>';
690
	}
691
/**
692
 * Calculates the coverage percentage based on a line count and a covered line count
693
 *
694
 * @param string $lineCount
695
 * @param string $coveredCount
696
 * @return void
697
 * @access private
698
 */
699
	function __calcCoverage($lineCount, $coveredCount) {
700
		if ($coveredCount > $lineCount) {
701
			trigger_error('Sorry, you cannot have more covered lines than total lines!');
702
		}
703
		return ($lineCount != 0)
704
				? round(100 * $coveredCount / $lineCount, 2)
705
				: '0.00';
706
	}
707
/**
708
 * Gets us the base path to look for the test files
709
 *
710
 * @param string $isApp
711
 * @return void
712
 * @access public
713
 */
714
	function __getTestFilesPath($isApp = true) {
715
		$manager = CodeCoverageManager::getInstance();
716
		$path = ROOT . DS;
717
 
718
		if ($isApp) {
719
			$path .= APP_DIR . DS;
720
		} elseif (!!$manager->pluginTest) {
721
			$pluginPath = APP . 'plugins' . DS . $manager->pluginTest . DS;
722
 
723
			$pluginPaths = Configure::read('pluginPaths');
724
			foreach ($pluginPaths as $tmpPath) {
725
				$tmpPath = $tmpPath . $manager->pluginTest . DS;
726
				if (file_exists($tmpPath)) {
727
					$pluginPath = $tmpPath;
728
					break;
729
				}
730
			}
731
 
732
			$path = $pluginPath;
733
		} else {
734
			$path = TEST_CAKE_CORE_INCLUDE_PATH;
735
		}
736
 
737
		return $path;
738
	}
739
/**
740
 * Finds the last element of an array that contains $needle in a strpos computation
741
 *
742
 * @param array $arr
743
 * @param string $needle
744
 * @return void
745
 * @access private
746
 */
747
	function __array_strpos($arr, $needle, $reverse = false) {
748
		if (!is_array($arr) || empty($arr)) {
749
			return false;
750
		}
751
 
752
		if ($reverse) {
753
			$arr = array_reverse($arr, true);
754
		}
755
 
756
		foreach ($arr as $key => $val) {
757
			if (strpos($val, $needle) !== false) {
758
				return $key;
759
			}
760
		}
761
		return false;
762
	}
763
}
764
?>