|
|
|
#include "cmStandardIncludes.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "cmSystemTools.h"
|
|
|
|
#include "cmParseCacheCoverage.h"
|
|
|
|
#include <cmsys/Directory.hxx>
|
|
|
|
#include <cmsys/Glob.hxx>
|
|
|
|
#include <cmsys/FStream.hxx>
|
|
|
|
|
|
|
|
|
|
|
|
cmParseCacheCoverage::cmParseCacheCoverage(
|
|
|
|
cmCTestCoverageHandlerContainer& cont,
|
|
|
|
cmCTest* ctest)
|
|
|
|
:cmParseMumpsCoverage(cont, ctest)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cmParseCacheCoverage::LoadCoverageData(const char* 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 = d;
|
|
|
|
path += "/";
|
|
|
|
path += 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.
|
|
|
|
cmCTestCoverageHandlerContainer::TotalCoverageMap::iterator ci =
|
|
|
|
this->Coverage.TotalCoverage.begin();
|
|
|
|
while(ci != this->Coverage.TotalCoverage.end())
|
|
|
|
{
|
|
|
|
cmCTestCoverageHandlerContainer::SingleFileCoverageVector& v =
|
|
|
|
ci->second;
|
|
|
|
bool nothing = true;
|
|
|
|
for(cmCTestCoverageHandlerContainer::SingleFileCoverageVector::iterator i=
|
|
|
|
v.begin(); i != v.end(); ++i)
|
|
|
|
{
|
|
|
|
if(*i > 0)
|
|
|
|
{
|
|
|
|
nothing = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(nothing)
|
|
|
|
{
|
|
|
|
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
|
|
|
|
"No coverage found in: " << ci->first
|
|
|
|
<< std::endl);
|
|
|
|
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 = "";
|
|
|
|
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(separateLine[0].substr(0, 6) == "Totals")
|
|
|
|
{
|
|
|
|
routine = ""; // at the end of this routine
|
|
|
|
filepath = "";
|
|
|
|
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;
|
|
|
|
}
|