Merge topic 'expand_cobertura_coverage'

50daf239 CTest: Generalize Cobertura coverage format handling
a2822d30 CTest: Rename coverage implementation for "Python" to "Cobertura"
This commit is contained in:
Brad King 2014-06-05 10:55:29 -04:00 committed by CMake Topic Stage
commit 994f4b71fc
10 changed files with 323 additions and 26 deletions

View File

@ -3,3 +3,58 @@ CTEST_COVERAGE_COMMAND
Specify the CTest ``CoverageCommand`` setting
in a :manual:`ctest(1)` dashboard client script.
Cobertura
'''''''''
Using `Cobertura`_ as the coverage generation within your multi-module
Java project can generate a series of XML files.
The Cobertura Coverage parser expects to read the coverage data from a
single XML file which contains the coverage data for all modules.
Cobertura has a program with the ability to merge given cobertura.ser files
and then another program to generate a combined XML file from the previous
merged file. For command line testing, this can be done by hand prior to
CTest looking for the coverage files. For script builds,
set the ``CTEST_COVERAGE_COMMAND`` variable to point to a file which will
perform these same steps, such as a .sh or .bat file.
.. code-block:: cmake
set(CTEST_COVERAGE_COMMAND .../run-coverage-and-consolidate.sh)
where the ``run-coverage-and-consolidate.sh`` script is perhaps created by
the :command:`configure_file` command and might contain the following code:
.. code-block:: bash
#!/usr/bin/env bash
CoberturaFiles="$(find "/path/to/source" -name "cobertura.ser")"
SourceDirs="$(find "/path/to/source" -name "java" -type d)"
cobertura-merge --datafile coberturamerge.ser $CoberturaFiles
cobertura-report --datafile coberturamerge.ser --destination . \
--format xml $SourceDirs
The script uses ``find`` to capture the paths to all of the cobertura.ser files
found below the project's source directory. It keeps the list of files and
supplies it as an argument to the ``cobertura-merge`` program. The ``--datafile``
argument signifies where the result of the merge will be kept.
The combined ``coberturamerge.ser`` file is then used to generate the XML report
using the ``cobertura-report`` program. The call to the cobertura-report program
requires some named arguments.
``--datafila``
path to the merged .ser file
``--destination``
path to put the output files(s)
``--format``
file format to write output in: xml or html
The rest of the supplied arguments consist of the full paths to the
/src/main/java directories of each module within the souce tree. These
directories are needed and should not be forgotten.
.. _`Cobertura`: http://cobertura.github.io/cobertura/

View File

@ -519,7 +519,7 @@ set(CTEST_SRCS cmCTest.cxx
CTest/cmParseCacheCoverage.cxx
CTest/cmParseGTMCoverage.cxx
CTest/cmParsePHPCoverage.cxx
CTest/cmParsePythonCoverage.cxx
CTest/cmParseCoberturaCoverage.cxx
CTest/cmCTestEmptyBinaryDirectoryCommand.cxx
CTest/cmCTestGenericHandler.cxx
CTest/cmCTestHandlerCommand.cxx

View File

@ -11,7 +11,7 @@
============================================================================*/
#include "cmCTestCoverageHandler.h"
#include "cmParsePHPCoverage.h"
#include "cmParsePythonCoverage.h"
#include "cmParseCoberturaCoverage.h"
#include "cmParseGTMCoverage.h"
#include "cmParseCacheCoverage.h"
#include "cmCTest.h"
@ -401,7 +401,7 @@ int cmCTestCoverageHandler::ProcessHandler()
{
return error;
}
file_count += this->HandlePythonCoverage(&cont);
file_count += this->HandleCoberturaCoverage(&cont);
error = cont.Error;
if ( file_count < 0 )
{
@ -779,10 +779,10 @@ int cmCTestCoverageHandler::HandlePHPCoverage(
}
//----------------------------------------------------------------------
int cmCTestCoverageHandler::HandlePythonCoverage(
int cmCTestCoverageHandler::HandleCoberturaCoverage(
cmCTestCoverageHandlerContainer* cont)
{
cmParsePythonCoverage cov(*cont, this->CTest);
cmParseCoberturaCoverage cov(*cont, this->CTest);
// Assume the coverage.xml is in the source directory
std::string coverageXMLFile = this->CTest->GetBinaryDir() + "/coverage.xml";
@ -790,14 +790,14 @@ int cmCTestCoverageHandler::HandlePythonCoverage(
if(cmSystemTools::FileExists(coverageXMLFile.c_str()))
{
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Parsing coverage.py XML file: " << coverageXMLFile
"Parsing Cobertura XML file: " << coverageXMLFile
<< std::endl);
cov.ReadCoverageXML(coverageXMLFile.c_str());
}
else
{
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Cannot find coverage.py XML file: " << coverageXMLFile
"Cannot find Cobertura XML file: " << coverageXMLFile
<< std::endl);
}
return static_cast<int>(cont->TotalCoverage.size());

View File

@ -76,7 +76,7 @@ private:
int HandlePHPCoverage(cmCTestCoverageHandlerContainer* cont);
//! Handle coverage for Python with coverage.py
int HandlePythonCoverage(cmCTestCoverageHandlerContainer* cont);
int HandleCoberturaCoverage(cmCTestCoverageHandlerContainer* cont);
//! Handle coverage for mumps
int HandleMumpsCoverage(cmCTestCoverageHandlerContainer* cont);

View File

@ -1,17 +1,21 @@
#include "cmStandardIncludes.h"
#include "cmSystemTools.h"
#include "cmXMLParser.h"
#include "cmParsePythonCoverage.h"
#include "cmParseCoberturaCoverage.h"
#include <cmsys/Directory.hxx>
#include <cmsys/FStream.hxx>
//----------------------------------------------------------------------------
class cmParsePythonCoverage::XMLParser: public cmXMLParser
class cmParseCoberturaCoverage::XMLParser: public cmXMLParser
{
public:
XMLParser(cmCTest* ctest, cmCTestCoverageHandlerContainer& cont)
: CTest(ctest), Coverage(cont)
{
this->InSources = false;
this->InSource = false;
this->FilePaths.push_back(this->Coverage.SourceDir);
this->CurFileName = "";
}
virtual ~XMLParser()
@ -20,9 +24,44 @@ public:
protected:
virtual void EndElement(const std::string& name)
{
if(name == "source")
{
this->InSource=false;
}
else if (name == "sources")
{
this->InSources=false;
}
}
virtual void CharacterDataHandler(const char* data, int length)
{
std::string tmp;
tmp.insert(0,data,length);
if (this->InSources && this->InSource)
{
this->FilePaths.push_back(tmp);
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Adding Source: "
<< tmp << std::endl);
}
}
virtual void StartElement(const std::string& name, const char** atts)
{
if(name == "class")
std::string FoundSource;
std::string finalpath = "";
if(name == "source")
{
this->InSource = true;
}
else if(name == "sources")
{
this->InSources = true;
}
else if(name == "class")
{
int tagCount = 0;
while(true)
@ -30,11 +69,19 @@ protected:
if(strcmp(atts[tagCount], "filename") == 0)
{
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Reading file: "
<< atts[tagCount+1] << std::endl);
this->CurFileName = this->Coverage.SourceDir + "/" +
atts[tagCount+1];
<< atts[tagCount+1]<< std::endl);
std::string filename = atts[tagCount+1];
for(size_t i=0;i < FilePaths.size();i++)
{
finalpath = FilePaths[i] + "/" + filename;
if(cmSystemTools::FileExists(finalpath.c_str()))
{
this->CurFileName = finalpath;
break;
}
}
cmsys::ifstream fin(this->CurFileName.c_str());
if(!fin)
if(this->CurFileName == "" || !fin )
{
this->CurFileName = this->Coverage.BinaryDir + "/" +
atts[tagCount+1];
@ -48,7 +95,6 @@ protected:
break;
}
}
std::string line;
FileLinesType& curFileLines =
this->Coverage.TotalCoverage[this->CurFileName];
@ -83,7 +129,9 @@ protected:
{
FileLinesType& curFileLines =
this->Coverage.TotalCoverage[this->CurFileName];
curFileLines[curNumber-1] = curHits;
{
curFileLines[curNumber-1] = curHits;
}
break;
}
++tagCount;
@ -91,10 +139,11 @@ protected:
}
}
virtual void EndElement(const std::string&) {}
private:
bool InSources;
bool InSource;
std::vector<std::string> FilePaths;
typedef cmCTestCoverageHandlerContainer::SingleFileCoverageVector
FileLinesType;
cmCTest* CTest;
@ -104,16 +153,16 @@ private:
};
cmParsePythonCoverage::cmParsePythonCoverage(
cmParseCoberturaCoverage::cmParseCoberturaCoverage(
cmCTestCoverageHandlerContainer& cont,
cmCTest* ctest)
:Coverage(cont), CTest(ctest)
{
}
bool cmParsePythonCoverage::ReadCoverageXML(const char* xmlFile)
bool cmParseCoberturaCoverage::ReadCoverageXML(const char* xmlFile)
{
cmParsePythonCoverage::XMLParser parser(this->CTest, this->Coverage);
cmParseCoberturaCoverage::XMLParser parser(this->CTest, this->Coverage);
parser.ParseFile(xmlFile);
return true;
}

View File

@ -10,8 +10,8 @@
See the License for more information.
============================================================================*/
#ifndef cmParsePythonCoverage_h
#define cmParsePythonCoverage_h
#ifndef cmParseCoberturaCoverage_h
#define cmParseCoberturaCoverage_h
#include "cmStandardIncludes.h"
#include "cmCTestCoverageHandler.h"
@ -25,15 +25,18 @@
* Java-based Cobertura coverage application. This helper class parses
* that XML file to fill the coverage-handler container.
*/
class cmParsePythonCoverage
class cmParseCoberturaCoverage
{
public:
//! Create the coverage parser by passing in the coverage handler
//! container and the cmCTest object
cmParsePythonCoverage(cmCTestCoverageHandlerContainer& cont,
cmParseCoberturaCoverage(cmCTestCoverageHandlerContainer& cont,
cmCTest* ctest);
bool inSources;
bool inSource;
std::vector<std::string> filepaths;
//! Read the XML produced by running `coverage xml`
bool ReadCoverageXML(const char* xmlFile);

View File

@ -2184,6 +2184,24 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release
"Process file.*foo.py.*Total LOC:.*13.*Percentage Coverage: 84.62.*"
ENVIRONMENT COVFILE=)
# Adding a test case for non-python Cobertura Coverage
configure_file(
"${CMake_SOURCE_DIR}/Tests/CoberturaCoverage/DartConfiguration.tcl.in"
"${CMake_BINARY_DIR}/Testing/CoberturaCoverage/DartConfiguration.tcl")
configure_file(
"${CMake_SOURCE_DIR}/Tests/CoberturaCoverage/coverage.xml.in"
"${CMake_BINARY_DIR}/Testing/CoberturaCoverage/coverage.xml")
file(COPY "${CMake_SOURCE_DIR}/Tests/CoberturaCoverage/src"
DESTINATION "${CMake_BINARY_DIR}/Testing/CoberturaCoverage")
add_test(NAME CTestCoberturaCoverage
COMMAND cmake -E chdir
${CMake_BINARY_DIR}/Testing/CoberturaCoverage
$<TARGET_FILE:ctest> -T Coverage --debug)
set_tests_properties(CTestCoberturaCoverage PROPERTIES
PASS_REGULAR_EXPRESSION
"Process file.*CoverageTest.java.*Total LOC:.*18.*Percentage Coverage: 72.22.*"
ENVIRONMENT COVFILE=)
function(add_config_tests cfg)
set(base "${CMake_BINARY_DIR}/Tests/CTestConfig")

View File

@ -0,0 +1,8 @@
# This file is configured by CMake automatically as DartConfiguration.tcl
# If you choose not to use CMake, this file may be hand configured, by
# filling in the required variables.
# Configuration directories and files
SourceDirectory: ${CMake_SOURCE_DIR}/Testing/CoberturaCoverage
BuildDirectory: ${CMake_BINARY_DIR}/Testing/CoberturaCoverage

View File

@ -0,0 +1,112 @@
<?xml version="1.0"?>
<!DOCTYPE coverage SYSTEM "http://cobertura.sourceforge.net/xml/coverage-04.dtd">
<coverage line-rate="0.7222222222222222" branch-rate="0.875" lines-covered="13" lines-valid="18" branches-covered="7" branches-valid="8" complexity="0.0" version="1.9.4.1" timestamp="1401890139281">
<sources>
<source>${CMake_BINARY_DIR}/Testing/CoberturaCoverage/src/main/java/</source>
</sources>
<packages>
<package name="org.cmake.Coverage" line-rate="0.7222222222222222" branch-rate="0.875" complexity="0.0">
<classes>
<class name="org.cmake.Coverage.CoverageTest" filename="org/cmake/CoverageTest.java" line-rate="0.7222222222222222" branch-rate="0.875" complexity="0.0">
<methods>
<method name="&lt;clinit&gt;" signature="()V" line-rate="1.0" branch-rate="1.0">
<lines>
<line number="10" hits="2" branch="false"/>
<line number="11" hits="2" branch="false"/>
</lines>
</method>
<method name="&lt;init&gt;" signature="()V" line-rate="0.0" branch-rate="1.0">
<lines>
<line number="8" hits="0" branch="false"/>
<line number="12" hits="0" branch="false"/>
</lines>
</method>
<method name="equalsVarOne" signature="(Ljava/lang/String;)Ljava/lang/Boolean;" line-rate="0.6666666666666666" branch-rate="0.5">
<lines>
<line number="16" hits="2" branch="true" condition-coverage="50% (1/2)">
<conditions>
<condition number="0" type="jump" coverage="50%"/>
</conditions>
</line>
<line number="17" hits="2" branch="false"/>
<line number="20" hits="0" branch="false"/>
</lines>
</method>
<method name="equalsVarTwo" signature="(Ljava/lang/String;)Z" line-rate="1.0" branch-rate="1.0">
<lines>
<line number="26" hits="4" branch="true" condition-coverage="100% (2/2)">
<conditions>
<condition number="0" type="jump" coverage="100%"/>
</conditions>
</line>
<line number="27" hits="2" branch="false"/>
<line number="30" hits="2" branch="false"/>
</lines>
</method>
<method name="timesIntOne" signature="(Ljava/lang/Integer;)Ljava/lang/Integer;" line-rate="0.0" branch-rate="1.0">
<lines>
<line number="35" hits="0" branch="false"/>
<line number="36" hits="0" branch="false"/>
</lines>
</method>
<method name="whileLoop" signature="(Ljava/lang/Integer;)Z" line-rate="1.0" branch-rate="1.0">
<lines>
<line number="41" hits="2" branch="false"/>
<line number="42" hits="10" branch="true" condition-coverage="100% (2/2)">
<conditions>
<condition number="0" type="jump" coverage="100%"/>
</conditions>
</line>
<line number="43" hits="8" branch="false"/>
<line number="45" hits="2" branch="true" condition-coverage="100% (2/2)">
<conditions>
<condition number="0" type="jump" coverage="100%"/>
</conditions>
</line>
<line number="46" hits="1" branch="false"/>
<line number="49" hits="1" branch="false"/>
</lines>
</method>
</methods>
<lines>
<line number="8" hits="0" branch="false"/>
<line number="10" hits="2" branch="false"/>
<line number="11" hits="2" branch="false"/>
<line number="12" hits="0" branch="false"/>
<line number="16" hits="2" branch="true" condition-coverage="50% (1/2)">
<conditions>
<condition number="0" type="jump" coverage="50%"/>
</conditions>
</line>
<line number="17" hits="2" branch="false"/>
<line number="20" hits="0" branch="false"/>
<line number="26" hits="4" branch="true" condition-coverage="100% (2/2)">
<conditions>
<condition number="0" type="jump" coverage="100%"/>
</conditions>
</line>
<line number="27" hits="2" branch="false"/>
<line number="30" hits="2" branch="false"/>
<line number="35" hits="0" branch="false"/>
<line number="36" hits="0" branch="false"/>
<line number="41" hits="2" branch="false"/>
<line number="42" hits="10" branch="true" condition-coverage="100% (2/2)">
<conditions>
<condition number="0" type="jump" coverage="100%"/>
</conditions>
</line>
<line number="43" hits="8" branch="false"/>
<line number="45" hits="2" branch="true" condition-coverage="100% (2/2)">
<conditions>
<condition number="0" type="jump" coverage="100%"/>
</conditions>
</line>
<line number="46" hits="1" branch="false"/>
<line number="49" hits="1" branch="false"/>
</lines>
</class>
</classes>
</package>
</packages>
</coverage>

View File

@ -0,0 +1,52 @@
package org.cmake.Coverage;
import java.io.Serializable;
import java.util.Map;
import java.util.List;
import java.awt.*;
public class CoverageTest {
public static String VarOne = "test1";
public static String VarTwo = "test2";
private Integer IntOne = 4;
public static Boolean equalsVarOne(String inString) {
if(VarOne.equals(inString)){
return true;
}
else {
return false;
}
}
public static boolean equalsVarTwo(String inString){
if(VarTwo.equals(inString)){
return true;
}
else {
return false;
}
}
private Integer timesIntOne(Integer inVal){
return inVal * IntOne;
}
public static boolean whileLoop(Integer StopInt){
Integer i = 0;
while(i < StopInt){
i=i+1;
}
if (i.equals(5)){
return true;
}
else {
return false;
}
}
}