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
|
#include "cmParseCacheCoverage.h"
#include <cstdio>
#include <cstdlib>
#include <map>
#include <utility>
#include <vector>
#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::ReadCMCovFile(char const* file)
{
cmsys::ifstream in(file);
if (!in) {
cmCTestLog(this->CTest, ERROR_MESSAGE, "Can not open : " << file << "\n");
return false;
}
std::string line;
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;
}
std::vector<std::string> separateLine =
cmSystemTools::SplitString(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)) {
// parse the comma separated line
separateLine = cmSystemTools::SplitString(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;
}
|