#include "cmParseDelphiCoverage.h" #include "cmCTest.h" #include "cmCTestCoverageHandler.h" #include "cmSystemTools.h" #include "cmsys/FStream.hxx" #include "cmsys/Glob.hxx" #include #include 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 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(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 == 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 = Coverage.SourceDir + "*/" + filename; gl.FindFiles(glob); std::vector 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: * * 47
     *     CheckEquals(1,2-1);
* */ 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 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(const char* file) { cmParseDelphiCoverage::HTMLParser parser(this->CTest, this->Coverage); parser.ParseFile(file); return true; }