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 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
|
#include "cmParseDelphiCoverage.h"
#include <cstdio>
#include <cstdlib>
#include "cmsys/FStream.hxx"
#include "cmsys/Glob.hxx"
#include "cmCTest.h"
#include "cmCTestCoverageHandler.h"
#include "cmSystemTools.h"
class cmParseDelphiCoverage::HTMLParser
{
public:
using FileLinesType =
cmCTestCoverageHandlerContainer::SingleFileCoverageVector;
HTMLParser(cmCTest* ctest, cmCTestCoverageHandlerContainer& cont)
: CTest(ctest)
, Coverage(cont)
{
}
virtual ~HTMLParser() = default;
bool initializeDelphiFile(
std::string const& 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 != std::string::npos) {
beginSet.emplace_back("begin");
coverageVector.push_back(-1);
continue;
}
if (line.find('{') != std::string::npos) {
blockComFlag = true;
} else if (line.find('}') != std::string::npos) {
blockComFlag = false;
coverageVector.push_back(-1);
continue;
} else if ((line.find("end;") != std::string::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 != std::string::npos) {
semiPos = line.find(';');
if (comPos < semiPos) {
lineComFlag = true;
}
}
// Based up what was found, add a line to the coverageVector
if (!beginSet.empty() && !line.empty() && !blockComFlag &&
!lineComFlag) {
coverageVector.push_back(0);
} else {
coverageVector.push_back(-1);
}
}
return true;
}
bool ParseFile(char const* 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 == std::string::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") != std::string::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 = this->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;">
* CheckEquals(1,2-1);</pre></td></tr>
*
*/
while (cmSystemTools::GetLineFromStream(in, line)) {
if (line.find("inline") == std::string::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(
std::vector<std::string> const& 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(char const* file)
{
cmParseDelphiCoverage::HTMLParser parser(this->CTest, this->Coverage);
parser.ParseFile(file);
return true;
}
|