diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index fe6cc1b2e..c3c24feef 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -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 diff --git a/Source/CTest/cmCTestCoverageHandler.cxx b/Source/CTest/cmCTestCoverageHandler.cxx index da27c8cf9..76f65840a 100644 --- a/Source/CTest/cmCTestCoverageHandler.cxx +++ b/Source/CTest/cmCTestCoverageHandler.cxx @@ -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 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 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(cont->TotalCoverage.size()); +} + + //---------------------------------------------------------------------- int cmCTestCoverageHandler::HandleGCovCoverage( cmCTestCoverageHandlerContainer* cont) diff --git a/Source/CTest/cmCTestCoverageHandler.h b/Source/CTest/cmCTestCoverageHandler.h index 38a33532d..d0f274c81 100644 --- a/Source/CTest/cmCTestCoverageHandler.h +++ b/Source/CTest/cmCTestCoverageHandler.h @@ -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, diff --git a/Source/CTest/cmParseJacocoCoverage.cxx b/Source/CTest/cmParseJacocoCoverage.cxx new file mode 100644 index 000000000..4723dd398 --- /dev/null +++ b/Source/CTest/cmParseJacocoCoverage.cxx @@ -0,0 +1,167 @@ +#include "cmStandardIncludes.h" +#include +#include +#include "cmSystemTools.h" +#include "cmXMLParser.h" +#include "cmParseJacocoCoverage.h" +#include +#include +#include + + +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 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 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; +} diff --git a/Source/CTest/cmParseJacocoCoverage.h b/Source/CTest/cmParseJacocoCoverage.h new file mode 100644 index 000000000..dad05a32e --- /dev/null +++ b/Source/CTest/cmParseJacocoCoverage.h @@ -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 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& 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 RoutineToDirectory; + cmCTestCoverageHandlerContainer& Coverage; + cmCTest* CTest; +}; + +#endif diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 05442a1c8..852f78fe9 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -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 + $ -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") diff --git a/Tests/JacocoCoverage/Coverage/src/main/java/org/cmake/CoverageTest.java b/Tests/JacocoCoverage/Coverage/src/main/java/org/cmake/CoverageTest.java new file mode 100644 index 000000000..4fb43c608 --- /dev/null +++ b/Tests/JacocoCoverage/Coverage/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; + } + } +} diff --git a/Tests/JacocoCoverage/Coverage/target/site/jacoco.xml b/Tests/JacocoCoverage/Coverage/target/site/jacoco.xml new file mode 100644 index 000000000..49c3e8740 --- /dev/null +++ b/Tests/JacocoCoverage/Coverage/target/site/jacoco.xml @@ -0,0 +1 @@ + diff --git a/Tests/JacocoCoverage/DartConfiguration.tcl.in b/Tests/JacocoCoverage/DartConfiguration.tcl.in new file mode 100644 index 000000000..cc10e9c76 --- /dev/null +++ b/Tests/JacocoCoverage/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_BINARY_DIR}/Testing/JacocoCoverage +BuildDirectory: ${CMake_BINARY_DIR}/Testing/JacocoCoverage