blob: e453fe16700eda38d3082f1c21e40cf345b2d43e [file] [log] [blame]
#include "cmStandardIncludes.h"
#include <stdio.h>
#include <stdlib.h>
#include "cmSystemTools.h"
#include "cmXMLParser.h"
#include "cmParseDelphiCoverage.h"
#include <cmsys/Directory.hxx>
#include <cmsys/Glob.hxx>
#include <cmsys/FStream.hxx>
class cmParseDelphiCoverage::HTMLParser
{
public:
typedef cmCTestCoverageHandlerContainer::SingleFileCoverageVector
FileLinesType;
HTMLParser(cmCTest* ctest, cmCTestCoverageHandlerContainer& cont)
: CTest(ctest), Coverage(cont)
{
}
virtual ~HTMLParser()
{
}
bool initializeDelphiFile(const std::string filename,
cmParseDelphiCoverage::HTMLParser::FileLinesType &coverageVector)
{
std::string line;
size_t comPos;
size_t semiPos;
bool blockComFlag= false;
bool lineComFlag= false;
std::vector<std::string> beginSet;
cmsys::ifstream in(filename.c_str());
if(!in)
{
return false;
}
while(cmSystemTools::GetLineFromStream(in, line))
{
lineComFlag=false;
// Unique cases found in lines.
size_t beginPos = line.find("begin");
//Check that the begin is the first non-space string on the line
if( (beginPos == line.find_first_not_of(' ')) && beginPos != line.npos )
{
beginSet.push_back("begin");
coverageVector.push_back(-1);
continue;
}
else if(line.find('{') != line.npos)
{
blockComFlag=true;
}
else if(line.find('}') != line.npos)
{
blockComFlag=false;
coverageVector.push_back(-1);
continue;
}
else if((line.find("end;") != line.npos)
&& !beginSet.empty())
{
beginSet.pop_back();
coverageVector.push_back(-1);
continue;
}
// This checks for comments after lines of code, finding the
// comment symbol after the ending semicolon.
comPos = line.find("//");
if(comPos != line.npos)
{
semiPos= line.find(';');
if(comPos < semiPos)
{
lineComFlag=true;
}
}
//Based up what was found, add a line to the coverageVector
if(!beginSet.empty() && line != "" && !blockComFlag
&& !lineComFlag)
{
coverageVector.push_back(0);
}
else
{
coverageVector.push_back(-1);
}
}
return true;
}
bool ParseFile(const char* file)
{
std::string line=file;
std::string lineresult;
std::string lastroutine;
std::string filename;
std::string filelineoffset;
size_t afterLineNum = 0;
size_t lastoffset = 0;
size_t endcovpos = 0;
size_t endnamepos = 0;
size_t pos = 0;
/*
* This first 'while' section goes through the found HTML
* file name and attempts to capture the source file name
* which is set as part of the HTML file name: the name of
* the file is found in parenthesis '()'
*
* See test HTML file name: UTCovTest(UTCovTest.pas).html.
*
* Find the text inside each pair of parenthesis and check
* to see if it ends in '.pas'. If it can't be found,
* exit the function.
*/
while(true)
{
lastoffset = line.find('(',pos);
if(lastoffset==line.npos)
{
cmCTestOptionalLog(this->CTest,HANDLER_VERBOSE_OUTPUT,
endnamepos << "File not found " << lastoffset << std::endl,
this->Coverage.Quiet);
return false;
}
endnamepos = line.find(')',lastoffset);
filename = line.substr(lastoffset+1,
(endnamepos-1)-lastoffset);
if(filename.find(".pas") != filename.npos)
{
cmCTestOptionalLog(this->CTest,HANDLER_VERBOSE_OUTPUT,
"Coverage found for file: " << filename << std::endl,
this->Coverage.Quiet);
break;
}
pos = lastoffset+1;
}
/*
* Glob through the source directory for the
* file found above
*/
cmsys::Glob gl;
gl.RecurseOn();
gl.RecurseThroughSymlinksOff();
std::string glob = Coverage.SourceDir + "*/" + filename;
gl.FindFiles(glob);
std::vector<std::string> const& files = gl.GetFiles();
if(files.empty())
{
/*
* If that doesn't find any matching files
* return a failure.
*/
cmCTestOptionalLog(this->CTest,HANDLER_VERBOSE_OUTPUT,
"Unable to find file matching" << glob << std::endl,
this->Coverage.Quiet);
return false;
}
FileLinesType& coverageVector =
this->Coverage.TotalCoverage[files[0]];
/*
* Initialize the file to have all code between 'begin' and
* 'end' tags marked as executable
*/
this->initializeDelphiFile(files[0],coverageVector);
cmsys::ifstream in(file);
if(!in)
{
return false;
}
/*
* Now read the HTML file, looking for the lines that have an
* "inline" in it. Then parse out the "class" value of that
* line to determine if the line is executed or not.
*
* Sample HTML line:
*
* <tr class="covered"><td>47</td><td><pre style="display:inline;">
* &nbsp;CheckEquals(1,2-1);</pre></td></tr>
*
*/
while( cmSystemTools::GetLineFromStream(in, line))
{
if(line.find("inline") == line.npos)
{
continue;
}
lastoffset = line.find("class=");
endcovpos = line.find(">",lastoffset);
lineresult = line.substr(lastoffset+7,(endcovpos-8)-lastoffset);
if(lineresult == "covered")
{
afterLineNum = line.find('<',endcovpos+5);
filelineoffset= line.substr(endcovpos+5,
afterLineNum-(endcovpos+5));
coverageVector[atoi(filelineoffset.c_str())-1] = 1;
}
}
return true;
}
private:
cmCTest* CTest;
cmCTestCoverageHandlerContainer& Coverage;
};
cmParseDelphiCoverage::cmParseDelphiCoverage(
cmCTestCoverageHandlerContainer& cont, cmCTest* ctest)
:Coverage(cont), CTest(ctest)
{
}
bool cmParseDelphiCoverage::LoadCoverageData(
const std::vector<std::string> files)
{
size_t i;
std::string path;
size_t numf = files.size();
for (i = 0; i < numf; i++)
{
path = files[i];
cmCTestOptionalLog(this->CTest,HANDLER_VERBOSE_OUTPUT,
"Reading HTML File " << path << std::endl, this->Coverage.Quiet);
if(cmSystemTools::GetFilenameLastExtension(path) == ".html")
{
if(!this->ReadDelphiHTML(path.c_str()))
{
return false;
}
}
}
return true;
}
bool cmParseDelphiCoverage::ReadDelphiHTML(const char* file)
{
cmParseDelphiCoverage::HTMLParser
parser(this->CTest, this->Coverage);
parser.ParseFile(file);
return true;
}