/** * Calculates coverage statistics for the file. * * @access protected */ protected function calculateStatistics() { $classes = PHPUnit_Util_Class::getClassesInFile($this->getPath()); $startLines = array(); $endLines = array(); foreach ($classes as $class) { if (!$class->isInterface()) { $className = $class->getName(); $classStartLine = $class->getStartLine(); $classEndLine = $class->getEndLine(); $this->classes[$className] = array('methods' => array(), 'startLine' => $classStartLine, 'executableLines' => 0, 'executedLines' => 0); $startLines[$classStartLine] =& $this->classes[$className]; $endLines[$classEndLine] =& $this->classes[$className]; foreach ($class->getMethods() as $method) { if (!$method->isAbstract() && $method->getDeclaringClass()->getName() == $className) { $methodName = $method->getName(); $methodStartLine = $method->getStartLine(); $methodEndLine = $method->getEndLine(); $this->classes[$className]['methods'][$methodName] = array('startLine' => $methodStartLine, 'executableLines' => 0, 'executedLines' => 0); $startLines[$methodStartLine] =& $this->classes[$className]['methods'][$methodName]; $endLines[$methodEndLine] =& $this->classes[$className]['methods'][$methodName]; $this->numMethods++; } } $this->numClasses++; } } $ignoreStart = -1; $lineNumber = 1; foreach ($this->codeLines as $line) { if (isset($startLines[$lineNumber])) { // Start line of a class. if (isset($startLines[$lineNumber]['methods'])) { $currentClass =& $startLines[$lineNumber]; } else { $currentMethod =& $startLines[$lineNumber]; } } if (strpos($line, '@codeCoverageIgnore') !== FALSE) { if (strpos($line, '@codeCoverageIgnoreStart') !== FALSE) { $ignoreStart = $line; } else { if (strpos($line, '@codeCoverageIgnoreEnd') !== FALSE) { $ignoreStart = -1; } } } if (isset($this->executedLines[$lineNumber])) { // Array: Line is executable and was executed. if (is_array($this->executedLines[$lineNumber])) { if (isset($currentClass)) { $currentClass['executableLines']++; $currentClass['executedLines']++; } if (isset($currentMethod)) { $currentMethod['executableLines']++; $currentMethod['executedLines']++; } $this->numExecutableLines++; $this->numExecutedLines++; } else { if ($this->executedLines[$lineNumber] == -1) { if (isset($currentClass)) { $currentClass['executableLines']++; } if (isset($currentMethod)) { $currentMethod['executableLines']++; } $this->numExecutableLines++; if ($ignoreStart != -1 && $line > $ignoreStart) { if (isset($currentClass)) { $currentClass['executedLines']++; } if (isset($currentMethod)) { $currentMethod['executedLines']++; } $this->numExecutedLines++; } } } } if (isset($endLines[$lineNumber])) { // End line of a class. if (isset($endLines[$lineNumber]['methods'])) { unset($currentClass); } else { unset($currentMethod); } } $lineNumber++; } foreach ($this->classes as $class) { foreach ($class['methods'] as $method) { if ($method['executedLines'] > 0) { $this->numCalledMethods++; } } if ($class['executedLines'] > 0) { $this->numCalledClasses++; } } }
protected function processClasses() { $classes = PHPUnit_Util_Class::getClassesInFile($this->getPath()); foreach ($classes as $class) { if (!$class->isInterface()) { $className = $class->getName(); $classStartLine = $class->getStartLine(); $classEndLine = $class->getEndLine(); $this->classes[$className] = array('methods' => array(), 'startLine' => $classStartLine, 'executableLines' => 0, 'executedLines' => 0); $this->startLines[$classStartLine] =& $this->classes[$className]; $this->endLines[$classEndLine] =& $this->classes[$className]; foreach ($class->getMethods() as $method) { if (!$method->isAbstract() && $method->getDeclaringClass()->getName() == $className) { $methodName = $method->getName(); $methodStartLine = $method->getStartLine(); $methodEndLine = $method->getEndLine(); $this->classes[$className]['methods'][$methodName] = array('startLine' => $methodStartLine, 'executableLines' => 0, 'executedLines' => 0); $this->startLines[$methodStartLine] =& $this->classes[$className]['methods'][$methodName]; $this->endLines[$methodEndLine] =& $this->classes[$className]['methods'][$methodName]; $this->numMethods++; } } $this->numClasses++; } } }
/** * @param PHPUnit_Framework_TestResult $result * @todo Count conditionals. */ public function process(PHPUnit_Framework_TestResult $result) { $time = time(); $document = new DOMDocument('1.0', 'UTF-8'); $document->formatOutput = TRUE; $coverage = $document->createElement('coverage'); $coverage->setAttribute('generated', $time); $coverage->setAttribute('phpunit', PHPUnit_Runner_Version::id()); $document->appendChild($coverage); $project = $document->createElement('project'); $project->setAttribute('name', $result->topTestSuite()->getName()); $project->setAttribute('timestamp', $time); $coverage->appendChild($project); $codeCoverageInformation = $result->getCodeCoverageInformation(); $files = PHPUnit_Util_CodeCoverage::getSummary($codeCoverageInformation); $packages = array(); $projectFiles = 0; $projectLoc = 0; $projectNcloc = 0; $projectClasses = 0; $projectMethods = 0; $projectCoveredMethods = 0; $projectConditionals = 0; $projectCoveredConditionals = 0; $projectStatements = 0; $projectCoveredStatements = 0; foreach ($files as $filename => $data) { $projectFiles++; $fileClasses = 0; $fileConditionals = 0; $fileCoveredConditionals = 0; $fileStatements = 0; $fileCoveredStatements = 0; $fileMethods = 0; $fileCoveredMethods = 0; $file = $document->createElement('file'); $file->setAttribute('name', $filename); $namespace = 'global'; $classes = PHPUnit_Util_Class::getClassesInFile($filename); $lines = array(); foreach ($classes as $class) { if ($class->isInterface()) { continue; } $className = $class->getName(); $methods = $class->getMethods(); $packageInformation = PHPUnit_Util_Class::getPackageInformation($className); $numMethods = 0; $fileClasses++; $projectClasses++; if (!empty($packageInformation['namespace'])) { $namespace = $packageInformation['namespace']; } $classConditionals = 0; $classCoveredConditionals = 0; $classStatements = 0; $classCoveredStatements = 0; $classCoveredMethods = 0; foreach ($methods as $method) { if ($method->getDeclaringClass()->getName() == $class->getName()) { $startLine = $method->getStartLine(); $endLine = $method->getEndLine(); $tests = array(); for ($i = $startLine; $i <= $endLine; $i++) { if (isset($files[$filename][$i])) { if (is_array($files[$filename][$i])) { foreach ($files[$filename][$i] as $_test) { $add = TRUE; foreach ($tests as $test) { if ($test === $_test) { $add = FALSE; break; } } if ($add) { $tests[] = $_test; } } $classCoveredStatements++; } $classStatements++; } } $count = count($tests); $lines[$startLine] = array('count' => $count, 'type' => 'method'); if ($count > 0) { $classCoveredMethods++; $fileCoveredMethods++; $projectCoveredMethods++; } $classStatements--; $numMethods++; $fileMethods++; $projectMethods++; } } $classXML = $document->createElement('class'); $classXML->setAttribute('name', $className); $classXML->setAttribute('namespace', $namespace); if (!empty($packageInformation['fullPackage'])) { $classXML->setAttribute('fullPackage', $packageInformation['fullPackage']); } if (!empty($packageInformation['category'])) { $classXML->setAttribute('category', $packageInformation['category']); } if (!empty($packageInformation['package'])) { $classXML->setAttribute('package', $packageInformation['package']); } if (!empty($packageInformation['subpackage'])) { $classXML->setAttribute('subpackage', $packageInformation['subpackage']); } $file->appendChild($classXML); $classMetricsXML = $document->createElement('metrics'); $classMetricsXML->setAttribute('methods', $numMethods); $classMetricsXML->setAttribute('coveredmethods', $classCoveredMethods); //$classMetricsXML->setAttribute('conditionals', $classConditionals); //$classMetricsXML->setAttribute('coveredconditionals', $classCoveredConditionals); $classMetricsXML->setAttribute('statements', $classStatements); $classMetricsXML->setAttribute('coveredstatements', $classCoveredStatements); $classMetricsXML->setAttribute('elements', $classConditionals + $classStatements + $numMethods); $classMetricsXML->setAttribute('coveredelements', $classCoveredConditionals + $classCoveredStatements + $classCoveredMethods); $classXML->appendChild($classMetricsXML); } foreach ($data as $_line => $_data) { if (is_array($_data)) { $count = count($_data); } else { if ($_data == -1) { $count = 0; } else { if ($_data == -2) { continue; } } } $lines[$_line] = array('count' => $count, 'type' => 'stmt'); } ksort($lines); foreach ($lines as $_line => $_data) { $line = $document->createElement('line'); $line->setAttribute('num', $_line); $line->setAttribute('type', $_data['type']); $line->setAttribute('count', $_data['count']); if ($_data['type'] == 'stmt') { if ($_data['count'] != 0) { $fileCoveredStatements++; } $fileStatements++; } $file->appendChild($line); } if (file_exists($filename)) { $fileMetrics = PHPUnit_Util_Metrics_File::factory($filename, $files); $fileLoc = $fileMetrics->getLoc(); $fileNcloc = $fileMetrics->getNcloc(); $fileMetricsXML = $document->createElement('metrics'); $fileMetricsXML->setAttribute('loc', $fileLoc); $fileMetricsXML->setAttribute('ncloc', $fileNcloc); $fileMetricsXML->setAttribute('classes', $fileClasses); $fileMetricsXML->setAttribute('methods', $fileMethods); $fileMetricsXML->setAttribute('coveredmethods', $fileCoveredMethods); //$fileMetricsXML->setAttribute('conditionals', $fileConditionals); //$fileMetricsXML->setAttribute('coveredconditionals', $fileCoveredConditionals); $fileMetricsXML->setAttribute('statements', $fileStatements); $fileMetricsXML->setAttribute('coveredstatements', $fileCoveredStatements); $fileMetricsXML->setAttribute('elements', $fileConditionals + $fileStatements + $fileMethods); $fileMetricsXML->setAttribute('coveredelements', $fileCoveredConditionals + $fileCoveredStatements + $fileCoveredMethods); $file->appendChild($fileMetricsXML); if ($namespace == 'global') { $project->appendChild($file); } else { if (!isset($packages[$namespace])) { $packages[$namespace] = $document->createElement('package'); $packages[$namespace]->setAttribute('name', $namespace); $project->appendChild($packages[$namespace]); } $packages[$namespace]->appendChild($file); } $projectLoc += $fileLoc; $projectNcloc += $fileNcloc; $projectStatements += $fileStatements; $projectCoveredStatements += $fileCoveredStatements; } } $projectMetricsXML = $document->createElement('metrics'); $projectMetricsXML->setAttribute('files', $projectFiles); $projectMetricsXML->setAttribute('loc', $projectLoc); $projectMetricsXML->setAttribute('ncloc', $projectNcloc); $projectMetricsXML->setAttribute('classes', $projectClasses); $projectMetricsXML->setAttribute('methods', $projectMethods); $projectMetricsXML->setAttribute('coveredmethods', $projectCoveredMethods); //$projectMetricsXML->setAttribute('conditionals', $projectConditionals); //$projectMetricsXML->setAttribute('coveredconditionals', $projectCoveredConditionals); $projectMetricsXML->setAttribute('statements', $projectStatements); $projectMetricsXML->setAttribute('coveredstatements', $projectCoveredStatements); $projectMetricsXML->setAttribute('elements', $projectConditionals + $projectStatements + $projectMethods); $projectMetricsXML->setAttribute('coveredelements', $projectCoveredConditionals + $projectCoveredStatements + $projectCoveredMethods); $project->appendChild($projectMetricsXML); $this->write($document->saveXML()); $this->flush(); }
/** * Stores code coverage information. * * @param PHPUnit_Framework_TestResult $result * @param integer $revision * @access public */ public function storeCodeCoverage(PHPUnit_Framework_TestResult $result, $revision) { $codeCoverage = $result->getCodeCoverageInformation(FALSE, TRUE); $summary = PHPUnit_Util_CodeCoverage::getSummary($codeCoverage); $files = array_keys($summary); $commonPath = PHPUnit_Util_Filesystem::getCommonPath($files); $this->dbh->beginTransaction(); foreach ($files as $file) { $filename = str_replace($commonPath, '', $file); $fileId = FALSE; $lines = file($file); $numLines = count($lines); $stmt = $this->dbh->query(sprintf('SELECT code_file_id FROM code_file WHERE code_file_name = "%s" AND revision = %d;', $filename, $revision)); if ($stmt) { $fileId = (int) $stmt->fetchColumn(); } unset($stmt); if ($fileId == 0) { $this->dbh->exec(sprintf('INSERT INTO code_file (code_file_name, code_file_md5, revision) VALUES("%s", "%s", %d);', $filename, md5_file($file), $revision)); $fileId = $this->dbh->lastInsertId(); $classes = PHPUnit_Util_Class::getClassesInFile($file, $commonPath); foreach ($classes as $class) { $this->dbh->exec(sprintf('INSERT INTO code_class (code_file_id, code_class_name, code_class_start_line, code_class_end_line) VALUES(%d, "%s", %d, %d);', $fileId, $class->getName(), $class->getStartLine(), $class->getEndLine())); $classId = $this->dbh->lastInsertId(); foreach ($class->getMethods() as $method) { if ($class->getName() != $method->getDeclaringClass()->getName()) { continue; } $this->dbh->exec(sprintf('INSERT INTO code_method (code_class_id, code_method_name, code_method_start_line, code_method_end_line) VALUES(%d, "%s", %d, %d);', $classId, $method->getName(), $method->getStartLine(), $method->getEndLine())); } } $i = 1; foreach ($lines as $line) { $this->dbh->exec(sprintf('INSERT INTO code_line (code_file_id, code_line_number, code_line, code_line_covered) VALUES(%d, %d, "%s", %d);', $fileId, $i, trim($line), isset($summary[$file][$i]) ? $summary[$file][$i] : 0)); $i++; } } for ($lineNumber = 1; $lineNumber <= $numLines; $lineNumber++) { $coveringTests = PHPUnit_Util_CodeCoverage::getCoveringTests($codeCoverage, $file, $lineNumber); if (is_array($coveringTests)) { $stmt = $this->dbh->query(sprintf('SELECT code_line_id, code_line_covered FROM code_line WHERE code_file_id = %d AND code_line_number = %d;', $fileId, $lineNumber)); $codeLineId = (int) $stmt->fetchColumn(0); $oldCoverageFlag = (int) $stmt->fetchColumn(1); unset($stmt); $newCoverageFlag = $summary[$file][$lineNumber]; if ($oldCoverageFlag == 0 && $newCoverageFlag != 0 || $oldCoverageFlag < 0 && $newCoverageFlag > 0) { $this->dbh->exec(sprintf('UPDATE code_line SET code_line_covered = %d WHERE code_line_id = %d;', $newCoverageFlag, $codeLineId)); } foreach ($coveringTests as $test) { $this->dbh->exec(sprintf('INSERT INTO code_coverage (test_id, code_line_id) VALUES(%d, %d);', $test->__db_id, $codeLineId)); } } } } foreach ($result->topTestSuite() as $test) { if ($test instanceof PHPUnit_Framework_TestCase) { $stmt = $this->dbh->query(sprintf('SELECT code_method.code_method_id FROM code_class, code_method WHERE code_class.code_class_id = code_method.code_class_id AND code_class.code_class_name = "%s" AND code_method.code_method_name = "%s";', get_class($test), $test->getName())); $methodId = (int) $stmt->fetchColumn(); unset($stmt); $this->dbh->exec(sprintf('UPDATE test SET code_method_id = %d WHERE test_id = %d;', $methodId, $test->__db_id)); } } $this->dbh->commit(); }
/** * Constructor. * * @param string $filename * @param array $codeCoverage * @throws RuntimeException */ protected function __construct($filename, &$codeCoverage = array()) { if (!file_exists($filename)) { throw new RuntimeException(sprintf('File "%s" not found.', $filename)); } $this->filename = $filename; $this->lines = file($filename); $this->tokens = token_get_all(file_get_contents($filename)); $this->countLines(); $this->setCoverage($codeCoverage); foreach (PHPUnit_Util_Class::getClassesInFile($filename) as $class) { $this->classes[$class->getName()] = PHPUnit_Util_Metrics_Class::factory($class, $codeCoverage); } foreach (PHPUnit_Util_Class::getFunctionsInFile($filename) as $function) { $this->functions[$function->getName()] = PHPUnit_Util_Metrics_Function::factory($function, $codeCoverage); } }
protected function _getClassessInFile($path) { if (version_compare(PHPUnit_Runner_Version::id(), '3.4', '<')) { $classes = array(); foreach (PHPUnit_Util_Class::getClassesInFile($path) as $reflection) { $classes[] = $reflection->getName(); } return $classes; } else { return array_keys(PHPUnit_Util_File::getClassesInFile($path)); } }