From f9ec1e752d723d9862e1014823bd33d6670fbb0d Mon Sep 17 00:00:00 2001 From: DarkAngelBGE Date: Tue, 29 Apr 2008 21:03:51 +0000 Subject: [PATCH] code coverage analysis for cli, fixing CliReporter extending SimpleReporter and not TextReporter (to enable the printing of exceptions in cli) some minor refactorings git-svn-id: https://svn.cakephp.org/repo/branches/1.2.x.x@6735 3807eeeb-6ff5-0310-8944-8be069107fe0 --- cake/console/libs/testsuite.php | 52 +++++++++++++--- .../cases/libs/code_coverage_manager.test.php | 56 +++++++++++------- cake/tests/lib/cli_reporter.php | 2 +- cake/tests/lib/code_coverage_manager.php | 59 +++++++++++++++++-- 4 files changed, 135 insertions(+), 34 deletions(-) diff --git a/cake/console/libs/testsuite.php b/cake/console/libs/testsuite.php index ca71ef89f..c2f6ad7d3 100644 --- a/cake/console/libs/testsuite.php +++ b/cake/console/libs/testsuite.php @@ -62,6 +62,13 @@ class TestSuiteShell extends Shell { * @access public */ var $isPluginTest = false; +/** + * Stores if the user wishes to get a code coverage analysis report + * + * @var string + * @access public + */ + var $doCoverage = false; /** * Initialization method installs Simpletest and loads all plugins * @@ -82,7 +89,10 @@ class TestSuiteShell extends Shell { require_once CAKE . 'tests' . DS . 'lib' . DS . 'test_manager.php'; require_once CAKE . 'tests' . DS . 'lib' . DS . 'cli_reporter.php'; - $this->plugins = Configure::listObjects('plugin'); + $plugins = Configure::listObjects('plugin'); + foreach ($plugins as $p) { + $this->plugins[] = Inflector::underscore($p); + } } /** * Main entry point to this shell @@ -106,7 +116,15 @@ class TestSuiteShell extends Shell { } if (isset($this->args[2])) { - $this->file = Inflector::underscore($this->args[2]); + if ($this->args[2] == 'cov') { + $this->doCoverage = true; + } else { + $this->file = Inflector::underscore($this->args[2]); + } + } + + if (isset($this->args[3]) && $this->args[3] == 'cov') { + $this->doCoverage = true; } } else { $this->err('Sorry, you did not pass any arguments!'); @@ -150,6 +168,11 @@ class TestSuiteShell extends Shell { $this->out(''); $this->out("\t\t cake testsuite bugs case models/bug // for the plugin 'bugs' and its test case 'bug'"); $this->out("\t\t cake testsuite bugs group bug // for the plugin bugs and its test group 'bug'"); + $this->out("\t\t cake testsuite bugs_me case models/bug // for the plugin 'bugs_me' and its test case 'bug'"); + $this->out("\t\t cake testsuite bugs_me group bug // for the plugin bugs_me and its test group 'bug'"); + $this->out(''); + $this->out('Code Coverage Analysis: '); + $this->out("\n\nAppend 'gov' to any of the above in order to enable code coverage analysis"); } /** * Checks if the arguments supplied point to a valid test file and thus the shell can be run. @@ -159,9 +182,9 @@ class TestSuiteShell extends Shell { */ function __canRun(){ $isNeitherAppNorCore = !in_array($this->category, array('app', 'core')); - $isNotPlugin = !in_array(Inflector::humanize($this->category), $this->plugins); + $isPlugin = in_array(Inflector::underscore($this->category), $this->plugins); - if ($isNeitherAppNorCore && $isNotPlugin) { + if ($isNeitherAppNorCore && !$isPlugin) { $this->err($this->category.' is an invalid test category (either "app", "core" or name of a plugin)'); return false; } @@ -173,7 +196,7 @@ class TestSuiteShell extends Shell { } if (!in_array($this->type, array('all', 'group', 'case'))) { - $this->err($this->category.' is invalid. Should be case, group or all'); + $this->err($this->type.' is invalid. Should be case, group or all'); return false; } @@ -195,7 +218,7 @@ class TestSuiteShell extends Shell { return true; } - if (!$isNotPlugin && file_exists($folder.DS.'cases'.DS.$this->file.'.test.php')) { + if ($isPlugin && file_exists($folder.DS.'cases'.DS.$this->file.'.test.php')) { return true; } break; @@ -237,7 +260,22 @@ class TestSuiteShell extends Shell { } elseif ($this->isPluginTest) { $case = $this->file.'.test.php'; } - return TestManager::runTestCase($case, $reporter); + + if ($this->doCoverage) { + if (!extension_loaded('xdebug')) { + $this->out('You must install Xdebug to use the CakePHP(tm) Code Coverage Analyzation. Download it from http://www.xdebug.org/docs/install'); + exit(0); + } + + require_once CAKE . 'tests' . DS . 'lib' . DS . 'code_coverage_manager.php'; + CodeCoverageManager::start($case, $reporter); + } + $result = TestManager::runTestCase($case, $reporter); + if ($this->doCoverage) { + CodeCoverageManager::report(); + } + + return $result; } /** * Finds the correct folder to look for tests for based on the input category diff --git a/cake/tests/cases/libs/code_coverage_manager.test.php b/cake/tests/cases/libs/code_coverage_manager.test.php index fcfb69fff..5f84c597c 100644 --- a/cake/tests/cases/libs/code_coverage_manager.test.php +++ b/cake/tests/cases/libs/code_coverage_manager.test.php @@ -27,6 +27,8 @@ * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License */ App::import('Core', 'CodeCoverageManager'); +require_once CAKE . 'tests' . DS . 'lib' . DS . 'cli_reporter.php'; +require_once CAKE . 'tests' . DS . 'lib' . DS . 'cake_reporter.php'; /** * Short description for class. * @@ -39,34 +41,41 @@ class CodeCoverageManagerTest extends UnitTestCase { * */ function testNoTestCaseSupplied() { - CodeCoverageManager::start(substr(md5(microtime()), 0, 5), CakeTestsGetReporter()); - CodeCoverageManager::report(false); - $this->assertError(); - - CodeCoverageManager::start('libs/'.basename(__FILE__), CakeTestsGetReporter()); - CodeCoverageManager::report(false); - $this->assertError(); - - App::import('Core', 'Folder'); - $folder = new Folder(); - $folder->cd(ROOT.DS.LIBS); - $contents = $folder->ls(); - function remove($var) { - return ($var != basename(__FILE__)); - } - $contents[1] = array_filter($contents[1], "remove"); - $keys = array_rand($contents[1], 5); - - foreach ($keys as $key) { - CodeCoverageManager::start('libs'.DS.$contents[1][$key], CakeTestsGetReporter()); + if (php_sapi_name() != 'cli') { // assertError seems to be not working for cli reports + CodeCoverageManager::start(substr(md5(microtime()), 0, 5), new CakeHtmlReporter); CodeCoverageManager::report(false); - $this->assertNoErrors(); + $this->assertError(); + + CodeCoverageManager::start('libs/'.basename(__FILE__), new CakeHtmlReporter); + CodeCoverageManager::report(false); + $this->assertError(); + + $path = LIBS; + if (strpos(LIBS, ROOT) === false) { // cli fix + $path = ROOT.DS.LIBS; + } + App::import('Core', 'Folder'); + $folder = new Folder(); + $folder->cd($path); + $contents = $folder->ls(); + + function remove($var) { + return ($var != basename(__FILE__)); + } + $contents[1] = array_filter($contents[1], "remove"); + $keys = array_rand($contents[1], 5); + foreach ($keys as $key) { + CodeCoverageManager::start('libs'.DS.$contents[1][$key], new CakeHtmlReporter); + CodeCoverageManager::report(false); + $this->assertNoErrors(); + } } } function testGetTestObjectFileNameFromTestCaseFile() { $manager = CodeCoverageManager::getInstance(); + $manager->reporter = new CakeHtmlReporter; $expected = $manager->__testObjectFileFromCaseFile('models/some_file.test.php', true); $this->assertIdentical(APP.'models'.DS.'some_file.php', $expected); @@ -88,6 +97,11 @@ class CodeCoverageManagerTest extends UnitTestCase { $manager->pluginTest = 'bugs'; $expected = $manager->__testObjectFileFromCaseFile('models/some_file.test.php', false); $this->assertIdentical(APP.'plugins'.DS.'bugs'.DS.'models'.DS.'some_file.php', $expected); + + $manager->pluginTest = false; + $manager->reporter = new CLIReporter; + $expected = $manager->__testObjectFileFromCaseFile('libs/set.test.php', false); + $this->assertIdentical(ROOT.DS.'cake'.DS.'libs'.DS.'set.php', $expected); } function testOfHtmlReport() { diff --git a/cake/tests/lib/cli_reporter.php b/cake/tests/lib/cli_reporter.php index 08a46be53..bc63a6473 100644 --- a/cake/tests/lib/cli_reporter.php +++ b/cake/tests/lib/cli_reporter.php @@ -48,7 +48,7 @@ * @package cake * @subpackage cake.cake.tests.libs */ -class CLIReporter extends SimpleReporter { +class CLIReporter extends TextReporter { var $faildetail_separator = ST_FAILDETAIL_SEPARATOR; function CLIReporter($faildetail_separator = NULL) { diff --git a/cake/tests/lib/code_coverage_manager.php b/cake/tests/lib/code_coverage_manager.php index fbf1067f6..abc6b2096 100644 --- a/cake/tests/lib/code_coverage_manager.php +++ b/cake/tests/lib/code_coverage_manager.php @@ -90,6 +90,7 @@ class CodeCoverageManager { $manager->reporter = $reporter; $thisFile = r('.php', '.test.php', basename(__FILE__)); + if (strpos($testCaseFile, $thisFile) !== false) { trigger_error('Xdebug supports no parallel coverage analysis - so this is not possible.', E_USER_ERROR); } @@ -97,9 +98,9 @@ class CodeCoverageManager { if (isset($_GET['app'])) { $manager->appTest = true; } - + if (isset($_GET['plugin'])) { - $manager->pluginTest = $_GET['plugin']; + $manager->pluginTest = Inflector::underscore($_GET['plugin']); } $manager->testCaseFile = $testCaseFile; @@ -114,8 +115,9 @@ class CodeCoverageManager { $manager =& CodeCoverageManager::getInstance(); $testObjectFile = $manager->__testObjectFileFromCaseFile($manager->testCaseFile, $manager->appTest); + if (!file_exists($testObjectFile)) { - trigger_error('This test object file is invalid.'); + trigger_error('This test object file is invalid: '.$testObjectFile); return ; } @@ -138,6 +140,9 @@ class CodeCoverageManager { case 'CakeHtmlReporter': $result = $manager->reportHtmlDiff(@file($testObjectFile), $coverageData, $execCodeLines, $manager->numDiffContextLines); break; + case 'CLIReporter': + $result = $manager->reportCli(@file($testObjectFile), $coverageData, $execCodeLines, $manager->numDiffContextLines); + break; default: trigger_error('Currently only HTML reporting is supported for code coverage analysis.'); break; @@ -175,7 +180,6 @@ class CodeCoverageManager { if ($coverageData[$num] > 0) { $class = 'covered'; $coveredCount++; - $numExecuted = $coverageData[$num]; } } else { $class = 'ignored'; @@ -303,6 +307,37 @@ class CodeCoverageManager { return $manager->__paintHeader($lineCount, $coveredCount, $report); } +/** + * CLI reporting + * + * @param string $testObjectFile + * @param string $coverageData + * @param string $execCodeLines + * @param string $output + * @return void + */ + function reportCli($testObjectFile, $coverageData, $execCodeLines) { + $manager = CodeCoverageManager::getInstance(); + $lineCount = $coveredCount = 0; + $report = ''; + + foreach ($testObjectFile as $num => $line) { + $num++; + + $foundByManualFinder = array_key_exists($num, $execCodeLines) && trim($execCodeLines[$num]) != ''; + $foundByXdebug = array_key_exists($num, $coverageData) && $coverageData[$num] !== -2; + + if ($foundByManualFinder && $foundByXdebug) { + $lineCount++; + + if ($coverageData[$num] > 0) { + $coveredCount++; + } + } + } + + return $manager->__paintHeaderCli($lineCount, $coveredCount, $report); + } /** * Returns the name of the test object file based on a given test case file name * @@ -320,7 +355,7 @@ class CodeCoverageManager { } elseif (!!$manager->pluginTest) { $path .= APP_DIR.DS.'plugins'.DS.$manager->pluginTest.DS; } else { - $path .= CAKE; + $path = ROOT.DS.'cake'.DS; } $folderPrefixMap = array( @@ -409,6 +444,20 @@ class CodeCoverageManager { return $report = '

Code Coverage: '.$codeCoverage.'%

'.$report.'
'; } +/** + * Paints the headline for code coverage analysis in the CLI + * + * @param string $codeCoverage + * @param string $report + * @return void + * @access private + */ + function __paintHeaderCli($lineCount, $coveredCount, $report) { + $manager =& CodeCoverageManager::getInstance(); + $codeCoverage = $manager->__calcCoverage($lineCount, $coveredCount); + + return $report = 'Code Coverage: '.$codeCoverage.'%'; + } /** * Paints a code line for html output *