CTest: Generalize Cobertura coverage format handling
Add support for Cobertura coverage files written by Java. Add a test which uses the report from a Java run of Cobertura to calculate coverage. In the documentation of CTEST_COVERAGE_COMMAND, give a sample .sh file to merge the Cobertura .ser files and generate the XML report from the merged file.
This commit is contained in:
parent
a2822d3089
commit
50daf239b0
|
@ -3,3 +3,58 @@ CTEST_COVERAGE_COMMAND
|
||||||
|
|
||||||
Specify the CTest ``CoverageCommand`` setting
|
Specify the CTest ``CoverageCommand`` setting
|
||||||
in a :manual:`ctest(1)` dashboard client script.
|
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/
|
||||||
|
|
|
@ -12,6 +12,10 @@ public:
|
||||||
XMLParser(cmCTest* ctest, cmCTestCoverageHandlerContainer& cont)
|
XMLParser(cmCTest* ctest, cmCTestCoverageHandlerContainer& cont)
|
||||||
: CTest(ctest), Coverage(cont)
|
: CTest(ctest), Coverage(cont)
|
||||||
{
|
{
|
||||||
|
this->InSources = false;
|
||||||
|
this->InSource = false;
|
||||||
|
this->FilePaths.push_back(this->Coverage.SourceDir);
|
||||||
|
this->CurFileName = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~XMLParser()
|
virtual ~XMLParser()
|
||||||
|
@ -20,9 +24,44 @@ public:
|
||||||
|
|
||||||
protected:
|
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)
|
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;
|
int tagCount = 0;
|
||||||
while(true)
|
while(true)
|
||||||
|
@ -30,11 +69,19 @@ protected:
|
||||||
if(strcmp(atts[tagCount], "filename") == 0)
|
if(strcmp(atts[tagCount], "filename") == 0)
|
||||||
{
|
{
|
||||||
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Reading file: "
|
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Reading file: "
|
||||||
<< atts[tagCount+1] << std::endl);
|
<< atts[tagCount+1]<< std::endl);
|
||||||
this->CurFileName = this->Coverage.SourceDir + "/" +
|
std::string filename = atts[tagCount+1];
|
||||||
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());
|
cmsys::ifstream fin(this->CurFileName.c_str());
|
||||||
if(!fin)
|
if(this->CurFileName == "" || !fin )
|
||||||
{
|
{
|
||||||
this->CurFileName = this->Coverage.BinaryDir + "/" +
|
this->CurFileName = this->Coverage.BinaryDir + "/" +
|
||||||
atts[tagCount+1];
|
atts[tagCount+1];
|
||||||
|
@ -48,7 +95,6 @@ protected:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string line;
|
std::string line;
|
||||||
FileLinesType& curFileLines =
|
FileLinesType& curFileLines =
|
||||||
this->Coverage.TotalCoverage[this->CurFileName];
|
this->Coverage.TotalCoverage[this->CurFileName];
|
||||||
|
@ -83,7 +129,9 @@ protected:
|
||||||
{
|
{
|
||||||
FileLinesType& curFileLines =
|
FileLinesType& curFileLines =
|
||||||
this->Coverage.TotalCoverage[this->CurFileName];
|
this->Coverage.TotalCoverage[this->CurFileName];
|
||||||
curFileLines[curNumber-1] = curHits;
|
{
|
||||||
|
curFileLines[curNumber-1] = curHits;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
++tagCount;
|
++tagCount;
|
||||||
|
@ -91,10 +139,11 @@ protected:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void EndElement(const std::string&) {}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
bool InSources;
|
||||||
|
bool InSource;
|
||||||
|
std::vector<std::string> FilePaths;
|
||||||
typedef cmCTestCoverageHandlerContainer::SingleFileCoverageVector
|
typedef cmCTestCoverageHandlerContainer::SingleFileCoverageVector
|
||||||
FileLinesType;
|
FileLinesType;
|
||||||
cmCTest* CTest;
|
cmCTest* CTest;
|
||||||
|
|
|
@ -34,6 +34,9 @@ public:
|
||||||
cmParseCoberturaCoverage(cmCTestCoverageHandlerContainer& cont,
|
cmParseCoberturaCoverage(cmCTestCoverageHandlerContainer& cont,
|
||||||
cmCTest* ctest);
|
cmCTest* ctest);
|
||||||
|
|
||||||
|
bool inSources;
|
||||||
|
bool inSource;
|
||||||
|
std::vector<std::string> filepaths;
|
||||||
//! Read the XML produced by running `coverage xml`
|
//! Read the XML produced by running `coverage xml`
|
||||||
bool ReadCoverageXML(const char* xmlFile);
|
bool ReadCoverageXML(const char* xmlFile);
|
||||||
|
|
||||||
|
|
|
@ -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.*"
|
"Process file.*foo.py.*Total LOC:.*13.*Percentage Coverage: 84.62.*"
|
||||||
ENVIRONMENT COVFILE=)
|
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)
|
function(add_config_tests cfg)
|
||||||
set(base "${CMake_BINARY_DIR}/Tests/CTestConfig")
|
set(base "${CMake_BINARY_DIR}/Tests/CTestConfig")
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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="<clinit>" 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="<init>" 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>
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue