PageRenderTime 40ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/code/ryzom/tools/server/www/webtt/cake/tests/lib/code_coverage_manager.php

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