1 #include "cmParseCoberturaCoverage.h"
2
3 #include <cstdlib>
4 #include <cstring>
5
6 #include "cmsys/FStream.hxx"
7
8 #include "cmCTest.h"
9 #include "cmCTestCoverageHandler.h"
10 #include "cmStringAlgorithms.h"
11 #include "cmSystemTools.h"
12 #include "cmXMLParser.h"
13
14 class cmParseCoberturaCoverage::XMLParser : public cmXMLParser
15 {
16 public:
XMLParser(cmCTest * ctest,cmCTestCoverageHandlerContainer & cont)17 XMLParser(cmCTest* ctest, cmCTestCoverageHandlerContainer& cont)
18 : FilePaths{ cont.SourceDir, cont.BinaryDir }
19 , CTest(ctest)
20 , Coverage(cont)
21 {
22 }
23
24 protected:
EndElement(const std::string & name)25 void EndElement(const std::string& name) override
26 {
27 if (name == "source") {
28 this->InSource = false;
29 } else if (name == "sources") {
30 this->InSources = false;
31 } else if (name == "class") {
32 this->SkipThisClass = false;
33 }
34 }
35
CharacterDataHandler(const char * data,int length)36 void CharacterDataHandler(const char* data, int length) override
37 {
38 std::string tmp;
39 tmp.insert(0, data, length);
40 if (this->InSources && this->InSource) {
41 this->FilePaths.push_back(tmp);
42 cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
43 "Adding Source: " << tmp << std::endl,
44 this->Coverage.Quiet);
45 }
46 }
47
StartElement(const std::string & name,const char ** atts)48 void StartElement(const std::string& name, const char** atts) override
49 {
50 std::string FoundSource;
51 std::string finalpath;
52 if (name == "source") {
53 this->InSource = true;
54 } else if (name == "sources") {
55 this->InSources = true;
56 } else if (name == "class") {
57 int tagCount = 0;
58 while (true) {
59 if (strcmp(atts[tagCount], "filename") == 0) {
60 cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
61 "Reading file: " << atts[tagCount + 1]
62 << std::endl,
63 this->Coverage.Quiet);
64 std::string filename = atts[tagCount + 1];
65 this->CurFileName.clear();
66
67 // Check if this is an absolute path that falls within our
68 // source or binary directories.
69 for (std::string const& filePath : this->FilePaths) {
70 if (cmHasPrefix(filename, filePath)) {
71 this->CurFileName = filename;
72 break;
73 }
74 }
75
76 if (this->CurFileName.empty()) {
77 // Check if this is a path that is relative to our source or
78 // binary directories.
79 for (std::string const& filePath : this->FilePaths) {
80 finalpath = cmStrCat(filePath, "/", filename);
81 if (cmSystemTools::FileExists(finalpath)) {
82 this->CurFileName = finalpath;
83 break;
84 }
85 }
86 }
87
88 cmsys::ifstream fin(this->CurFileName.c_str());
89 if (this->CurFileName.empty() || !fin) {
90 this->CurFileName =
91 cmStrCat(this->Coverage.BinaryDir, "/", atts[tagCount + 1]);
92 fin.open(this->CurFileName.c_str());
93 if (!fin) {
94 cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
95 "Skipping system file " << filename
96 << std::endl,
97 this->Coverage.Quiet);
98
99 this->SkipThisClass = true;
100 break;
101 }
102 }
103 std::string line;
104 FileLinesType& curFileLines =
105 this->Coverage.TotalCoverage[this->CurFileName];
106 while (cmSystemTools::GetLineFromStream(fin, line)) {
107 curFileLines.push_back(-1);
108 }
109
110 break;
111 }
112 ++tagCount;
113 }
114 } else if (name == "line") {
115 int tagCount = 0;
116 int curNumber = -1;
117 int curHits = -1;
118 while (true) {
119 if (this->SkipThisClass) {
120 break;
121 }
122 if (strcmp(atts[tagCount], "hits") == 0) {
123 curHits = atoi(atts[tagCount + 1]);
124 } else if (strcmp(atts[tagCount], "number") == 0) {
125 curNumber = atoi(atts[tagCount + 1]);
126 }
127
128 if (curHits > -1 && curNumber > 0) {
129 FileLinesType& curFileLines =
130 this->Coverage.TotalCoverage[this->CurFileName];
131 {
132 curFileLines[curNumber - 1] = curHits;
133 }
134 break;
135 }
136 ++tagCount;
137 }
138 }
139 }
140
141 private:
142 bool InSources = false;
143 bool InSource = false;
144 bool SkipThisClass = false;
145 std::vector<std::string> FilePaths;
146 using FileLinesType =
147 cmCTestCoverageHandlerContainer::SingleFileCoverageVector;
148 cmCTest* CTest;
149 cmCTestCoverageHandlerContainer& Coverage;
150 std::string CurFileName;
151 };
152
cmParseCoberturaCoverage(cmCTestCoverageHandlerContainer & cont,cmCTest * ctest)153 cmParseCoberturaCoverage::cmParseCoberturaCoverage(
154 cmCTestCoverageHandlerContainer& cont, cmCTest* ctest)
155 : Coverage(cont)
156 , CTest(ctest)
157 {
158 }
159
ReadCoverageXML(const char * xmlFile)160 bool cmParseCoberturaCoverage::ReadCoverageXML(const char* xmlFile)
161 {
162 cmParseCoberturaCoverage::XMLParser parser(this->CTest, this->Coverage);
163 parser.ParseFile(xmlFile);
164 return true;
165 }
166