summaryrefslogtreecommitdiffstats
path: root/Source/CTest/cmParseCacheCoverage.cxx
blob: 1a5e7c58a7ed8c4565ae71acc94d7bacd0a77f1b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
#include "cmParseCacheCoverage.h"

#include <cstdio>
#include <cstdlib>
#include <map>
#include <utility>

#include "cmsys/Directory.hxx"
#include "cmsys/FStream.hxx"

#include "cmCTest.h"
#include "cmCTestCoverageHandler.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"

cmParseCacheCoverage::cmParseCacheCoverage(
  cmCTestCoverageHandlerContainer& cont, cmCTest* ctest)
  : cmParseMumpsCoverage(cont, ctest)
{
}

bool cmParseCacheCoverage::LoadCoverageData(std::string const& d)
{
  // load all the .mcov files in the specified directory
  cmsys::Directory dir;
  if (!dir.Load(d)) {
    return false;
  }
  size_t numf;
  unsigned int i;
  numf = dir.GetNumberOfFiles();
  for (i = 0; i < numf; i++) {
    std::string file = dir.GetFile(i);
    if (file != "." && file != ".." && !cmSystemTools::FileIsDirectory(file)) {
      std::string path = cmStrCat(d, '/', file);
      if (cmSystemTools::GetFilenameLastExtension(path) == ".cmcov") {
        if (!this->ReadCMCovFile(path.c_str())) {
          return false;
        }
      }
    }
  }
  return true;
}

// not currently used, but leave it in case we want it in the future
void cmParseCacheCoverage::RemoveUnCoveredFiles()
{
  // loop over the coverage data computed and remove all files
  // that only have -1 or 0 for the lines.
  auto ci = this->Coverage.TotalCoverage.begin();
  while (ci != this->Coverage.TotalCoverage.end()) {
    cmCTestCoverageHandlerContainer::SingleFileCoverageVector& v = ci->second;
    bool nothing = true;
    for (int i : v) {
      if (i > 0) {
        nothing = false;
        break;
      }
    }
    if (nothing) {
      cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                         "No coverage found in: " << ci->first << std::endl,
                         this->Coverage.Quiet);
      this->Coverage.TotalCoverage.erase(ci++);
    } else {
      ++ci;
    }
  }
}

bool cmParseCacheCoverage::SplitString(std::vector<std::string>& args,
                                       std::string const& line)
{
  std::string::size_type pos1 = 0;
  std::string::size_type pos2 = line.find(',', 0);
  if (pos2 == std::string::npos) {
    return false;
  }
  std::string arg;
  while (pos2 != std::string::npos) {
    arg = line.substr(pos1, pos2 - pos1);
    args.push_back(arg);
    pos1 = pos2 + 1;
    pos2 = line.find(',', pos1);
  }
  arg = line.substr(pos1);
  args.push_back(arg);
  return true;
}

bool cmParseCacheCoverage::ReadCMCovFile(const char* file)
{
  cmsys::ifstream in(file);
  if (!in) {
    cmCTestLog(this->CTest, ERROR_MESSAGE, "Can not open : " << file << "\n");
    return false;
  }
  std::string line;
  std::vector<std::string> separateLine;
  if (!cmSystemTools::GetLineFromStream(in, line)) {
    cmCTestLog(this->CTest, ERROR_MESSAGE,
               "Empty file : " << file
                               << "  referenced in this line of cmcov data:\n"
                                  "["
                               << line << "]\n");
    return false;
  }
  separateLine.clear();
  this->SplitString(separateLine, line);
  if (separateLine.size() != 4 || separateLine[0] != "Routine" ||
      separateLine[1] != "Line" || separateLine[2] != "RtnLine" ||
      separateLine[3] != "Code") {
    cmCTestLog(this->CTest, ERROR_MESSAGE,
               "Bad first line of cmcov file : " << file
                                                 << "  line:\n"
                                                    "["
                                                 << line << "]\n");
  }
  std::string routine;
  std::string filepath;
  while (cmSystemTools::GetLineFromStream(in, line)) {
    // clear out line argument vector
    separateLine.clear();
    // parse the comma separated line
    this->SplitString(separateLine, line);
    // might have more because code could have a quoted , in it
    // but we only care about the first 3 args anyway
    if (separateLine.size() < 4) {
      cmCTestLog(this->CTest, ERROR_MESSAGE,
                 "Bad line of cmcov file expected at least 4 found: "
                   << separateLine.size() << " " << file
                   << "  line:\n"
                      "["
                   << line << "]\n");
      for (std::string::size_type i = 0; i < separateLine.size(); ++i) {
        cmCTestLog(this->CTest, ERROR_MESSAGE, "" << separateLine[1] << " ");
      }
      cmCTestLog(this->CTest, ERROR_MESSAGE, "\n");
      return false;
    }
    // if we do not have a routine yet, then it should be
    // the first argument in the vector
    if (routine.empty()) {
      routine = separateLine[0];
      // Find the full path to the file
      if (!this->FindMumpsFile(routine, filepath)) {
        cmCTestLog(this->CTest, ERROR_MESSAGE,
                   "Could not find mumps file for routine: " << routine
                                                             << "\n");
        filepath.clear();
        continue; // move to next line
      }
    }
    // if we have a routine name, check for end of routine
    else {
      // Totals in arg 0 marks the end of a routine
      if (cmHasLiteralPrefix(separateLine[0], "Totals")) {
        routine.clear(); // at the end of this routine
        filepath.clear();
        continue; // move to next line
      }
    }
    // if the file path was not found for the routine
    // move to next line. We should have already warned
    // after the call to FindMumpsFile that we did not find
    // it, so don't report again to cut down on output
    if (filepath.empty()) {
      continue;
    }
    // now we are ready to set the coverage from the line of data
    cmCTestCoverageHandlerContainer::SingleFileCoverageVector& coverageVector =
      this->Coverage.TotalCoverage[filepath];
    std::string::size_type linenumber = atoi(separateLine[1].c_str()) - 1;
    int count = atoi(separateLine[2].c_str());
    if (linenumber > coverageVector.size()) {
      cmCTestLog(this->CTest, ERROR_MESSAGE,
                 "Parse error line is greater than number of lines in file: "
                   << linenumber << " " << filepath << "\n");
      continue; // skip setting count to avoid crash
    }
    // now add to count for linenumber
    // for some reason the cache coverage adds extra lines to the
    // end of the file in some cases. Since they do not exist, we will
    // mark them as non executable
    while (linenumber >= coverageVector.size()) {
      coverageVector.push_back(-1);
    }
    // Accounts for lines that were previously marked
    // as non-executable code (-1). if the parser comes back with
    // a non-zero count, increase the count by 1 to push the line
    // into the executable code set in addition to the count found.
    if (coverageVector[linenumber] == -1 && count > 0) {
      coverageVector[linenumber] += count + 1;
    } else {
      coverageVector[linenumber] += count;
    }
  }
  return true;
}