| #include "cmParseJacocoCoverage.h" | |
| #include <algorithm> | |
| #include <cstdlib> | |
| #include <cstring> | |
| #include "cmsys/Directory.hxx" | |
| #include "cmsys/FStream.hxx" | |
| #include "cmsys/Glob.hxx" | |
| #include "cmCTest.h" | |
| #include "cmCTestCoverageHandler.h" | |
| #include "cmStringAlgorithms.h" | |
| #include "cmSystemTools.h" | |
| #include "cmXMLParser.h" | |
| class cmParseJacocoCoverage::XMLParser : public cmXMLParser | |
| { | |
| public: | |
| XMLParser(cmCTest* ctest, cmCTestCoverageHandlerContainer& cont) | |
| : CTest(ctest) | |
| , Coverage(cont) | |
| { | |
| } | |
| protected: | |
| void EndElement(std::string const& /*name*/) override {} | |
| void StartElement(std::string const& name, char const** atts) override | |
| { | |
| if (name == "package") { | |
| this->PackageName = atts[1]; | |
| this->PackagePath.clear(); | |
| } else if (name == "sourcefile") { | |
| this->FilePath.clear(); | |
| std::string fileName = atts[1]; | |
| if (this->PackagePath.empty()) { | |
| if (!this->FindPackagePath(fileName)) { | |
| cmCTestLog(this->CTest, ERROR_MESSAGE, | |
| "Cannot find file: ", this->PackageName, '/', fileName, | |
| '\n'); | |
| this->Coverage.Error++; | |
| return; | |
| } | |
| } | |
| cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, | |
| this->Coverage.Quiet, "Reading file: ", fileName, | |
| '\n'); | |
| this->FilePath = cmStrCat(this->PackagePath, '/', fileName); | |
| cmsys::ifstream fin(this->FilePath.c_str()); | |
| if (!fin) { | |
| cmCTestLog(this->CTest, ERROR_MESSAGE, | |
| "Jacoco Coverage: Error opening ", this->FilePath, '\n'); | |
| } | |
| std::string line; | |
| FileLinesType& curFileLines = | |
| this->Coverage.TotalCoverage[this->FilePath]; | |
| if (fin) { | |
| curFileLines.push_back(-1); | |
| } | |
| while (cmSystemTools::GetLineFromStream(fin, line)) { | |
| curFileLines.push_back(-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->FilePath]; | |
| if (!curFileLines.empty()) { | |
| curFileLines[nr - 1] = ci; | |
| } | |
| break; | |
| } | |
| ++tagCount; | |
| } | |
| } | |
| } | |
| virtual bool FindPackagePath(std::string const& fileName) | |
| { | |
| // Search for the source file in the source directory. | |
| if (this->PackagePathFound(fileName, this->Coverage.SourceDir)) { | |
| return true; | |
| } | |
| // If not found there, check the binary directory. | |
| if (this->PackagePathFound(fileName, this->Coverage.BinaryDir)) { | |
| return true; | |
| } | |
| return false; | |
| } | |
| virtual bool PackagePathFound(std::string const& fileName, | |
| std::string const& baseDir) | |
| { | |
| // Search for the file in the baseDir and its subdirectories. | |
| std::string packageGlob = cmStrCat(baseDir, '/', fileName); | |
| cmsys::Glob gl; | |
| gl.RecurseOn(); | |
| gl.RecurseThroughSymlinksOn(); | |
| gl.FindFiles(packageGlob); | |
| std::vector<std::string> const& files = gl.GetFiles(); | |
| if (files.empty()) { | |
| return false; | |
| } | |
| // Check if any of the locations found match our package. | |
| return std::any_of( | |
| files.begin(), files.end(), [this, &fileName](std::string const& f) { | |
| std::string dir = cmsys::SystemTools::GetParentDirectory(f); | |
| if (cmHasSuffix(dir, this->PackageName)) { | |
| cmCTestOptionalLog( | |
| this->CTest, HANDLER_VERBOSE_OUTPUT, this->Coverage.Quiet, | |
| "Found package directory for ", fileName, ": ", dir, '\n'); | |
| this->PackagePath = dir; | |
| return true; | |
| } | |
| return false; | |
| }); | |
| } | |
| private: | |
| std::string FilePath; | |
| std::string PackagePath; | |
| std::string PackageName; | |
| using FileLinesType = | |
| cmCTestCoverageHandlerContainer::SingleFileCoverageVector; | |
| cmCTest* CTest; | |
| cmCTestCoverageHandlerContainer& Coverage; | |
| }; | |
| cmParseJacocoCoverage::cmParseJacocoCoverage( | |
| cmCTestCoverageHandlerContainer& cont, cmCTest* ctest) | |
| : Coverage(cont) | |
| , CTest(ctest) | |
| { | |
| } | |
| bool cmParseJacocoCoverage::LoadCoverageData( | |
| std::vector<std::string> const& 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]; | |
| cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, | |
| this->Coverage.Quiet, "Reading XML File ", path, '\n'); | |
| if (cmSystemTools::GetFilenameLastExtension(path) == ".xml") { | |
| if (!this->ReadJacocoXML(path.c_str())) { | |
| return false; | |
| } | |
| } | |
| } | |
| return true; | |
| } | |
| bool cmParseJacocoCoverage::ReadJacocoXML(char const* file) | |
| { | |
| cmParseJacocoCoverage::XMLParser parser(this->CTest, this->Coverage); | |
| parser.ParseFile(file); | |
| return true; | |
| } |