cmParseCacheCoverage.cxx 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. #include "cmParseCacheCoverage.h"
  2. #include "cmCTest.h"
  3. #include "cmCTestCoverageHandler.h"
  4. #include "cmSystemTools.h"
  5. #include <cmsys/Directory.hxx>
  6. #include <cmsys/FStream.hxx>
  7. #include <map>
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <utility>
  11. cmParseCacheCoverage::cmParseCacheCoverage(
  12. cmCTestCoverageHandlerContainer& cont, cmCTest* ctest)
  13. : cmParseMumpsCoverage(cont, ctest)
  14. {
  15. }
  16. bool cmParseCacheCoverage::LoadCoverageData(const char* d)
  17. {
  18. // load all the .mcov files in the specified directory
  19. cmsys::Directory dir;
  20. if (!dir.Load(d)) {
  21. return false;
  22. }
  23. size_t numf;
  24. unsigned int i;
  25. numf = dir.GetNumberOfFiles();
  26. for (i = 0; i < numf; i++) {
  27. std::string file = dir.GetFile(i);
  28. if (file != "." && file != ".." && !cmSystemTools::FileIsDirectory(file)) {
  29. std::string path = d;
  30. path += "/";
  31. path += file;
  32. if (cmSystemTools::GetFilenameLastExtension(path) == ".cmcov") {
  33. if (!this->ReadCMCovFile(path.c_str())) {
  34. return false;
  35. }
  36. }
  37. }
  38. }
  39. return true;
  40. }
  41. // not currently used, but leave it in case we want it in the future
  42. void cmParseCacheCoverage::RemoveUnCoveredFiles()
  43. {
  44. // loop over the coverage data computed and remove all files
  45. // that only have -1 or 0 for the lines.
  46. cmCTestCoverageHandlerContainer::TotalCoverageMap::iterator ci =
  47. this->Coverage.TotalCoverage.begin();
  48. while (ci != this->Coverage.TotalCoverage.end()) {
  49. cmCTestCoverageHandlerContainer::SingleFileCoverageVector& v = ci->second;
  50. bool nothing = true;
  51. for (cmCTestCoverageHandlerContainer::SingleFileCoverageVector::iterator
  52. i = v.begin();
  53. i != v.end(); ++i) {
  54. if (*i > 0) {
  55. nothing = false;
  56. break;
  57. }
  58. }
  59. if (nothing) {
  60. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  61. "No coverage found in: " << ci->first << std::endl,
  62. this->Coverage.Quiet);
  63. this->Coverage.TotalCoverage.erase(ci++);
  64. } else {
  65. ++ci;
  66. }
  67. }
  68. }
  69. bool cmParseCacheCoverage::SplitString(std::vector<std::string>& args,
  70. std::string const& line)
  71. {
  72. std::string::size_type pos1 = 0;
  73. std::string::size_type pos2 = line.find(',', 0);
  74. if (pos2 == std::string::npos) {
  75. return false;
  76. }
  77. std::string arg;
  78. while (pos2 != std::string::npos) {
  79. arg = line.substr(pos1, pos2 - pos1);
  80. args.push_back(arg);
  81. pos1 = pos2 + 1;
  82. pos2 = line.find(',', pos1);
  83. }
  84. arg = line.substr(pos1);
  85. args.push_back(arg);
  86. return true;
  87. }
  88. bool cmParseCacheCoverage::ReadCMCovFile(const char* file)
  89. {
  90. cmsys::ifstream in(file);
  91. if (!in) {
  92. cmCTestLog(this->CTest, ERROR_MESSAGE, "Can not open : " << file << "\n");
  93. return false;
  94. }
  95. std::string line;
  96. std::vector<std::string> separateLine;
  97. if (!cmSystemTools::GetLineFromStream(in, line)) {
  98. cmCTestLog(this->CTest, ERROR_MESSAGE, "Empty file : "
  99. << file << " referenced in this line of cmcov data:\n"
  100. "["
  101. << line << "]\n");
  102. return false;
  103. }
  104. separateLine.clear();
  105. this->SplitString(separateLine, line);
  106. if (separateLine.size() != 4 || separateLine[0] != "Routine" ||
  107. separateLine[1] != "Line" || separateLine[2] != "RtnLine" ||
  108. separateLine[3] != "Code") {
  109. cmCTestLog(this->CTest, ERROR_MESSAGE,
  110. "Bad first line of cmcov file : " << file << " line:\n"
  111. "["
  112. << line << "]\n");
  113. }
  114. std::string routine;
  115. std::string filepath;
  116. while (cmSystemTools::GetLineFromStream(in, line)) {
  117. // clear out line argument vector
  118. separateLine.clear();
  119. // parse the comma separated line
  120. this->SplitString(separateLine, line);
  121. // might have more because code could have a quoted , in it
  122. // but we only care about the first 3 args anyway
  123. if (separateLine.size() < 4) {
  124. cmCTestLog(this->CTest, ERROR_MESSAGE,
  125. "Bad line of cmcov file expected at least 4 found: "
  126. << separateLine.size() << " " << file << " line:\n"
  127. "["
  128. << line << "]\n");
  129. for (std::string::size_type i = 0; i < separateLine.size(); ++i) {
  130. cmCTestLog(this->CTest, ERROR_MESSAGE, "" << separateLine[1] << " ");
  131. }
  132. cmCTestLog(this->CTest, ERROR_MESSAGE, "\n");
  133. return false;
  134. }
  135. // if we do not have a routine yet, then it should be
  136. // the first argument in the vector
  137. if (routine.empty()) {
  138. routine = separateLine[0];
  139. // Find the full path to the file
  140. if (!this->FindMumpsFile(routine, filepath)) {
  141. cmCTestLog(this->CTest, ERROR_MESSAGE,
  142. "Could not find mumps file for routine: " << routine
  143. << "\n");
  144. filepath = "";
  145. continue; // move to next line
  146. }
  147. }
  148. // if we have a routine name, check for end of routine
  149. else {
  150. // Totals in arg 0 marks the end of a routine
  151. if (separateLine[0].substr(0, 6) == "Totals") {
  152. routine = ""; // at the end of this routine
  153. filepath = "";
  154. continue; // move to next line
  155. }
  156. }
  157. // if the file path was not found for the routine
  158. // move to next line. We should have already warned
  159. // after the call to FindMumpsFile that we did not find
  160. // it, so don't report again to cut down on output
  161. if (filepath.empty()) {
  162. continue;
  163. }
  164. // now we are ready to set the coverage from the line of data
  165. cmCTestCoverageHandlerContainer::SingleFileCoverageVector& coverageVector =
  166. this->Coverage.TotalCoverage[filepath];
  167. std::string::size_type linenumber = atoi(separateLine[1].c_str()) - 1;
  168. int count = atoi(separateLine[2].c_str());
  169. if (linenumber > coverageVector.size()) {
  170. cmCTestLog(this->CTest, ERROR_MESSAGE,
  171. "Parse error line is greater than number of lines in file: "
  172. << linenumber << " " << filepath << "\n");
  173. continue; // skip setting count to avoid crash
  174. }
  175. // now add to count for linenumber
  176. // for some reason the cache coverage adds extra lines to the
  177. // end of the file in some cases. Since they do not exist, we will
  178. // mark them as non executable
  179. while (linenumber >= coverageVector.size()) {
  180. coverageVector.push_back(-1);
  181. }
  182. // Accounts for lines that were previously marked
  183. // as non-executable code (-1). if the parser comes back with
  184. // a non-zero count, increase the count by 1 to push the line
  185. // into the executable code set in addition to the count found.
  186. if (coverageVector[linenumber] == -1 && count > 0) {
  187. coverageVector[linenumber] += count + 1;
  188. } else {
  189. coverageVector[linenumber] += count;
  190. }
  191. }
  192. return true;
  193. }