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