diff options
author | Joseph Snyder <joe.snyder@kitware.com> | 2014-05-29 17:47:31 (GMT) |
---|---|---|
committer | Joseph Snyder <joe.snyder@kitware.com> | 2014-06-04 14:26:19 (GMT) |
commit | 50daf239b001ddda61449044e75bb98651e99d21 (patch) | |
tree | ce47d4d773037e2515149de475c7edeee6baff17 | |
parent | a2822d30899f3fc2ad96abfdf5de0d0ce4166139 (diff) | |
download | CMake-50daf239b001ddda61449044e75bb98651e99d21.zip CMake-50daf239b001ddda61449044e75bb98651e99d21.tar.gz CMake-50daf239b001ddda61449044e75bb98651e99d21.tar.bz2 |
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.
-rw-r--r-- | Help/variable/CTEST_COVERAGE_COMMAND.rst | 55 | ||||
-rw-r--r-- | Source/CTest/cmParseCoberturaCoverage.cxx | 67 | ||||
-rw-r--r-- | Source/CTest/cmParseCoberturaCoverage.h | 3 | ||||
-rw-r--r-- | Tests/CMakeLists.txt | 18 | ||||
-rw-r--r-- | Tests/CoberturaCoverage/DartConfiguration.tcl.in | 8 | ||||
-rw-r--r-- | Tests/CoberturaCoverage/coverage.xml.in | 112 | ||||
-rw-r--r-- | Tests/CoberturaCoverage/src/main/java/org/cmake/CoverageTest.java | 52 |
7 files changed, 306 insertions, 9 deletions
diff --git a/Help/variable/CTEST_COVERAGE_COMMAND.rst b/Help/variable/CTEST_COVERAGE_COMMAND.rst index 1a8e499..a669dd7 100644 --- a/Help/variable/CTEST_COVERAGE_COMMAND.rst +++ b/Help/variable/CTEST_COVERAGE_COMMAND.rst @@ -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/ diff --git a/Source/CTest/cmParseCoberturaCoverage.cxx b/Source/CTest/cmParseCoberturaCoverage.cxx index 0f5daba..6b98056 100644 --- a/Source/CTest/cmParseCoberturaCoverage.cxx +++ b/Source/CTest/cmParseCoberturaCoverage.cxx @@ -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; + { + 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; diff --git a/Source/CTest/cmParseCoberturaCoverage.h b/Source/CTest/cmParseCoberturaCoverage.h index 4204b10..ff5954d 100644 --- a/Source/CTest/cmParseCoberturaCoverage.h +++ b/Source/CTest/cmParseCoberturaCoverage.h @@ -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); diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 8d2b7fc..05442a1 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -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") diff --git a/Tests/CoberturaCoverage/DartConfiguration.tcl.in b/Tests/CoberturaCoverage/DartConfiguration.tcl.in new file mode 100644 index 0000000..954f59a --- /dev/null +++ b/Tests/CoberturaCoverage/DartConfiguration.tcl.in @@ -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 diff --git a/Tests/CoberturaCoverage/coverage.xml.in b/Tests/CoberturaCoverage/coverage.xml.in new file mode 100644 index 0000000..b3f6691 --- /dev/null +++ b/Tests/CoberturaCoverage/coverage.xml.in @@ -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> diff --git a/Tests/CoberturaCoverage/src/main/java/org/cmake/CoverageTest.java b/Tests/CoberturaCoverage/src/main/java/org/cmake/CoverageTest.java new file mode 100644 index 0000000..4fb43c6 --- /dev/null +++ b/Tests/CoberturaCoverage/src/main/java/org/cmake/CoverageTest.java @@ -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; + } + } +} |