PageRenderTime 61ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/cake/tests/lib/code_coverage_manager.php

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