CTest: Add Jacoco Coverage functionality

Add the ability to parse the XML output of the Jacoco tool.

Jacoco (www.eclemma.org/jacoco) is a Java coverage tool.
Add and integrate a class for the parser and
include a test which utilizes the new parser.
This commit is contained in:
Joseph Snyder 2014-05-21 19:19:35 +00:00
parent 47cde18849
commit 558c2190e8
9 changed files with 348 additions and 1 deletions

View File

@ -518,6 +518,7 @@ set(CTEST_SRCS cmCTest.cxx
CTest/cmParseMumpsCoverage.cxx
CTest/cmParseCacheCoverage.cxx
CTest/cmParseGTMCoverage.cxx
CTest/cmParseJacocoCoverage.cxx
CTest/cmParsePHPCoverage.cxx
CTest/cmParseCoberturaCoverage.cxx
CTest/cmCTestEmptyBinaryDirectoryCommand.cxx

View File

@ -14,6 +14,7 @@
#include "cmParseCoberturaCoverage.h"
#include "cmParseGTMCoverage.h"
#include "cmParseCacheCoverage.h"
#include "cmParseJacocoCoverage.h"
#include "cmCTest.h"
#include "cmake.h"
#include "cmMakefile.h"
@ -415,6 +416,13 @@ int cmCTestCoverageHandler::ProcessHandler()
return error;
}
file_count += this->HandleJacocoCoverage(&cont);
error = cont.Error;
if ( file_count < 0 )
{
return error;
}
std::set<std::string> uncovered = this->FindUncoveredFiles(&cont);
if ( file_count == 0 )
@ -871,6 +879,38 @@ struct cmCTestCoverageHandlerLocale
std::string lc_all;
};
//----------------------------------------------------------------------
int cmCTestCoverageHandler::HandleJacocoCoverage(
cmCTestCoverageHandlerContainer* cont)
{
cmParseJacocoCoverage cov =
cmParseJacocoCoverage(*cont, this->CTest);
cmsys::Glob g;
std::vector<std::string> files;
g.SetRecurse(true);
std::string SourceDir
= this->CTest->GetCTestConfiguration("SourceDirectory");
std::string coverageFile = SourceDir+ "/*jacoco.xml";
g.FindFiles(coverageFile);
files=g.GetFiles();
if (files.size() > 0)
{
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Found Jacoco Files, Performing Coverage" << std::endl);
cov.LoadCoverageData(files);
}
else
{
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
" Cannot find Jacoco coverage files: " << coverageFile
<< std::endl);
}
return static_cast<int>(cont->TotalCoverage.size());
}
//----------------------------------------------------------------------
int cmCTestCoverageHandler::HandleGCovCoverage(
cmCTestCoverageHandlerContainer* cont)

View File

@ -81,7 +81,10 @@ private:
//! Handle coverage for mumps
int HandleMumpsCoverage(cmCTestCoverageHandlerContainer* cont);
//! Handle coverage using Bullseye
//! Handle coverage for Jacoco
int HandleJacocoCoverage(cmCTestCoverageHandlerContainer* cont);
//! Handle coverage using Bullseye
int HandleBullseyeCoverage(cmCTestCoverageHandlerContainer* cont);
int RunBullseyeSourceSummary(cmCTestCoverageHandlerContainer* cont);
int RunBullseyeCoverageBranch(cmCTestCoverageHandlerContainer* cont,

View File

@ -0,0 +1,167 @@
#include "cmStandardIncludes.h"
#include <stdio.h>
#include <stdlib.h>
#include "cmSystemTools.h"
#include "cmXMLParser.h"
#include "cmParseJacocoCoverage.h"
#include <cmsys/Directory.hxx>
#include <cmsys/Glob.hxx>
#include <cmsys/FStream.hxx>
class cmParseJacocoCoverage::XMLParser: public cmXMLParser
{
public:
XMLParser(cmCTest* ctest, cmCTestCoverageHandlerContainer& cont)
: CTest(ctest), Coverage(cont)
{
this->PackageName = "";
this->ModuleName = "";
this->FileName = "";
this->CurFileName = "";
this->FilePaths.push_back(this->Coverage.SourceDir);
}
virtual ~XMLParser()
{
}
protected:
virtual void EndElement(const std::string&)
{
}
virtual void StartElement(const std::string& name,
const char** atts)
{
if(name == "package")
{
this->PackageName = atts[1];
std::string FilePath = this->Coverage.SourceDir +
"/" + this->ModuleName + "/src/main/java/" +
this->PackageName;
this->FilePaths.push_back(FilePath);
}
else if(name == "sourcefile")
{
this->FileName = atts[1];
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Reading file: "
<< this->FileName << std::endl);
for(size_t i=0;i < FilePaths.size();i++)
{
std::string finalpath = FilePaths[i] + "/" + this->FileName;
if(cmSystemTools::FileExists(finalpath.c_str()))
{
this->CurFileName = finalpath;
break;
}
}
cmsys::ifstream fin(this->CurFileName.c_str());
if(this->CurFileName == "" || !fin )
{
this->CurFileName = this->Coverage.BinaryDir + "/" +
this->FileName;
fin.open(this->CurFileName.c_str());
if (!fin)
{
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Jacoco Coverage: Error opening " << this->CurFileName
<< std::endl);
this->Coverage.Error++;
}
}
std::string line;
FileLinesType& curFileLines =
this->Coverage.TotalCoverage[this->CurFileName];
curFileLines.push_back(-1);
while(cmSystemTools::GetLineFromStream(fin, line))
{
curFileLines.push_back(-1);
}
}
else if(name == "report")
{
this->ModuleName=atts[1];
}
else if(name == "line")
{
int tagCount = 0;
int nr = -1;
int ci = -1;
while(true)
{
if(strcmp(atts[tagCount],"ci") == 0)
{
ci = atoi(atts[tagCount+1]);
}
else if (strcmp(atts[tagCount],"nr") == 0)
{
nr = atoi(atts[tagCount+1]);
}
if (ci > -1 && nr > 0)
{
FileLinesType& curFileLines=
this->Coverage.TotalCoverage[this->CurFileName];
if(curFileLines.size() > 0)
{
curFileLines[nr-1] = ci;
}
break;
}
++tagCount;
}
}
}
private:
std::string PackageName;
std::string FileName;
std::string ModuleName;
std::string CurFileName;
std::vector<std::string> FilePaths;
typedef cmCTestCoverageHandlerContainer::SingleFileCoverageVector
FileLinesType;
cmCTest* CTest;
cmCTestCoverageHandlerContainer& Coverage;
};
cmParseJacocoCoverage::cmParseJacocoCoverage(
cmCTestCoverageHandlerContainer& cont,
cmCTest* ctest)
:Coverage(cont), CTest(ctest)
{
}
bool cmParseJacocoCoverage::LoadCoverageData(
const std::vector<std::string> files)
{
// load all the jacoco.xml files in the source directory
cmsys::Directory dir;
size_t i;
std::string path;
size_t numf = files.size();
for (i = 0; i < numf; i++)
{
path = files[i];
cmCTestLog(this->CTest,HANDLER_VERBOSE_OUTPUT,
"Reading XML File " << path << std::endl);
if(cmSystemTools::GetFilenameLastExtension(path) == ".xml")
{
if(!this->ReadJacocoXML(path.c_str()))
{
return false;
}
}
}
return true;
}
bool cmParseJacocoCoverage::ReadJacocoXML(const char* file)
{
cmParseJacocoCoverage::XMLParser
parser(this->CTest, this->Coverage);
parser.ParseFile(file);
return true;
}

View File

@ -0,0 +1,59 @@
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2000-2009 Kitware, Inc.
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#ifndef cmParseJacocoCoverage_h
#define cmParseJacocoCoverage_h
#include "cmStandardIncludes.h"
#include "cmCTestCoverageHandler.h"
/** \class cmParseJacocoCoverage
* \brief Parse JaCoCO coverage information
*
* This class is used to parse coverage information for
* java using the JaCoCo tool:
*
* http://www.eclemma.org/jacoco/trunk/index.html
*/
class cmParseJacocoCoverage
{
public:
cmParseJacocoCoverage(cmCTestCoverageHandlerContainer& cont,
cmCTest* ctest);
bool LoadCoverageData(const std::vector<std::string> files);
std::string PackageName;
std::string FileName;
std::string ModuleName;
std::string CurFileName;
private:
// implement virtual from parent
// remove files with no coverage
void RemoveUnCoveredFiles();
// Read a single mcov file
bool ReadJacocoXML(const char* f);
// split a string based on ,
bool SplitString(std::vector<std::string>& args,
std::string const& line);
bool FindJavaFile(std::string const& routine,
std::string& filepath);
void InitializeJavaFile(std::string& file);
bool LoadSource(std::string d);
class XMLParser;
std::map<std::string, std::string> RoutineToDirectory;
cmCTestCoverageHandlerContainer& Coverage;
cmCTest* CTest;
};
#endif

View File

@ -2202,6 +2202,22 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release
"Process file.*CoverageTest.java.*Total LOC:.*18.*Percentage Coverage: 72.22.*"
ENVIRONMENT COVFILE=)
# Adding a test case for JaCoCo Coverage
configure_file(
"${CMake_SOURCE_DIR}/Tests/JacocoCoverage/DartConfiguration.tcl.in"
"${CMake_BINARY_DIR}/Testing/JacocoCoverage/DartConfiguration.tcl")
file(COPY "${CMake_SOURCE_DIR}/Tests/JacocoCoverage/Coverage"
DESTINATION "${CMake_BINARY_DIR}/Testing/JacocoCoverage")
add_test(NAME CTestJacocoCoverage
COMMAND cmake -E chdir
${CMake_BINARY_DIR}/Testing/JacocoCoverage
$<TARGET_FILE:ctest> -T Coverage --debug)
set_tests_properties(CTestJacocoCoverage PROPERTIES
PASS_REGULAR_EXPRESSION
"Process file.*CoverageTest.java.*Total LOC:.*17.*Percentage Coverage: 76.47*"
ENVIRONMENT COVFILE=)
function(add_config_tests cfg)
set(base "${CMake_BINARY_DIR}/Tests/CTestConfig")

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;
}
}
}

View File

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><!DOCTYPE report PUBLIC "-//JACOCO//DTD Report 1.0//EN" "report.dtd"><report name="Coverage"><sessioninfo id="vagrant-ubuntu-precise-32-f1c264e9" start="1402427058670" dump="1402427059269"/><package name="org/cmake"><class name="org/cmake/Coverage/CoverageTest"><method name="&lt;init&gt;" desc="()V" line="8"><counter type="INSTRUCTION" missed="7" covered="0"/><counter type="LINE" missed="2" covered="0"/><counter type="COMPLEXITY" missed="1" covered="0"/><counter type="METHOD" missed="1" covered="0"/></method><method name="equalsVarOne" desc="(Ljava/lang/String;)Ljava/lang/Boolean;" line="16"><counter type="INSTRUCTION" missed="3" covered="7"/><counter type="BRANCH" missed="1" covered="1"/><counter type="LINE" missed="1" covered="2"/><counter type="COMPLEXITY" missed="1" covered="1"/><counter type="METHOD" missed="0" covered="1"/></method><method name="equalsVarTwo" desc="(Ljava/lang/String;)Z" line="26"><counter type="INSTRUCTION" missed="0" covered="8"/><counter type="BRANCH" missed="0" covered="2"/><counter type="LINE" missed="0" covered="3"/><counter type="COMPLEXITY" missed="0" covered="2"/><counter type="METHOD" missed="0" covered="1"/></method><method name="timesIntOne" desc="(Ljava/lang/Integer;)Ljava/lang/Integer;" line="36"><counter type="INSTRUCTION" missed="8" covered="0"/><counter type="LINE" missed="1" covered="0"/><counter type="COMPLEXITY" missed="1" covered="0"/><counter type="METHOD" missed="1" covered="0"/></method><method name="whileLoop" desc="(Ljava/lang/Integer;)Z" line="41"><counter type="INSTRUCTION" missed="0" covered="24"/><counter type="BRANCH" missed="0" covered="4"/><counter type="LINE" missed="0" covered="6"/><counter type="COMPLEXITY" missed="0" covered="3"/><counter type="METHOD" missed="0" covered="1"/></method><method name="&lt;clinit&gt;" desc="()V" line="10"><counter type="INSTRUCTION" missed="0" covered="5"/><counter type="LINE" missed="0" covered="2"/><counter type="COMPLEXITY" missed="0" covered="1"/><counter type="METHOD" missed="0" covered="1"/></method><counter type="INSTRUCTION" missed="18" covered="44"/><counter type="BRANCH" missed="1" covered="7"/><counter type="LINE" missed="4" covered="13"/><counter type="COMPLEXITY" missed="3" covered="7"/><counter type="METHOD" missed="2" covered="4"/><counter type="CLASS" missed="0" covered="1"/></class><sourcefile name="CoverageTest.java"><line nr="8" mi="2" ci="0" mb="0" cb="0"/><line nr="10" mi="0" ci="2" mb="0" cb="0"/><line nr="11" mi="0" ci="3" mb="0" cb="0"/><line nr="12" mi="5" ci="0" mb="0" cb="0"/><line nr="16" mi="0" ci="4" mb="1" cb="1"/><line nr="17" mi="0" ci="3" mb="0" cb="0"/><line nr="20" mi="3" ci="0" mb="0" cb="0"/><line nr="26" mi="0" ci="4" mb="0" cb="2"/><line nr="27" mi="0" ci="2" mb="0" cb="0"/><line nr="30" mi="0" ci="2" mb="0" cb="0"/><line nr="36" mi="8" ci="0" mb="0" cb="0"/><line nr="41" mi="0" ci="3" mb="0" cb="0"/><line nr="42" mi="0" ci="5" mb="0" cb="2"/><line nr="43" mi="0" ci="7" mb="0" cb="0"/><line nr="45" mi="0" ci="5" mb="0" cb="2"/><line nr="46" mi="0" ci="2" mb="0" cb="0"/><line nr="49" mi="0" ci="2" mb="0" cb="0"/><counter type="INSTRUCTION" missed="18" covered="44"/><counter type="BRANCH" missed="1" covered="7"/><counter type="LINE" missed="4" covered="13"/><counter type="COMPLEXITY" missed="3" covered="7"/><counter type="METHOD" missed="2" covered="4"/><counter type="CLASS" missed="0" covered="1"/></sourcefile><counter type="INSTRUCTION" missed="18" covered="44"/><counter type="BRANCH" missed="1" covered="7"/><counter type="LINE" missed="4" covered="13"/><counter type="COMPLEXITY" missed="3" covered="7"/><counter type="METHOD" missed="2" covered="4"/><counter type="CLASS" missed="0" covered="1"/></package><counter type="INSTRUCTION" missed="18" covered="44"/><counter type="BRANCH" missed="1" covered="7"/><counter type="LINE" missed="4" covered="13"/><counter type="COMPLEXITY" missed="3" covered="7"/><counter type="METHOD" missed="2" covered="4"/><counter type="CLASS" missed="0" covered="1"/></report>

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_BINARY_DIR}/Testing/JacocoCoverage
BuildDirectory: ${CMake_BINARY_DIR}/Testing/JacocoCoverage