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
|
||||
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)
|
||||
: 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;
|
||||
}
|
||||
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;
|
||||
|
|
|
@ -34,6 +34,9 @@ public:
|
|||
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);
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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