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
This commit is contained in:
DarkAngelBGE 2008-04-29 21:03:51 +00:00
parent e38afee93d
commit f9ec1e752d
4 changed files with 135 additions and 34 deletions

View file

@ -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,8 +116,16 @@ class TestSuiteShell extends Shell {
}
if (isset($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

View file

@ -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());
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->assertError();
CodeCoverageManager::start('libs/'.basename(__FILE__), CakeTestsGetReporter());
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(ROOT.DS.LIBS);
$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], CakeTestsGetReporter());
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() {

View file

@ -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) {

View file

@ -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);
}
@ -99,7 +100,7 @@ class CodeCoverageManager {
}
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 = '<h2>Code Coverage: '.$codeCoverage.'%</h2>
<div class="code-coverage-results"><pre>'.$report.'</pre></div>';
}
/**
* 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
*