CMake/Source/CTest/cmParseCacheCoverage.cxx
Bill Hoffman c806b23cfe CDash now supports lots of files in coverage. So, show all files.
Prior to this commit the mumps coverage only showed files that
had at least one line of coverage. Now 0% covered files are shown
as well.
2012-05-11 15:22:25 -04:00

221 lines
6.4 KiB
C++

#include "cmStandardIncludes.h"
#include <stdio.h>
#include <stdlib.h>
#include "cmSystemTools.h"
#include "cmParseCacheCoverage.h"
#include <cmsys/Directory.hxx>
#include <cmsys/Glob.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.c_str()))
{
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)
{
std::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.size() == 0)
{
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.size() == 0)
{
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);
}
coverageVector[linenumber] += count;
}
return true;
}