cmParseDelphiCoverage.cxx 6.5 KB

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