cmParseCacheCoverage.cxx 6.2 KB

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