cmParseDelphiCoverage.cxx 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. #include "cmParseDelphiCoverage.h"
  2. #include <cstdio>
  3. #include <cstdlib>
  4. #include "cmsys/FStream.hxx"
  5. #include "cmsys/Glob.hxx"
  6. #include "cmCTest.h"
  7. #include "cmCTestCoverageHandler.h"
  8. #include "cmSystemTools.h"
  9. class cmParseDelphiCoverage::HTMLParser
  10. {
  11. public:
  12. using FileLinesType =
  13. cmCTestCoverageHandlerContainer::SingleFileCoverageVector;
  14. HTMLParser(cmCTest* ctest, cmCTestCoverageHandlerContainer& cont)
  15. : CTest(ctest)
  16. , Coverage(cont)
  17. {
  18. }
  19. virtual ~HTMLParser() = default;
  20. bool initializeDelphiFile(
  21. std::string const& filename,
  22. cmParseDelphiCoverage::HTMLParser::FileLinesType& coverageVector)
  23. {
  24. std::string line;
  25. size_t comPos;
  26. size_t semiPos;
  27. bool blockComFlag = false;
  28. bool lineComFlag = false;
  29. std::vector<std::string> beginSet;
  30. cmsys::ifstream in(filename.c_str());
  31. if (!in) {
  32. return false;
  33. }
  34. while (cmSystemTools::GetLineFromStream(in, line)) {
  35. lineComFlag = false;
  36. // Unique cases found in lines.
  37. size_t beginPos = line.find("begin");
  38. // Check that the begin is the first non-space string on the line
  39. if ((beginPos == line.find_first_not_of(' ')) &&
  40. beginPos != std::string::npos) {
  41. beginSet.emplace_back("begin");
  42. coverageVector.push_back(-1);
  43. continue;
  44. }
  45. if (line.find('{') != std::string::npos) {
  46. blockComFlag = true;
  47. } else if (line.find('}') != std::string::npos) {
  48. blockComFlag = false;
  49. coverageVector.push_back(-1);
  50. continue;
  51. } else if ((line.find("end;") != std::string::npos) &&
  52. !beginSet.empty()) {
  53. beginSet.pop_back();
  54. coverageVector.push_back(-1);
  55. continue;
  56. }
  57. // This checks for comments after lines of code, finding the
  58. // comment symbol after the ending semicolon.
  59. comPos = line.find("//");
  60. if (comPos != std::string::npos) {
  61. semiPos = line.find(';');
  62. if (comPos < semiPos) {
  63. lineComFlag = true;
  64. }
  65. }
  66. // Based up what was found, add a line to the coverageVector
  67. if (!beginSet.empty() && !line.empty() && !blockComFlag &&
  68. !lineComFlag) {
  69. coverageVector.push_back(0);
  70. } else {
  71. coverageVector.push_back(-1);
  72. }
  73. }
  74. return true;
  75. }
  76. bool ParseFile(const char* file)
  77. {
  78. std::string line = file;
  79. std::string lineresult;
  80. std::string lastroutine;
  81. std::string filename;
  82. std::string filelineoffset;
  83. size_t afterLineNum = 0;
  84. size_t lastoffset = 0;
  85. size_t endcovpos = 0;
  86. size_t endnamepos = 0;
  87. size_t pos = 0;
  88. /*
  89. * This first 'while' section goes through the found HTML
  90. * file name and attempts to capture the source file name
  91. * which is set as part of the HTML file name: the name of
  92. * the file is found in parenthesis '()'
  93. *
  94. * See test HTML file name: UTCovTest(UTCovTest.pas).html.
  95. *
  96. * Find the text inside each pair of parenthesis and check
  97. * to see if it ends in '.pas'. If it can't be found,
  98. * exit the function.
  99. */
  100. while (true) {
  101. lastoffset = line.find('(', pos);
  102. if (lastoffset == std::string::npos) {
  103. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  104. endnamepos << "File not found " << lastoffset
  105. << std::endl,
  106. this->Coverage.Quiet);
  107. return false;
  108. }
  109. endnamepos = line.find(')', lastoffset);
  110. filename = line.substr(lastoffset + 1, (endnamepos - 1) - lastoffset);
  111. if (filename.find(".pas") != std::string::npos) {
  112. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  113. "Coverage found for file: " << filename
  114. << std::endl,
  115. this->Coverage.Quiet);
  116. break;
  117. }
  118. pos = lastoffset + 1;
  119. }
  120. /*
  121. * Glob through the source directory for the
  122. * file found above
  123. */
  124. cmsys::Glob gl;
  125. gl.RecurseOn();
  126. gl.RecurseThroughSymlinksOff();
  127. std::string glob = this->Coverage.SourceDir + "*/" + filename;
  128. gl.FindFiles(glob);
  129. std::vector<std::string> const& files = gl.GetFiles();
  130. if (files.empty()) {
  131. /*
  132. * If that doesn't find any matching files
  133. * return a failure.
  134. */
  135. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  136. "Unable to find file matching" << glob << std::endl,
  137. this->Coverage.Quiet);
  138. return false;
  139. }
  140. FileLinesType& coverageVector = this->Coverage.TotalCoverage[files[0]];
  141. /*
  142. * Initialize the file to have all code between 'begin' and
  143. * 'end' tags marked as executable
  144. */
  145. this->initializeDelphiFile(files[0], coverageVector);
  146. cmsys::ifstream in(file);
  147. if (!in) {
  148. return false;
  149. }
  150. /*
  151. * Now read the HTML file, looking for the lines that have an
  152. * "inline" in it. Then parse out the "class" value of that
  153. * line to determine if the line is executed or not.
  154. *
  155. * Sample HTML line:
  156. *
  157. * <tr class="covered"><td>47</td><td><pre style="display:inline;">
  158. * &nbsp;CheckEquals(1,2-1);</pre></td></tr>
  159. *
  160. */
  161. while (cmSystemTools::GetLineFromStream(in, line)) {
  162. if (line.find("inline") == std::string::npos) {
  163. continue;
  164. }
  165. lastoffset = line.find("class=");
  166. endcovpos = line.find('>', lastoffset);
  167. lineresult = line.substr(lastoffset + 7, (endcovpos - 8) - lastoffset);
  168. if (lineresult == "covered") {
  169. afterLineNum = line.find('<', endcovpos + 5);
  170. filelineoffset =
  171. line.substr(endcovpos + 5, afterLineNum - (endcovpos + 5));
  172. coverageVector[atoi(filelineoffset.c_str()) - 1] = 1;
  173. }
  174. }
  175. return true;
  176. }
  177. private:
  178. cmCTest* CTest;
  179. cmCTestCoverageHandlerContainer& Coverage;
  180. };
  181. cmParseDelphiCoverage::cmParseDelphiCoverage(
  182. cmCTestCoverageHandlerContainer& cont, cmCTest* ctest)
  183. : Coverage(cont)
  184. , CTest(ctest)
  185. {
  186. }
  187. bool cmParseDelphiCoverage::LoadCoverageData(
  188. std::vector<std::string> const& files)
  189. {
  190. size_t i;
  191. std::string path;
  192. size_t numf = files.size();
  193. for (i = 0; i < numf; i++) {
  194. path = files[i];
  195. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  196. "Reading HTML File " << path << std::endl,
  197. this->Coverage.Quiet);
  198. if (cmSystemTools::GetFilenameLastExtension(path) == ".html") {
  199. if (!this->ReadDelphiHTML(path.c_str())) {
  200. return false;
  201. }
  202. }
  203. }
  204. return true;
  205. }
  206. bool cmParseDelphiCoverage::ReadDelphiHTML(const char* file)
  207. {
  208. cmParseDelphiCoverage::HTMLParser parser(this->CTest, this->Coverage);
  209. parser.ParseFile(file);
  210. return true;
  211. }