| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366 | /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying   file Copyright.txt or https://cmake.org/licensing for details.  */#include "cmCTestCoverageHandler.h"#include "cmCTest.h"#include "cmDuration.h"#include "cmGeneratedFileStream.h"#include "cmParseBlanketJSCoverage.h"#include "cmParseCacheCoverage.h"#include "cmParseCoberturaCoverage.h"#include "cmParseDelphiCoverage.h"#include "cmParseGTMCoverage.h"#include "cmParseJacocoCoverage.h"#include "cmParsePHPCoverage.h"#include "cmSystemTools.h"#include "cmWorkingDirectory.h"#include "cmXMLWriter.h"#include "cmake.h"#include "cmsys/FStream.hxx"#include "cmsys/Glob.hxx"#include "cmsys/Process.h"#include "cmsys/RegularExpression.hxx"#include <algorithm>#include <chrono>#include <cstring>#include <iomanip>#include <iterator>#include <sstream>#include <stdio.h>#include <stdlib.h>#include <utility>class cmMakefile;#define SAFEDIV(x, y) (((y) != 0) ? ((x) / (y)) : (0))class cmCTestRunProcess{public:  cmCTestRunProcess()  {    this->Process = cmsysProcess_New();    this->PipeState = -1;    this->TimeOut = cmDuration(-1);  }  ~cmCTestRunProcess()  {    if (!(this->PipeState == -1) &&        !(this->PipeState == cmsysProcess_Pipe_None) &&        !(this->PipeState == cmsysProcess_Pipe_Timeout)) {      this->WaitForExit();    }    cmsysProcess_Delete(this->Process);  }  void SetCommand(const char* command)  {    this->CommandLineStrings.clear();    this->CommandLineStrings.push_back(command);  }  void AddArgument(const char* arg)  {    if (arg) {      this->CommandLineStrings.push_back(arg);    }  }  void SetWorkingDirectory(const char* dir) { this->WorkingDirectory = dir; }  void SetTimeout(cmDuration t) { this->TimeOut = t; }  bool StartProcess()  {    std::vector<const char*> args;    for (std::string const& cl : this->CommandLineStrings) {      args.push_back(cl.c_str());    }    args.push_back(nullptr); // null terminate    cmsysProcess_SetCommand(this->Process, &*args.begin());    if (!this->WorkingDirectory.empty()) {      cmsysProcess_SetWorkingDirectory(this->Process,                                       this->WorkingDirectory.c_str());    }    cmsysProcess_SetOption(this->Process, cmsysProcess_Option_HideWindow, 1);    if (this->TimeOut >= cmDuration::zero()) {      cmsysProcess_SetTimeout(this->Process, this->TimeOut.count());    }    cmsysProcess_Execute(this->Process);    this->PipeState = cmsysProcess_GetState(this->Process);    // if the process is running or exited return true    return this->PipeState == cmsysProcess_State_Executing ||      this->PipeState == cmsysProcess_State_Exited;  }  void SetStdoutFile(const char* fname)  {    cmsysProcess_SetPipeFile(this->Process, cmsysProcess_Pipe_STDOUT, fname);  }  void SetStderrFile(const char* fname)  {    cmsysProcess_SetPipeFile(this->Process, cmsysProcess_Pipe_STDERR, fname);  }  int WaitForExit(double* timeout = nullptr)  {    this->PipeState = cmsysProcess_WaitForExit(this->Process, timeout);    return this->PipeState;  }  int GetProcessState() { return this->PipeState; }private:  int PipeState;  cmsysProcess* Process;  std::vector<std::string> CommandLineStrings;  std::string WorkingDirectory;  cmDuration TimeOut;};cmCTestCoverageHandler::cmCTestCoverageHandler(){}void cmCTestCoverageHandler::Initialize(){  this->Superclass::Initialize();  this->CustomCoverageExclude.clear();  this->SourceLabels.clear();  this->TargetDirs.clear();  this->LabelIdMap.clear();  this->Labels.clear();  this->LabelFilter.clear();}void cmCTestCoverageHandler::CleanCoverageLogFiles(std::ostream& log){  std::string logGlob = this->CTest->GetCTestConfiguration("BuildDirectory");  logGlob += "/Testing/";  logGlob += this->CTest->GetCurrentTag();  logGlob += "/CoverageLog*";  cmsys::Glob gl;  gl.FindFiles(logGlob);  std::vector<std::string> const& files = gl.GetFiles();  for (std::string const& f : files) {    log << "Removing old coverage log: " << f << "\n";    cmSystemTools::RemoveFile(f);  }}bool cmCTestCoverageHandler::StartCoverageLogFile(  cmGeneratedFileStream& covLogFile, int logFileCount){  char covLogFilename[1024];  sprintf(covLogFilename, "CoverageLog-%d", logFileCount);  cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                     "Open file: " << covLogFilename << std::endl,                     this->Quiet);  if (!this->StartResultingXML(cmCTest::PartCoverage, covLogFilename,                               covLogFile)) {    cmCTestLog(this->CTest, ERROR_MESSAGE,               "Cannot open log file: " << covLogFilename << std::endl);    return false;  }  return true;}void cmCTestCoverageHandler::EndCoverageLogFile(cmGeneratedFileStream& ostr,                                                int logFileCount){  char covLogFilename[1024];  sprintf(covLogFilename, "CoverageLog-%d.xml", logFileCount);  cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                     "Close file: " << covLogFilename << std::endl,                     this->Quiet);  ostr.Close();}void cmCTestCoverageHandler::StartCoverageLogXML(cmXMLWriter& xml){  this->CTest->StartXML(xml, this->AppendXML);  xml.StartElement("CoverageLog");  xml.Element("StartDateTime", this->CTest->CurrentTime());  xml.Element("StartTime", std::chrono::system_clock::now());}void cmCTestCoverageHandler::EndCoverageLogXML(cmXMLWriter& xml){  xml.Element("EndDateTime", this->CTest->CurrentTime());  xml.Element("EndTime", std::chrono::system_clock::now());  xml.EndElement(); // CoverageLog  this->CTest->EndXML(xml);}bool cmCTestCoverageHandler::ShouldIDoCoverage(std::string const& file,                                               std::string const& srcDir,                                               std::string const& binDir){  if (this->IsFilteredOut(file)) {    return false;  }  for (cmsys::RegularExpression& rx : this->CustomCoverageExcludeRegex) {    if (rx.find(file)) {      cmCTestOptionalLog(        this->CTest, HANDLER_VERBOSE_OUTPUT,        "  File " << file << " is excluded in CTestCustom.ctest" << std::endl;        , this->Quiet);      return false;    }  }  std::string fSrcDir = cmSystemTools::CollapseFullPath(srcDir);  std::string fBinDir = cmSystemTools::CollapseFullPath(binDir);  std::string fFile = cmSystemTools::CollapseFullPath(file);  bool sourceSubDir = cmSystemTools::IsSubDirectory(fFile, fSrcDir);  bool buildSubDir = cmSystemTools::IsSubDirectory(fFile, fBinDir);  // Always check parent directory of the file.  std::string fileDir = cmSystemTools::GetFilenamePath(fFile);  std::string checkDir;  // We also need to check the binary/source directory pair.  if (sourceSubDir && buildSubDir) {    if (fSrcDir.size() > fBinDir.size()) {      checkDir = fSrcDir;    } else {      checkDir = fBinDir;    }  } else if (sourceSubDir) {    checkDir = fSrcDir;  } else if (buildSubDir) {    checkDir = fBinDir;  }  std::string ndc = cmSystemTools::FileExistsInParentDirectories(    ".NoDartCoverage", fFile.c_str(), checkDir.c_str());  if (!ndc.empty()) {    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                       "Found: " << ndc << " so skip coverage of " << file                                 << std::endl,                       this->Quiet);    return false;  }  // By now checkDir should be set to parent directory of the file.  // Get the relative path to the file an apply it to the opposite directory.  // If it is the same as fileDir, then ignore, otherwise check.  std::string relPath;  if (!checkDir.empty()) {    relPath = cmSystemTools::RelativePath(checkDir, fFile);  } else {    relPath = fFile;  }  if (checkDir == fSrcDir) {    checkDir = fBinDir;  } else {    checkDir = fSrcDir;  }  fFile = checkDir + "/" + relPath;  fFile = cmSystemTools::GetFilenamePath(fFile);  if (fileDir == fFile) {    // This is in-source build, so we trust the previous check.    return true;  }  ndc = cmSystemTools::FileExistsInParentDirectories(    ".NoDartCoverage", fFile.c_str(), checkDir.c_str());  if (!ndc.empty()) {    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                       "Found: " << ndc << " so skip coverage of: " << file                                 << std::endl,                       this->Quiet);    return false;  }  // Ok, nothing in source tree, nothing in binary tree  return true;}// clearly it would be nice if this were broken up into a few smaller// functions and commented...int cmCTestCoverageHandler::ProcessHandler(){  this->CTest->ClearSubmitFiles(cmCTest::PartCoverage);  int error = 0;  // do we have time for this  if (this->CTest->GetRemainingTimeAllowed() < std::chrono::minutes(2)) {    return error;  }  std::string coverage_start_time = this->CTest->CurrentTime();  auto coverage_start_time_time = std::chrono::system_clock::now();  std::string sourceDir =    this->CTest->GetCTestConfiguration("SourceDirectory");  std::string binaryDir = this->CTest->GetCTestConfiguration("BuildDirectory");  this->LoadLabels();  cmGeneratedFileStream ofs;  auto elapsed_time_start = std::chrono::steady_clock::now();  if (!this->StartLogFile("Coverage", ofs)) {    cmCTestLog(this->CTest, ERROR_MESSAGE,               "Cannot create LastCoverage.log file" << std::endl);  }  ofs << "Performing coverage: "      << elapsed_time_start.time_since_epoch().count() << std::endl;  this->CleanCoverageLogFiles(ofs);  cmSystemTools::ConvertToUnixSlashes(sourceDir);  cmSystemTools::ConvertToUnixSlashes(binaryDir);  cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,                     "Performing coverage" << std::endl, this->Quiet);  cmCTestCoverageHandlerContainer cont;  cont.Error = error;  cont.SourceDir = sourceDir;  cont.BinaryDir = binaryDir;  cont.OFS = &ofs;  cont.Quiet = this->Quiet;  // setup the regex exclude stuff  this->CustomCoverageExcludeRegex.clear();  for (std::string const& rex : this->CustomCoverageExclude) {    this->CustomCoverageExcludeRegex.push_back(      cmsys::RegularExpression(rex.c_str()));  }  if (this->HandleBullseyeCoverage(&cont)) {    return cont.Error;  }  int file_count = 0;  file_count += this->HandleGCovCoverage(&cont);  error = cont.Error;  if (file_count < 0) {    return error;  }  file_count += this->HandleLCovCoverage(&cont);  error = cont.Error;  if (file_count < 0) {    return error;  }  file_count += this->HandleTracePyCoverage(&cont);  error = cont.Error;  if (file_count < 0) {    return error;  }  file_count += this->HandlePHPCoverage(&cont);  error = cont.Error;  if (file_count < 0) {    return error;  }  file_count += this->HandleCoberturaCoverage(&cont);  error = cont.Error;  if (file_count < 0) {    return error;  }  file_count += this->HandleMumpsCoverage(&cont);  error = cont.Error;  if (file_count < 0) {    return error;  }  file_count += this->HandleJacocoCoverage(&cont);  error = cont.Error;  if (file_count < 0) {    return error;  }  file_count += this->HandleBlanketJSCoverage(&cont);  error = cont.Error;  if (file_count < 0) {    return error;  }  file_count += this->HandleDelphiCoverage(&cont);  error = cont.Error;  if (file_count < 0) {    return error;  }  std::set<std::string> uncovered = this->FindUncoveredFiles(&cont);  if (file_count == 0 && this->ExtraCoverageGlobs.empty()) {    cmCTestOptionalLog(      this->CTest, WARNING,      " Cannot find any coverage files. Ignoring Coverage request."        << std::endl,      this->Quiet);    return error;  }  cmGeneratedFileStream covSumFile;  cmGeneratedFileStream covLogFile;  cmXMLWriter covSumXML(covSumFile);  cmXMLWriter covLogXML(covLogFile);  if (!this->StartResultingXML(cmCTest::PartCoverage, "Coverage",                               covSumFile)) {    cmCTestLog(this->CTest, ERROR_MESSAGE,               "Cannot open coverage summary file." << std::endl);    return -1;  }  covSumFile.setf(std::ios::fixed, std::ios::floatfield);  covSumFile.precision(2);  this->CTest->StartXML(covSumXML, this->AppendXML);  // Produce output xml files  covSumXML.StartElement("Coverage");  covSumXML.Element("StartDateTime", coverage_start_time);  covSumXML.Element("StartTime", coverage_start_time_time);  int logFileCount = 0;  if (!this->StartCoverageLogFile(covLogFile, logFileCount)) {    return -1;  }  this->StartCoverageLogXML(covLogXML);  int cnt = 0;  long total_tested = 0;  long total_untested = 0;  // std::string fullSourceDir = sourceDir + "/";  // std::string fullBinaryDir = binaryDir + "/";  cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, std::endl, this->Quiet);  cmCTestOptionalLog(    this->CTest, HANDLER_OUTPUT,    "   Accumulating results (each . represents one file):" << std::endl,    this->Quiet);  cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "    ", this->Quiet);  std::vector<std::string> errorsWhileAccumulating;  file_count = 0;  for (auto const& file : cont.TotalCoverage) {    cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "." << std::flush,                       this->Quiet);    file_count++;    if (file_count % 50 == 0) {      cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,                         " processed: " << file_count << " out of "                                        << cont.TotalCoverage.size()                                        << std::endl,                         this->Quiet);      cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "    ", this->Quiet);    }    const std::string fullFileName = file.first;    bool shouldIDoCoverage =      this->ShouldIDoCoverage(fullFileName, sourceDir, binaryDir);    if (!shouldIDoCoverage) {      cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                         ".NoDartCoverage found, so skip coverage check for: "                           << fullFileName << std::endl,                         this->Quiet);      continue;    }    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                       "Process file: " << fullFileName << std::endl,                       this->Quiet);    if (!cmSystemTools::FileExists(fullFileName)) {      cmCTestLog(this->CTest, ERROR_MESSAGE,                 "Cannot find file: " << fullFileName << std::endl);      continue;    }    if (++cnt % 100 == 0) {      this->EndCoverageLogXML(covLogXML);      this->EndCoverageLogFile(covLogFile, logFileCount);      logFileCount++;      if (!this->StartCoverageLogFile(covLogFile, logFileCount)) {        return -1;      }      this->StartCoverageLogXML(covLogXML);    }    const std::string fileName = cmSystemTools::GetFilenameName(fullFileName);    std::string shortFileName =      this->CTest->GetShortPathToFile(fullFileName.c_str());    const cmCTestCoverageHandlerContainer::SingleFileCoverageVector& fcov =      file.second;    covLogXML.StartElement("File");    covLogXML.Attribute("Name", fileName);    covLogXML.Attribute("FullPath", shortFileName);    covLogXML.StartElement("Report");    cmsys::ifstream ifs(fullFileName.c_str());    if (!ifs) {      std::ostringstream ostr;      ostr << "Cannot open source file: " << fullFileName;      errorsWhileAccumulating.push_back(ostr.str());      error++;      continue;    }    int tested = 0;    int untested = 0;    cmCTestCoverageHandlerContainer::SingleFileCoverageVector::size_type cc;    std::string line;    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                       "Actually performing coverage for: " << fullFileName                                                            << std::endl,                       this->Quiet);    for (cc = 0; cc < fcov.size(); cc++) {      if (!cmSystemTools::GetLineFromStream(ifs, line) &&          cc != fcov.size() - 1) {        std::ostringstream ostr;        ostr << "Problem reading source file: " << fullFileName             << " line:" << cc << "  out total: " << fcov.size() - 1;        errorsWhileAccumulating.push_back(ostr.str());        error++;        break;      }      covLogXML.StartElement("Line");      covLogXML.Attribute("Number", cc);      covLogXML.Attribute("Count", fcov[cc]);      covLogXML.Content(line);      covLogXML.EndElement(); // Line      if (fcov[cc] == 0) {        untested++;      } else if (fcov[cc] > 0) {        tested++;      }    }    if (cmSystemTools::GetLineFromStream(ifs, line)) {      std::ostringstream ostr;      ostr << "Looks like there are more lines in the file: " << fullFileName;      errorsWhileAccumulating.push_back(ostr.str());    }    float cper = 0;    float cmet = 0;    if (tested + untested > 0) {      cper = (100 *              SAFEDIV(static_cast<float>(tested),                      static_cast<float>(tested + untested)));      cmet = (SAFEDIV(static_cast<float>(tested + 10),                      static_cast<float>(tested + untested + 10)));    }    total_tested += tested;    total_untested += untested;    covLogXML.EndElement(); // Report    covLogXML.EndElement(); // File    covSumXML.StartElement("File");    covSumXML.Attribute("Name", fileName);    covSumXML.Attribute("FullPath",                        this->CTest->GetShortPathToFile(fullFileName.c_str()));    covSumXML.Attribute("Covered", tested + untested > 0 ? "true" : "false");    covSumXML.Element("LOCTested", tested);    covSumXML.Element("LOCUnTested", untested);    covSumXML.Element("PercentCoverage", cper);    covSumXML.Element("CoverageMetric", cmet);    this->WriteXMLLabels(covSumXML, shortFileName);    covSumXML.EndElement(); // File  }  // Handle all the files in the extra coverage globs that have no cov data  for (std::string const& u : uncovered) {    std::string fileName = cmSystemTools::GetFilenameName(u);    std::string fullPath = cont.SourceDir + "/" + u;    covLogXML.StartElement("File");    covLogXML.Attribute("Name", fileName);    covLogXML.Attribute("FullPath", u);    covLogXML.StartElement("Report");    cmsys::ifstream ifs(fullPath.c_str());    if (!ifs) {      std::ostringstream ostr;      ostr << "Cannot open source file: " << fullPath;      errorsWhileAccumulating.push_back(ostr.str());      error++;      covLogXML.EndElement(); // Report      covLogXML.EndElement(); // File      continue;    }    int untested = 0;    std::string line;    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                       "Actually performing coverage for: " << u << std::endl,                       this->Quiet);    while (cmSystemTools::GetLineFromStream(ifs, line)) {      covLogXML.StartElement("Line");      covLogXML.Attribute("Number", untested);      covLogXML.Attribute("Count", 0);      covLogXML.Content(line);      covLogXML.EndElement(); // Line      untested++;    }    covLogXML.EndElement(); // Report    covLogXML.EndElement(); // File    total_untested += untested;    covSumXML.StartElement("File");    covSumXML.Attribute("Name", fileName);    covSumXML.Attribute("FullPath", u);    covSumXML.Attribute("Covered", "true");    covSumXML.Element("LOCTested", 0);    covSumXML.Element("LOCUnTested", untested);    covSumXML.Element("PercentCoverage", 0);    covSumXML.Element("CoverageMetric", 0);    this->WriteXMLLabels(covSumXML, u);    covSumXML.EndElement(); // File  }  this->EndCoverageLogXML(covLogXML);  this->EndCoverageLogFile(covLogFile, logFileCount);  if (!errorsWhileAccumulating.empty()) {    cmCTestLog(this->CTest, ERROR_MESSAGE, std::endl);    cmCTestLog(this->CTest, ERROR_MESSAGE,               "Error(s) while accumulating results:" << std::endl);    for (std::string const& er : errorsWhileAccumulating) {      cmCTestLog(this->CTest, ERROR_MESSAGE, "  " << er << std::endl);    }  }  long total_lines = total_tested + total_untested;  float percent_coverage = 100 *    SAFEDIV(static_cast<float>(total_tested), static_cast<float>(total_lines));  if (total_lines == 0) {    percent_coverage = 0;  }  std::string end_time = this->CTest->CurrentTime();  covSumXML.Element("LOCTested", total_tested);  covSumXML.Element("LOCUntested", total_untested);  covSumXML.Element("LOC", total_lines);  covSumXML.Element("PercentCoverage", percent_coverage);  covSumXML.Element("EndDateTime", end_time);  covSumXML.Element("EndTime", std::chrono::system_clock::now());  covSumXML.Element("ElapsedMinutes",                    std::chrono::duration_cast<std::chrono::minutes>(                      std::chrono::steady_clock::now() - elapsed_time_start)                      .count());  covSumXML.EndElement(); // Coverage  this->CTest->EndXML(covSumXML);  cmCTestLog(this->CTest, HANDLER_OUTPUT,             "" << std::endl                << "\tCovered LOC:         " << total_tested << std::endl                << "\tNot covered LOC:     " << total_untested << std::endl                << "\tTotal LOC:           " << total_lines << std::endl                << "\tPercentage Coverage: "                << std::setiosflags(std::ios::fixed) << std::setprecision(2)                << (percent_coverage) << "%" << std::endl);  ofs << "\tCovered LOC:         " << total_tested << std::endl      << "\tNot covered LOC:     " << total_untested << std::endl      << "\tTotal LOC:           " << total_lines << std::endl      << "\tPercentage Coverage: " << std::setiosflags(std::ios::fixed)      << std::setprecision(2) << (percent_coverage) << "%" << std::endl;  if (error) {    return -1;  }  return 0;}void cmCTestCoverageHandler::PopulateCustomVectors(cmMakefile* mf){  cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                     " Add coverage exclude regular expressions." << std::endl,                     this->Quiet);  this->CTest->PopulateCustomVector(mf, "CTEST_CUSTOM_COVERAGE_EXCLUDE",                                    this->CustomCoverageExclude);  this->CTest->PopulateCustomVector(mf, "CTEST_EXTRA_COVERAGE_GLOB",                                    this->ExtraCoverageGlobs);  for (std::string const& cce : this->CustomCoverageExclude) {    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                       " Add coverage exclude: " << cce << std::endl,                       this->Quiet);  }  for (std::string const& ecg : this->ExtraCoverageGlobs) {    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                       " Add coverage glob: " << ecg << std::endl,                       this->Quiet);  }}// Fix for issue #4971 where the case of the drive letter component of// the filenames might be different when analyzing gcov output.//// Compare file names: fnc(fn1) == fnc(fn2) // fnc == file name compare//#ifdef _WIN32#  define fnc(s) cmSystemTools::LowerCase(s)#else#  define fnc(s) s#endifbool IsFileInDir(const std::string& infile, const std::string& indir){  std::string file = cmSystemTools::CollapseFullPath(infile);  std::string dir = cmSystemTools::CollapseFullPath(indir);  return file.size() > dir.size() &&    fnc(file.substr(0, dir.size())) == fnc(dir) && file[dir.size()] == '/';}int cmCTestCoverageHandler::HandlePHPCoverage(  cmCTestCoverageHandlerContainer* cont){  cmParsePHPCoverage cov(*cont, this->CTest);  std::string coverageDir = this->CTest->GetBinaryDir() + "/xdebugCoverage";  if (cmSystemTools::FileIsDirectory(coverageDir)) {    cov.ReadPHPCoverageDirectory(coverageDir.c_str());  }  return static_cast<int>(cont->TotalCoverage.size());}int cmCTestCoverageHandler::HandleCoberturaCoverage(  cmCTestCoverageHandlerContainer* cont){  cmParseCoberturaCoverage cov(*cont, this->CTest);  // Assume the coverage.xml is in the binary directory  // check for the COBERTURADIR environment variable,  // if it doesn't exist or is empty, assume the  // binary directory is used.  std::string coverageXMLFile;  if (!cmSystemTools::GetEnv("COBERTURADIR", coverageXMLFile) ||      coverageXMLFile.empty()) {    coverageXMLFile = this->CTest->GetBinaryDir();  }  // build the find file string with the directory from above  coverageXMLFile += "/coverage.xml";  if (cmSystemTools::FileExists(coverageXMLFile)) {    // If file exists, parse it    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                       "Parsing Cobertura XML file: " << coverageXMLFile                                                      << std::endl,                       this->Quiet);    cov.ReadCoverageXML(coverageXMLFile.c_str());  } else {    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                       " Cannot find Cobertura XML file: " << coverageXMLFile                                                           << std::endl,                       this->Quiet);  }  return static_cast<int>(cont->TotalCoverage.size());}int cmCTestCoverageHandler::HandleMumpsCoverage(  cmCTestCoverageHandlerContainer* cont){  // try gtm coverage  cmParseGTMCoverage cov(*cont, this->CTest);  std::string coverageFile =    this->CTest->GetBinaryDir() + "/gtm_coverage.mcov";  if (cmSystemTools::FileExists(coverageFile)) {    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                       "Parsing Cache Coverage: " << coverageFile << std::endl,                       this->Quiet);    cov.ReadCoverageFile(coverageFile.c_str());    return static_cast<int>(cont->TotalCoverage.size());  }  cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                     " Cannot find GTM coverage file: " << coverageFile                                                        << std::endl,                     this->Quiet);  cmParseCacheCoverage ccov(*cont, this->CTest);  coverageFile = this->CTest->GetBinaryDir() + "/cache_coverage.cmcov";  if (cmSystemTools::FileExists(coverageFile)) {    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                       "Parsing Cache Coverage: " << coverageFile << std::endl,                       this->Quiet);    ccov.ReadCoverageFile(coverageFile.c_str());  } else {    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                       " Cannot find Cache coverage file: " << coverageFile                                                            << std::endl,                       this->Quiet);  }  return static_cast<int>(cont->TotalCoverage.size());}struct cmCTestCoverageHandlerLocale{  cmCTestCoverageHandlerLocale()  {    std::string l;    if (cmSystemTools::GetEnv("LC_ALL", l)) {      lc_all = l;    }    if (lc_all != "C") {      cmSystemTools::PutEnv("LC_ALL=C");    }  }  ~cmCTestCoverageHandlerLocale()  {    if (!lc_all.empty()) {      cmSystemTools::PutEnv("LC_ALL=" + lc_all);    } else {      cmSystemTools::UnsetEnv("LC_ALL");    }  }  std::string lc_all;};int cmCTestCoverageHandler::HandleJacocoCoverage(  cmCTestCoverageHandlerContainer* cont){  cmParseJacocoCoverage cov = cmParseJacocoCoverage(*cont, this->CTest);  // Search in the source directory.  cmsys::Glob g1;  std::vector<std::string> files;  g1.SetRecurse(true);  std::string SourceDir =    this->CTest->GetCTestConfiguration("SourceDirectory");  std::string coverageFile = SourceDir + "/*jacoco.xml";  g1.FindFiles(coverageFile);  files = g1.GetFiles();  // ...and in the binary directory.  cmsys::Glob g2;  std::vector<std::string> binFiles;  g2.SetRecurse(true);  std::string binaryDir = this->CTest->GetCTestConfiguration("BuildDirectory");  std::string binCoverageFile = binaryDir + "/*jacoco.xml";  g2.FindFiles(binCoverageFile);  binFiles = g2.GetFiles();  if (!binFiles.empty()) {    files.insert(files.end(), binFiles.begin(), binFiles.end());  }  if (!files.empty()) {    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                       "Found Jacoco Files, Performing Coverage" << std::endl,                       this->Quiet);    cov.LoadCoverageData(files);  } else {    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                       " Cannot find Jacoco coverage files: " << coverageFile                                                              << std::endl,                       this->Quiet);  }  return static_cast<int>(cont->TotalCoverage.size());}int cmCTestCoverageHandler::HandleDelphiCoverage(  cmCTestCoverageHandlerContainer* cont){  cmParseDelphiCoverage cov = cmParseDelphiCoverage(*cont, this->CTest);  cmsys::Glob g;  std::vector<std::string> files;  g.SetRecurse(true);  std::string BinDir = this->CTest->GetBinaryDir();  std::string coverageFile = BinDir + "/*(*.pas).html";  g.FindFiles(coverageFile);  files = g.GetFiles();  if (!files.empty()) {    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                       "Found Delphi HTML Files, Performing Coverage"                         << std::endl,                       this->Quiet);    cov.LoadCoverageData(files);  } else {    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                       " Cannot find Delphi coverage files: " << coverageFile                                                              << std::endl,                       this->Quiet);  }  return static_cast<int>(cont->TotalCoverage.size());}static std::string joinCommandLine(const std::vector<std::string>& args){  std::string ret;  for (std::string const& s : args) {    if (s.find(' ') == std::string::npos) {      ret += s + ' ';    } else {      ret += "\"" + s + "\" ";    }  }  // drop trailing whitespace  ret.erase(ret.size() - 1);  return ret;}int cmCTestCoverageHandler::HandleBlanketJSCoverage(  cmCTestCoverageHandlerContainer* cont){  cmParseBlanketJSCoverage cov = cmParseBlanketJSCoverage(*cont, this->CTest);  std::string SourceDir =    this->CTest->GetCTestConfiguration("SourceDirectory");  // Look for something other than output.json, still JSON extension.  std::string coverageFile = SourceDir + "/*.json";  cmsys::Glob g;  std::vector<std::string> files;  std::vector<std::string> blanketFiles;  g.FindFiles(coverageFile);  files = g.GetFiles();  // Ensure that the JSON files found are the result of the  // Blanket.js output. Check for the "node-jscoverage"  // string on the second line  std::string line;  for (std::string const& fileEntry : files) {    cmsys::ifstream in(fileEntry.c_str());    cmSystemTools::GetLineFromStream(in, line);    cmSystemTools::GetLineFromStream(in, line);    if (line.find("node-jscoverage") != std::string::npos) {      blanketFiles.push_back(fileEntry);    }  }  //  Take all files with the node-jscoverage string and parse those  if (!blanketFiles.empty()) {    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                       "Found BlanketJS output JSON, Performing Coverage"                         << std::endl,                       this->Quiet);    cov.LoadCoverageData(files);  } else {    cmCTestOptionalLog(      this->CTest, HANDLER_VERBOSE_OUTPUT,      " Cannot find BlanketJS coverage files: " << coverageFile << std::endl,      this->Quiet);  }  return static_cast<int>(cont->TotalCoverage.size());}int cmCTestCoverageHandler::HandleGCovCoverage(  cmCTestCoverageHandlerContainer* cont){  std::string gcovCommand =    this->CTest->GetCTestConfiguration("CoverageCommand");  if (gcovCommand.empty()) {    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                       "Could not find gcov." << std::endl, this->Quiet);    return 0;  }  std::string gcovExtraFlags =    this->CTest->GetCTestConfiguration("CoverageExtraFlags");  // Immediately skip to next coverage option since codecov is only for Intel  // compiler  if (gcovCommand == "codecov") {    return 0;  }  // Style 1  std::string st1gcovOutputRex1 =    "[0-9]+\\.[0-9]+% of [0-9]+ (source |)lines executed in file (.*)$";  std::string st1gcovOutputRex2 = "^Creating (.*\\.gcov)\\.";  cmsys::RegularExpression st1re1(st1gcovOutputRex1.c_str());  cmsys::RegularExpression st1re2(st1gcovOutputRex2.c_str());  // Style 2  std::string st2gcovOutputRex1 = "^File *[`'](.*)'$";  std::string st2gcovOutputRex2 =    "Lines executed: *[0-9]+\\.[0-9]+% of [0-9]+$";  std::string st2gcovOutputRex3 = "^(.*)reating [`'](.*\\.gcov)'";  std::string st2gcovOutputRex4 = "^(.*):unexpected EOF *$";  std::string st2gcovOutputRex5 = "^(.*):cannot open source file*$";  std::string st2gcovOutputRex6 =    "^(.*):source file is newer than graph file `(.*)'$";  cmsys::RegularExpression st2re1(st2gcovOutputRex1.c_str());  cmsys::RegularExpression st2re2(st2gcovOutputRex2.c_str());  cmsys::RegularExpression st2re3(st2gcovOutputRex3.c_str());  cmsys::RegularExpression st2re4(st2gcovOutputRex4.c_str());  cmsys::RegularExpression st2re5(st2gcovOutputRex5.c_str());  cmsys::RegularExpression st2re6(st2gcovOutputRex6.c_str());  std::vector<std::string> files;  this->FindGCovFiles(files);  if (files.empty()) {    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                       " Cannot find any GCov coverage files." << std::endl,                       this->Quiet);    // No coverage files is a valid thing, so the exit code is 0    return 0;  }  std::string testingDir = this->CTest->GetBinaryDir() + "/Testing";  std::string tempDir = testingDir + "/CoverageInfo";  if (!cmSystemTools::MakeDirectory(tempDir)) {    cmCTestLog(this->CTest, ERROR_MESSAGE,               "Unable to make directory: " << tempDir << std::endl);    cont->Error++;    return 0;  }  cmWorkingDirectory workdir(tempDir);  int gcovStyle = 0;  std::set<std::string> missingFiles;  std::string actualSourceFile;  cmCTestOptionalLog(    this->CTest, HANDLER_OUTPUT,    "   Processing coverage (each . represents one file):" << std::endl,    this->Quiet);  cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "    ", this->Quiet);  int file_count = 0;  // make sure output from gcov is in English!  cmCTestCoverageHandlerLocale locale_C;  static_cast<void>(locale_C);  std::vector<std::string> basecovargs =    cmSystemTools::ParseArguments(gcovExtraFlags.c_str());  basecovargs.insert(basecovargs.begin(), gcovCommand);  basecovargs.push_back("-o");  // files is a list of *.da and *.gcda files with coverage data in them.  // These are binary files that you give as input to gcov so that it will  // give us text output we can analyze to summarize coverage.  //  for (std::string const& f : files) {    cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "." << std::flush,                       this->Quiet);    // Call gcov to get coverage data for this *.gcda file:    //    std::string fileDir = cmSystemTools::GetFilenamePath(f);    std::vector<std::string> covargs = basecovargs;    covargs.push_back(fileDir);    covargs.push_back(f);    const std::string command = joinCommandLine(covargs);    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                       command << std::endl, this->Quiet);    std::string output;    std::string errors;    int retVal = 0;    *cont->OFS << "* Run coverage for: " << fileDir << std::endl;    *cont->OFS << "  Command: " << command << std::endl;    int res = this->CTest->RunCommand(covargs, &output, &errors, &retVal,                                      tempDir.c_str(),                                      cmDuration::zero() /*this->TimeOut*/);    *cont->OFS << "  Output: " << output << std::endl;    *cont->OFS << "  Errors: " << errors << std::endl;    if (!res) {      cmCTestLog(this->CTest, ERROR_MESSAGE,                 "Problem running coverage on file: " << f << std::endl);      cmCTestLog(this->CTest, ERROR_MESSAGE,                 "Command produced error: " << errors << std::endl);      cont->Error++;      continue;    }    if (retVal != 0) {      cmCTestLog(this->CTest, ERROR_MESSAGE,                 "Coverage command returned: "                   << retVal << " while processing: " << f << std::endl);      cmCTestLog(this->CTest, ERROR_MESSAGE,                 "Command produced error: " << cont->Error << std::endl);    }    cmCTestOptionalLog(      this->CTest, HANDLER_VERBOSE_OUTPUT,      "--------------------------------------------------------------"        << std::endl        << output << std::endl        << "--------------------------------------------------------------"        << std::endl,      this->Quiet);    std::vector<std::string> lines;    cmSystemTools::Split(output.c_str(), lines);    for (std::string const& line : lines) {      std::string sourceFile;      std::string gcovFile;      cmCTestOptionalLog(this->CTest, DEBUG,                         "Line: [" << line << "]" << std::endl, this->Quiet);      if (line.empty()) {        // Ignore empty line; probably style 2      } else if (st1re1.find(line)) {        if (gcovStyle == 0) {          gcovStyle = 1;        }        if (gcovStyle != 1) {          cmCTestLog(this->CTest, ERROR_MESSAGE,                     "Unknown gcov output style e1" << std::endl);          cont->Error++;          break;        }        actualSourceFile.clear();        sourceFile = st1re1.match(2);      } else if (st1re2.find(line)) {        if (gcovStyle == 0) {          gcovStyle = 1;        }        if (gcovStyle != 1) {          cmCTestLog(this->CTest, ERROR_MESSAGE,                     "Unknown gcov output style e2" << std::endl);          cont->Error++;          break;        }        gcovFile = st1re2.match(1);      } else if (st2re1.find(line)) {        if (gcovStyle == 0) {          gcovStyle = 2;        }        if (gcovStyle != 2) {          cmCTestLog(this->CTest, ERROR_MESSAGE,                     "Unknown gcov output style e3" << std::endl);          cont->Error++;          break;        }        actualSourceFile.clear();        sourceFile = st2re1.match(1);      } else if (st2re2.find(line)) {        if (gcovStyle == 0) {          gcovStyle = 2;        }        if (gcovStyle != 2) {          cmCTestLog(this->CTest, ERROR_MESSAGE,                     "Unknown gcov output style e4" << std::endl);          cont->Error++;          break;        }      } else if (st2re3.find(line)) {        if (gcovStyle == 0) {          gcovStyle = 2;        }        if (gcovStyle != 2) {          cmCTestLog(this->CTest, ERROR_MESSAGE,                     "Unknown gcov output style e5" << std::endl);          cont->Error++;          break;        }        gcovFile = st2re3.match(2);      } else if (st2re4.find(line)) {        if (gcovStyle == 0) {          gcovStyle = 2;        }        if (gcovStyle != 2) {          cmCTestLog(this->CTest, ERROR_MESSAGE,                     "Unknown gcov output style e6" << std::endl);          cont->Error++;          break;        }        cmCTestOptionalLog(this->CTest, WARNING,                           "Warning: " << st2re4.match(1)                                       << " had unexpected EOF" << std::endl,                           this->Quiet);      } else if (st2re5.find(line)) {        if (gcovStyle == 0) {          gcovStyle = 2;        }        if (gcovStyle != 2) {          cmCTestLog(this->CTest, ERROR_MESSAGE,                     "Unknown gcov output style e7" << std::endl);          cont->Error++;          break;        }        cmCTestOptionalLog(this->CTest, WARNING,                           "Warning: Cannot open file: " << st2re5.match(1)                                                         << std::endl,                           this->Quiet);      } else if (st2re6.find(line)) {        if (gcovStyle == 0) {          gcovStyle = 2;        }        if (gcovStyle != 2) {          cmCTestLog(this->CTest, ERROR_MESSAGE,                     "Unknown gcov output style e8" << std::endl);          cont->Error++;          break;        }        cmCTestOptionalLog(this->CTest, WARNING,                           "Warning: File: " << st2re6.match(1)                                             << " is newer than "                                             << st2re6.match(2) << std::endl,                           this->Quiet);      } else {        // gcov 4.7 can have output lines saying "No executable lines" and        // "Removing 'filename.gcov'"... Don't log those as "errors."        if (line != "No executable lines" &&            !cmSystemTools::StringStartsWith(line.c_str(), "Removing ")) {          cmCTestLog(this->CTest, ERROR_MESSAGE,                     "Unknown gcov output line: [" << line << "]"                                                   << std::endl);          cont->Error++;          // abort();        }      }      // If the last line of gcov output gave us a valid value for gcovFile,      // and we have an actualSourceFile, then insert a (or add to existing)      // SingleFileCoverageVector for actualSourceFile:      //      if (!gcovFile.empty() && !actualSourceFile.empty()) {        cmCTestCoverageHandlerContainer::SingleFileCoverageVector& vec =          cont->TotalCoverage[actualSourceFile];        cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                           "   in gcovFile: " << gcovFile << std::endl,                           this->Quiet);        cmsys::ifstream ifile(gcovFile.c_str());        if (!ifile) {          cmCTestLog(this->CTest, ERROR_MESSAGE,                     "Cannot open file: " << gcovFile << std::endl);        } else {          long cnt = -1;          std::string nl;          while (cmSystemTools::GetLineFromStream(ifile, nl)) {            cnt++;            // TODO: Handle gcov 3.0 non-coverage lines            // Skip empty lines            if (nl.empty()) {              continue;            }            // Skip unused lines            if (nl.size() < 12) {              continue;            }            // Read the coverage count from the beginning of the gcov output            // line            std::string prefix = nl.substr(0, 12);            int cov = atoi(prefix.c_str());            // Read the line number starting at the 10th character of the gcov            // output line            std::string lineNumber = nl.substr(10, 5);            int lineIdx = atoi(lineNumber.c_str()) - 1;            if (lineIdx >= 0) {              while (vec.size() <= static_cast<size_t>(lineIdx)) {                vec.push_back(-1);              }              // Initially all entries are -1 (not used). If we get coverage              // information, increment it to 0 first.              if (vec[lineIdx] < 0) {                if (cov > 0 || prefix.find('#') != std::string::npos) {                  vec[lineIdx] = 0;                }              }              vec[lineIdx] += cov;            }          }        }        actualSourceFile.clear();      }      if (!sourceFile.empty() && actualSourceFile.empty()) {        gcovFile.clear();        // Is it in the source dir or the binary dir?        //        if (IsFileInDir(sourceFile, cont->SourceDir)) {          cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                             "   produced s: " << sourceFile << std::endl,                             this->Quiet);          *cont->OFS << "  produced in source dir: " << sourceFile                     << std::endl;          actualSourceFile = cmSystemTools::CollapseFullPath(sourceFile);        } else if (IsFileInDir(sourceFile, cont->BinaryDir)) {          cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                             "   produced b: " << sourceFile << std::endl,                             this->Quiet);          *cont->OFS << "  produced in binary dir: " << sourceFile                     << std::endl;          actualSourceFile = cmSystemTools::CollapseFullPath(sourceFile);        }        if (actualSourceFile.empty()) {          if (missingFiles.find(sourceFile) == missingFiles.end()) {            cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                               "Something went wrong" << std::endl,                               this->Quiet);            cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                               "Cannot find file: [" << sourceFile << "]"                                                     << std::endl,                               this->Quiet);            cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                               " in source dir: [" << cont->SourceDir << "]"                                                   << std::endl,                               this->Quiet);            cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                               " or binary dir: [" << cont->BinaryDir.size()                                                   << "]" << std::endl,                               this->Quiet);            *cont->OFS << "  Something went wrong. Cannot find file: "                       << sourceFile << " in source dir: " << cont->SourceDir                       << " or binary dir: " << cont->BinaryDir << std::endl;            missingFiles.insert(sourceFile);          }        }      }    }    file_count++;    if (file_count % 50 == 0) {      cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,                         " processed: " << file_count << " out of "                                        << files.size() << std::endl,                         this->Quiet);      cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "    ", this->Quiet);    }  }  return file_count;}int cmCTestCoverageHandler::HandleLCovCoverage(  cmCTestCoverageHandlerContainer* cont){  std::string lcovCommand =    this->CTest->GetCTestConfiguration("CoverageCommand");  std::string lcovExtraFlags =    this->CTest->GetCTestConfiguration("CoverageExtraFlags");  if (lcovCommand != "codecov") {    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                       " Not a valid Intel Coverage command." << std::endl,                       this->Quiet);    return 0;  }  // There is only percentage completed output from LCOV  std::string st2lcovOutputRex3 = "[0-9]+%";  cmsys::RegularExpression st2re3(st2lcovOutputRex3.c_str());  cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                     " This is coverage command: " << lcovCommand << std::endl,                     this->Quiet);  cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                     " These are coverage command flags: " << lcovExtraFlags                                                           << std::endl,                     this->Quiet);  std::vector<std::string> files;  if (!this->FindLCovFiles(files)) {    cmCTestLog(this->CTest, ERROR_MESSAGE,               "Error while finding LCov files.\n");    return 0;  }  if (files.empty()) {    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                       " Cannot find any LCov coverage files." << std::endl,                       this->Quiet);    // No coverage files is a valid thing, so the exit code is 0    return 0;  }  std::string testingDir = this->CTest->GetBinaryDir();  std::set<std::string> missingFiles;  std::string actualSourceFile;  cmCTestOptionalLog(    this->CTest, HANDLER_OUTPUT,    "   Processing coverage (each . represents one file):" << std::endl,    this->Quiet);  cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "    ", this->Quiet);  int file_count = 0;  // make sure output from lcov is in English!  cmCTestCoverageHandlerLocale locale_C;  static_cast<void>(locale_C);  std::vector<std::string> covargs =    cmSystemTools::ParseArguments(lcovExtraFlags.c_str());  covargs.insert(covargs.begin(), lcovCommand);  const std::string command = joinCommandLine(covargs);  // In intel compiler we have to call codecov only once in each executable  // directory. It collects all *.dyn files to generate .dpi file.  for (std::string const& f : files) {    cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "." << std::flush,                       this->Quiet);    std::string fileDir = cmSystemTools::GetFilenamePath(f);    cmWorkingDirectory workdir(fileDir);    if (workdir.Failed()) {      cmCTestLog(this->CTest, ERROR_MESSAGE,                 "Unable to change working directory to "                   << fileDir << " : "                   << std::strerror(workdir.GetLastResult()) << std::endl);      cont->Error++;      continue;    }    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                       "Current coverage dir: " << fileDir << std::endl,                       this->Quiet);    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                       command << std::endl, this->Quiet);    std::string output;    std::string errors;    int retVal = 0;    *cont->OFS << "* Run coverage for: " << fileDir << std::endl;    *cont->OFS << "  Command: " << command << std::endl;    int res = this->CTest->RunCommand(covargs, &output, &errors, &retVal,                                      fileDir.c_str(),                                      cmDuration::zero() /*this->TimeOut*/);    *cont->OFS << "  Output: " << output << std::endl;    *cont->OFS << "  Errors: " << errors << std::endl;    if (!res) {      cmCTestLog(this->CTest, ERROR_MESSAGE,                 "Problem running coverage on file: " << f << std::endl);      cmCTestLog(this->CTest, ERROR_MESSAGE,                 "Command produced error: " << errors << std::endl);      cont->Error++;      continue;    }    if (retVal != 0) {      cmCTestLog(this->CTest, ERROR_MESSAGE,                 "Coverage command returned: "                   << retVal << " while processing: " << f << std::endl);      cmCTestLog(this->CTest, ERROR_MESSAGE,                 "Command produced error: " << cont->Error << std::endl);    }    cmCTestOptionalLog(      this->CTest, HANDLER_VERBOSE_OUTPUT,      "--------------------------------------------------------------"        << std::endl        << output << std::endl        << "--------------------------------------------------------------"        << std::endl,      this->Quiet);    std::vector<std::string> lines;    cmSystemTools::Split(output.c_str(), lines);    for (std::string const& line : lines) {      std::string sourceFile;      std::string lcovFile;      if (line.empty()) {        // Ignore empty line      }      // Look for LCOV files in binary directory      // Intel Compiler creates a CodeCoverage dir for each subfolder and      // each subfolder has LCOV files      cmsys::Glob gl;      gl.RecurseOn();      gl.RecurseThroughSymlinksOff();      std::string dir;      std::vector<std::string> lcovFiles;      dir = this->CTest->GetBinaryDir();      std::string daGlob;      daGlob = dir;      daGlob += "/*.LCOV";      cmCTestOptionalLog(        this->CTest, HANDLER_VERBOSE_OUTPUT,        "   looking for LCOV files in: " << daGlob << std::endl, this->Quiet);      gl.FindFiles(daGlob);      // Keep a list of all LCOV files      lcovFiles.insert(lcovFiles.end(), gl.GetFiles().begin(),                       gl.GetFiles().end());      for (std::string const& file : lcovFiles) {        lcovFile = file;        cmsys::ifstream srcead(lcovFile.c_str());        if (!srcead) {          cmCTestLog(this->CTest, ERROR_MESSAGE,                     "Cannot open file: " << lcovFile << std::endl);        }        std::string srcname;        int success = cmSystemTools::GetLineFromStream(srcead, srcname);        if (!success) {          cmCTestLog(this->CTest, ERROR_MESSAGE,                     "Error while parsing lcov file '"                       << lcovFile << "':"                       << " No source file name found!" << std::endl);          return 0;        }        srcname = srcname.substr(18);        // We can directly read found LCOV files to determine the source        // files        sourceFile = srcname;        actualSourceFile = srcname;        for (std::string const& t : lcovFiles) {          cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                             "Found LCOV File: " << t << std::endl,                             this->Quiet);        }        cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                           "SourceFile: " << sourceFile << std::endl,                           this->Quiet);        cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                           "lCovFile: " << lcovFile << std::endl, this->Quiet);        // If we have some LCOV files to process        if (!lcovFile.empty() && !actualSourceFile.empty()) {          cmCTestCoverageHandlerContainer::SingleFileCoverageVector& vec =            cont->TotalCoverage[actualSourceFile];          cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                             "   in lcovFile: " << lcovFile << std::endl,                             this->Quiet);          cmsys::ifstream ifile(lcovFile.c_str());          if (!ifile) {            cmCTestLog(this->CTest, ERROR_MESSAGE,                       "Cannot open file: " << lcovFile << std::endl);          } else {            long cnt = -1;            std::string nl;            // Skip the first line            cmSystemTools::GetLineFromStream(ifile, nl);            cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                               "File is ready, start reading." << std::endl,                               this->Quiet);            while (cmSystemTools::GetLineFromStream(ifile, nl)) {              cnt++;              // Skip empty lines              if (nl.empty()) {                continue;              }              // Skip unused lines              if (nl.size() < 12) {                continue;              }              // Read the coverage count from the beginning of the lcov              // output line              std::string prefix = nl.substr(0, 17);              int cov = atoi(prefix.c_str());              // Read the line number starting at the 17th character of the              // lcov output line              std::string lineNumber = nl.substr(17, 7);              int lineIdx = atoi(lineNumber.c_str()) - 1;              if (lineIdx >= 0) {                while (vec.size() <= static_cast<size_t>(lineIdx)) {                  vec.push_back(-1);                }                // Initially all entries are -1 (not used). If we get coverage                // information, increment it to 0 first.                if (vec[lineIdx] < 0) {                  if (cov > 0 || prefix.find('#') != std::string::npos) {                    vec[lineIdx] = 0;                  }                }                vec[lineIdx] += cov;              }            }          }          actualSourceFile.clear();        }      }    }    file_count++;    if (file_count % 50 == 0) {      cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,                         " processed: " << file_count << " out of "                                        << files.size() << std::endl,                         this->Quiet);      cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "    ", this->Quiet);    }  }  return file_count;}void cmCTestCoverageHandler::FindGCovFiles(std::vector<std::string>& files){  cmsys::Glob gl;  gl.RecurseOn();  gl.RecurseThroughSymlinksOff();  for (auto const& lm : this->TargetDirs) {    // Skip targets containing no interesting labels.    if (!this->IntersectsFilter(lm.second)) {      continue;    }    // Coverage files appear next to their object files in the target    // support directory.    cmCTestOptionalLog(      this->CTest, HANDLER_VERBOSE_OUTPUT,      "   globbing for coverage in: " << lm.first << std::endl, this->Quiet);    std::string daGlob = lm.first;    daGlob += "/*.da";    gl.FindFiles(daGlob);    files.insert(files.end(), gl.GetFiles().begin(), gl.GetFiles().end());    daGlob = lm.first;    daGlob += "/*.gcda";    gl.FindFiles(daGlob);    files.insert(files.end(), gl.GetFiles().begin(), gl.GetFiles().end());  }}bool cmCTestCoverageHandler::FindLCovFiles(std::vector<std::string>& files){  cmsys::Glob gl;  gl.RecurseOff(); // No need of recurse if -prof_dir${BUILD_DIR} flag is                   // used while compiling.  gl.RecurseThroughSymlinksOff();  std::string buildDir = this->CTest->GetCTestConfiguration("BuildDirectory");  cmWorkingDirectory workdir(buildDir);  if (workdir.Failed()) {    cmCTestLog(this->CTest, ERROR_MESSAGE,               "Unable to change working directory to " << buildDir                                                        << std::endl);    return false;  }  // Run profmerge to merge all *.dyn files into dpi files  if (!cmSystemTools::RunSingleCommand("profmerge")) {    cmCTestLog(this->CTest, ERROR_MESSAGE, "Error while running profmerge.\n");    return false;  }  // DPI file should appear in build directory  std::string daGlob;  daGlob = buildDir;  daGlob += "/*.dpi";  cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                     "   looking for dpi files in: " << daGlob << std::endl,                     this->Quiet);  if (!gl.FindFiles(daGlob)) {    cmCTestLog(this->CTest, ERROR_MESSAGE,               "Error while finding files matching " << daGlob << std::endl);    return false;  }  files.insert(files.end(), gl.GetFiles().begin(), gl.GetFiles().end());  cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                     "Now searching in: " << daGlob << std::endl, this->Quiet);  return true;}int cmCTestCoverageHandler::HandleTracePyCoverage(  cmCTestCoverageHandlerContainer* cont){  cmsys::Glob gl;  gl.RecurseOn();  gl.RecurseThroughSymlinksOff();  std::string daGlob = cont->BinaryDir + "/*.cover";  gl.FindFiles(daGlob);  std::vector<std::string> files = gl.GetFiles();  if (files.empty()) {    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                       " Cannot find any Python Trace.py coverage files."                         << std::endl,                       this->Quiet);    // No coverage files is a valid thing, so the exit code is 0    return 0;  }  std::string testingDir = this->CTest->GetBinaryDir() + "/Testing";  std::string tempDir = testingDir + "/CoverageInfo";  cmSystemTools::MakeDirectory(tempDir);  int file_count = 0;  for (std::string const& file : files) {    std::string fileName = this->FindFile(cont, file);    if (fileName.empty()) {      cmCTestLog(this->CTest, ERROR_MESSAGE,                 "Cannot find source Python file corresponding to: "                   << file << std::endl);      continue;    }    std::string actualSourceFile = cmSystemTools::CollapseFullPath(fileName);    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                       "   Check coverage for file: " << actualSourceFile                                                      << std::endl,                       this->Quiet);    cmCTestCoverageHandlerContainer::SingleFileCoverageVector* vec =      &cont->TotalCoverage[actualSourceFile];    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                       "   in file: " << file << std::endl, this->Quiet);    cmsys::ifstream ifile(file.c_str());    if (!ifile) {      cmCTestLog(this->CTest, ERROR_MESSAGE,                 "Cannot open file: " << file << std::endl);    } else {      long cnt = -1;      std::string nl;      while (cmSystemTools::GetLineFromStream(ifile, nl)) {        cnt++;        // Skip empty lines        if (nl.empty()) {          continue;        }        // Skip unused lines        if (nl.size() < 12) {          continue;        }        // Read the coverage count from the beginning of the Trace.py output        // line        std::string prefix = nl.substr(0, 6);        if (prefix[5] != ' ' && prefix[5] != ':') {          // This is a hack. We should really do something more elaborate          prefix = nl.substr(0, 7);          if (prefix[6] != ' ' && prefix[6] != ':') {            prefix = nl.substr(0, 8);            if (prefix[7] != ' ' && prefix[7] != ':') {              cmCTestLog(this->CTest, ERROR_MESSAGE,                         "Currently the limit is maximum coverage of 999999"                           << std::endl);            }          }        }        int cov = atoi(prefix.c_str());        if (prefix[prefix.size() - 1] != ':') {          // This line does not have ':' so no coverage here. That said,          // Trace.py does not handle not covered lines versus comments etc.          // So, this will be set to 0.          cov = 0;        }        cmCTestOptionalLog(          this->CTest, DEBUG,          "Prefix: " << prefix << " cov: " << cov << std::endl, this->Quiet);        // Read the line number starting at the 10th character of the gcov        // output line        long lineIdx = cnt;        if (lineIdx >= 0) {          while (vec->size() <= static_cast<size_t>(lineIdx)) {            vec->push_back(-1);          }          // Initially all entries are -1 (not used). If we get coverage          // information, increment it to 0 first.          if ((*vec)[lineIdx] < 0) {            if (cov >= 0) {              (*vec)[lineIdx] = 0;            }          }          (*vec)[lineIdx] += cov;        }      }    }    ++file_count;  }  return file_count;}std::string cmCTestCoverageHandler::FindFile(  cmCTestCoverageHandlerContainer* cont, std::string const& fileName){  std::string fileNameNoE =    cmSystemTools::GetFilenameWithoutLastExtension(fileName);  // First check in source and binary directory  std::string fullName = cont->SourceDir + "/" + fileNameNoE + ".py";  if (cmSystemTools::FileExists(fullName)) {    return fullName;  }  fullName = cont->BinaryDir + "/" + fileNameNoE + ".py";  if (cmSystemTools::FileExists(fullName)) {    return fullName;  }  return "";}// This is a header put on each marked up source filenamespace {const char* bullseyeHelp[] = {  "    Coverage produced by bullseye covbr tool: ",  "      www.bullseye.com/help/ref_covbr.html",  "    * An arrow --> indicates incomplete coverage.",  "    * An X indicates a function that was invoked, a switch label that ",  "      was exercised, a try-block that finished, or an exception handler ",  "      that was invoked.",  "    * A T or F indicates a boolean decision that evaluated true or false,",  "      respectively.",  "    * A t or f indicates a boolean condition within a decision if the ",  "      condition evaluated true or false, respectively.",  "    * A k indicates a constant decision or condition.",  "    * The slash / means this probe is excluded from summary results. ",  nullptr};}int cmCTestCoverageHandler::RunBullseyeCoverageBranch(  cmCTestCoverageHandlerContainer* cont,  std::set<std::string>& coveredFileNames, std::vector<std::string>& files,  std::vector<std::string>& filesFullPath){  if (files.size() != filesFullPath.size()) {    cmCTestLog(this->CTest, ERROR_MESSAGE,               "Files and full path files not the same size?:\n");    return 0;  }  // create the output stream for the CoverageLog-N.xml file  cmGeneratedFileStream covLogFile;  cmXMLWriter covLogXML(covLogFile);  int logFileCount = 0;  if (!this->StartCoverageLogFile(covLogFile, logFileCount)) {    return -1;  }  this->StartCoverageLogXML(covLogXML);  // for each file run covbr on that file to get the coverage  // information for that file  std::string outputFile;  cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                     "run covbr: " << std::endl, this->Quiet);  if (!this->RunBullseyeCommand(cont, "covbr", nullptr, outputFile)) {    cmCTestLog(this->CTest, ERROR_MESSAGE,               "error running covbr for."                 << "\n");    return -1;  }  cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                     "covbr output in  " << outputFile << std::endl,                     this->Quiet);  // open the output file  cmsys::ifstream fin(outputFile.c_str());  if (!fin) {    cmCTestLog(this->CTest, ERROR_MESSAGE,               "Cannot open coverage file: " << outputFile << std::endl);    return 0;  }  std::map<std::string, std::string> fileMap;  std::vector<std::string>::iterator fp = filesFullPath.begin();  for (std::vector<std::string>::iterator f = files.begin(); f != files.end();       ++f, ++fp) {    fileMap[*f] = *fp;  }  int count = 0; // keep count of the number of files  // Now parse each line from the bullseye cov log file  std::string lineIn;  bool valid = false; // are we in a valid output file  int line = 0;       // line of the current file  std::string file;  while (cmSystemTools::GetLineFromStream(fin, lineIn)) {    bool startFile = false;    if (lineIn.size() > 1 && lineIn[lineIn.size() - 1] == ':') {      file = lineIn.substr(0, lineIn.size() - 1);      if (coveredFileNames.find(file) != coveredFileNames.end()) {        startFile = true;      }    }    if (startFile) {      // if we are in a valid file close it because a new one started      if (valid) {        covLogXML.EndElement(); // Report        covLogXML.EndElement(); // File      }      // only allow 100 files in each log file      if (count != 0 && count % 100 == 0) {        cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                           "start a new log file: " << count << std::endl,                           this->Quiet);        this->EndCoverageLogXML(covLogXML);        this->EndCoverageLogFile(covLogFile, logFileCount);        logFileCount++;        if (!this->StartCoverageLogFile(covLogFile, logFileCount)) {          return -1;        }        this->StartCoverageLogXML(covLogXML);        count++; // move on one      }      std::map<std::string, std::string>::iterator i = fileMap.find(file);      // if the file should be covered write out the header for that file      if (i != fileMap.end()) {        // we have a new file so count it in the output        count++;        cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                           "Produce coverage for file: " << file << " "                                                         << count << std::endl,                           this->Quiet);        // start the file output        covLogXML.StartElement("File");        covLogXML.Attribute("Name", i->first);        covLogXML.Attribute(          "FullPath", this->CTest->GetShortPathToFile(i->second.c_str()));        covLogXML.StartElement("Report");        // write the bullseye header        line = 0;        for (int k = 0; bullseyeHelp[k] != nullptr; ++k) {          covLogXML.StartElement("Line");          covLogXML.Attribute("Number", line);          covLogXML.Attribute("Count", -1);          covLogXML.Content(bullseyeHelp[k]);          covLogXML.EndElement(); // Line          line++;        }        valid = true; // we are in a valid file section      } else {        // this is not a file that we want coverage for        valid = false;      }    }    // we are not at a start file, and we are in a valid file output the line    else if (valid) {      covLogXML.StartElement("Line");      covLogXML.Attribute("Number", line);      covLogXML.Attribute("Count", -1);      covLogXML.Content(lineIn);      covLogXML.EndElement(); // Line      line++;    }  }  // if we ran out of lines a valid file then close that file  if (valid) {    covLogXML.EndElement(); // Report    covLogXML.EndElement(); // File  }  this->EndCoverageLogXML(covLogXML);  this->EndCoverageLogFile(covLogFile, logFileCount);  return 1;}int cmCTestCoverageHandler::RunBullseyeCommand(  cmCTestCoverageHandlerContainer* cont, const char* cmd, const char* arg,  std::string& outputFile){  std::string program = cmSystemTools::FindProgram(cmd);  if (program.empty()) {    cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot find :" << cmd << "\n");    return 0;  }  if (arg) {    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                       "Run : " << program << " " << arg << "\n", this->Quiet);  } else {    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                       "Run : " << program << "\n", this->Quiet);  }  // create a process object and start it  cmCTestRunProcess runCoverageSrc;  runCoverageSrc.SetCommand(program.c_str());  runCoverageSrc.AddArgument(arg);  std::string stdoutFile = cont->BinaryDir + "/Testing/Temporary/";  stdoutFile += this->GetCTestInstance()->GetCurrentTag();  stdoutFile += "-";  stdoutFile += cmd;  std::string stderrFile = stdoutFile;  stdoutFile += ".stdout";  stderrFile += ".stderr";  runCoverageSrc.SetStdoutFile(stdoutFile.c_str());  runCoverageSrc.SetStderrFile(stderrFile.c_str());  if (!runCoverageSrc.StartProcess()) {    cmCTestLog(this->CTest, ERROR_MESSAGE,               "Could not run : " << program << " " << arg << "\n"                                  << "kwsys process state : "                                  << runCoverageSrc.GetProcessState());    return 0;  }  // since we set the output file names wait for it to end  runCoverageSrc.WaitForExit();  outputFile = stdoutFile;  return 1;}int cmCTestCoverageHandler::RunBullseyeSourceSummary(  cmCTestCoverageHandlerContainer* cont){  // Run the covsrc command and create a temp outputfile  std::string outputFile;  if (!this->RunBullseyeCommand(cont, "covsrc", "-c", outputFile)) {    cmCTestLog(this->CTest, ERROR_MESSAGE, "error running covsrc:\n");    return 0;  }  std::ostream& tmpLog = *cont->OFS;  // copen the Coverage.xml file in the Testing directory  cmGeneratedFileStream covSumFile;  cmXMLWriter xml(covSumFile);  if (!this->StartResultingXML(cmCTest::PartCoverage, "Coverage",                               covSumFile)) {    cmCTestLog(this->CTest, ERROR_MESSAGE,               "Cannot open coverage summary file." << std::endl);    return 0;  }  this->CTest->StartXML(xml, this->AppendXML);  auto elapsed_time_start = std::chrono::steady_clock::now();  std::string coverage_start_time = this->CTest->CurrentTime();  xml.StartElement("Coverage");  xml.Element("StartDateTime", coverage_start_time);  xml.Element("StartTime", std::chrono::system_clock::now());  std::string stdline;  std::string errline;  // expected output:  // first line is:  // "Source","Function Coverage","out of","%","C/D Coverage","out of","%"  // after that data follows in that format  std::string sourceFile;  int functionsCalled = 0;  int totalFunctions = 0;  int percentFunction = 0;  int branchCovered = 0;  int totalBranches = 0;  int percentBranch = 0;  double total_tested = 0;  double total_untested = 0;  double total_functions = 0;  double percent_coverage = 0;  double number_files = 0;  std::vector<std::string> coveredFiles;  std::vector<std::string> coveredFilesFullPath;  // Read and parse the summary output file  cmsys::ifstream fin(outputFile.c_str());  if (!fin) {    cmCTestLog(this->CTest, ERROR_MESSAGE,               "Cannot open coverage summary file: " << outputFile                                                     << std::endl);    return 0;  }  std::set<std::string> coveredFileNames;  while (cmSystemTools::GetLineFromStream(fin, stdline)) {    // if we have a line of output from stdout    if (!stdline.empty()) {      // parse the comma separated output      this->ParseBullsEyeCovsrcLine(        stdline, sourceFile, functionsCalled, totalFunctions, percentFunction,        branchCovered, totalBranches, percentBranch);      // The first line is the header      if (sourceFile == "Source" || sourceFile == "Total") {        continue;      }      std::string file = sourceFile;      coveredFileNames.insert(file);      if (!cmSystemTools::FileIsFullPath(sourceFile)) {        // file will be relative to the binary dir        file = cont->BinaryDir;        file += "/";        file += sourceFile;      }      file = cmSystemTools::CollapseFullPath(file);      bool shouldIDoCoverage =        this->ShouldIDoCoverage(file, cont->SourceDir, cont->BinaryDir);      if (!shouldIDoCoverage) {        cmCTestOptionalLog(          this->CTest, HANDLER_VERBOSE_OUTPUT,          ".NoDartCoverage found, so skip coverage check for: " << file                                                                << std::endl,          this->Quiet);        continue;      }      cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                         "Doing coverage for: " << file << std::endl,                         this->Quiet);      coveredFiles.push_back(sourceFile);      coveredFilesFullPath.push_back(file);      number_files++;      total_functions += totalFunctions;      total_tested += functionsCalled;      total_untested += (totalFunctions - functionsCalled);      std::string fileName = cmSystemTools::GetFilenameName(file);      std::string shortFileName =        this->CTest->GetShortPathToFile(file.c_str());      float cper = static_cast<float>(percentBranch + percentFunction);      if (totalBranches > 0) {        cper /= 2.0f;      }      percent_coverage += static_cast<double>(cper);      float cmet = static_cast<float>(percentFunction + percentBranch);      if (totalBranches > 0) {        cmet /= 2.0f;      }      cmet /= 100.0f;      tmpLog << stdline << "\n";      tmpLog << fileName << "\n";      tmpLog << "functionsCalled: " << functionsCalled / 100 << "\n";      tmpLog << "totalFunctions: " << totalFunctions / 100 << "\n";      tmpLog << "percentFunction: " << percentFunction << "\n";      tmpLog << "branchCovered: " << branchCovered << "\n";      tmpLog << "totalBranches: " << totalBranches << "\n";      tmpLog << "percentBranch: " << percentBranch << "\n";      tmpLog << "percentCoverage: " << percent_coverage << "\n";      tmpLog << "coverage metric: " << cmet << "\n";      xml.StartElement("File");      xml.Attribute("Name", sourceFile);      xml.Attribute("FullPath", shortFileName);      xml.Attribute("Covered", cmet > 0 ? "true" : "false");      xml.Element("BranchesTested", branchCovered);      xml.Element("BranchesUnTested", totalBranches - branchCovered);      xml.Element("FunctionsTested", functionsCalled);      xml.Element("FunctionsUnTested", totalFunctions - functionsCalled);      // Hack for conversion of function to loc assume a function      // has 100 lines of code      xml.Element("LOCTested", functionsCalled * 100);      xml.Element("LOCUnTested", (totalFunctions - functionsCalled) * 100);      xml.Element("PercentCoverage", cper);      xml.Element("CoverageMetric", cmet);      this->WriteXMLLabels(xml, shortFileName);      xml.EndElement(); // File    }  }  std::string end_time = this->CTest->CurrentTime();  xml.Element("LOCTested", total_tested);  xml.Element("LOCUntested", total_untested);  xml.Element("LOC", total_functions);  xml.Element("PercentCoverage", SAFEDIV(percent_coverage, number_files));  xml.Element("EndDateTime", end_time);  xml.Element("EndTime", std::chrono::system_clock::now());  xml.Element("ElapsedMinutes",              std::chrono::duration_cast<std::chrono::minutes>(                std::chrono::steady_clock::now() - elapsed_time_start)                .count());  xml.EndElement(); // Coverage  this->CTest->EndXML(xml);  // Now create the coverage information for each file  return this->RunBullseyeCoverageBranch(cont, coveredFileNames, coveredFiles,                                         coveredFilesFullPath);}int cmCTestCoverageHandler::HandleBullseyeCoverage(  cmCTestCoverageHandlerContainer* cont){  std::string covfile;  if (!cmSystemTools::GetEnv("COVFILE", covfile) || covfile.empty()) {    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                       " COVFILE environment variable not found, not running "                       " bullseye\n",                       this->Quiet);    return 0;  }  cmCTestOptionalLog(    this->CTest, HANDLER_VERBOSE_OUTPUT,    " run covsrc with COVFILE=[" << covfile << "]" << std::endl, this->Quiet);  if (!this->RunBullseyeSourceSummary(cont)) {    cmCTestLog(this->CTest, ERROR_MESSAGE,               "Error running bullseye summary.\n");    return 0;  }  cmCTestOptionalLog(this->CTest, DEBUG,                     "HandleBullseyeCoverage return 1 " << std::endl,                     this->Quiet);  return 1;}bool cmCTestCoverageHandler::GetNextInt(std::string const& inputLine,                                        std::string::size_type& pos,                                        int& value){  std::string::size_type start = pos;  pos = inputLine.find(',', start);  value = atoi(inputLine.substr(start, pos).c_str());  if (pos == std::string::npos) {    return true;  }  pos++;  return true;}bool cmCTestCoverageHandler::ParseBullsEyeCovsrcLine(  std::string const& inputLine, std::string& sourceFile, int& functionsCalled,  int& totalFunctions, int& percentFunction, int& branchCovered,  int& totalBranches, int& percentBranch){  // find the first comma  std::string::size_type pos = inputLine.find(',');  if (pos == std::string::npos) {    cmCTestLog(this->CTest, ERROR_MESSAGE,               "Error parsing string : " << inputLine << "\n");    return false;  }  // the source file has "" around it so extract out the file name  sourceFile = inputLine.substr(1, pos - 2);  pos++;  if (!this->GetNextInt(inputLine, pos, functionsCalled)) {    return false;  }  if (!this->GetNextInt(inputLine, pos, totalFunctions)) {    return false;  }  if (!this->GetNextInt(inputLine, pos, percentFunction)) {    return false;  }  if (!this->GetNextInt(inputLine, pos, branchCovered)) {    return false;  }  if (!this->GetNextInt(inputLine, pos, totalBranches)) {    return false;  }  if (!this->GetNextInt(inputLine, pos, percentBranch)) {    return false;  }  // should be at the end now  if (pos != std::string::npos) {    cmCTestLog(this->CTest, ERROR_MESSAGE,               "Error parsing input : "                 << inputLine << " last pos not npos =  " << pos << "\n");  }  return true;}int cmCTestCoverageHandler::GetLabelId(std::string const& label){  LabelIdMapType::iterator i = this->LabelIdMap.find(label);  if (i == this->LabelIdMap.end()) {    int n = int(this->Labels.size());    this->Labels.push_back(label);    LabelIdMapType::value_type entry(label, n);    i = this->LabelIdMap.insert(entry).first;  }  return i->second;}void cmCTestCoverageHandler::LoadLabels(){  std::string fileList = this->CTest->GetBinaryDir();  fileList += cmake::GetCMakeFilesDirectory();  fileList += "/TargetDirectories.txt";  cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                     " target directory list [" << fileList << "]\n",                     this->Quiet);  cmsys::ifstream finList(fileList.c_str());  std::string line;  while (cmSystemTools::GetLineFromStream(finList, line)) {    this->LoadLabels(line.c_str());  }}void cmCTestCoverageHandler::LoadLabels(const char* dir){  LabelSet& dirLabels = this->TargetDirs[dir];  std::string fname = dir;  fname += "/Labels.txt";  cmsys::ifstream fin(fname.c_str());  if (!fin) {    return;  }  cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,                     " loading labels from [" << fname << "]\n", this->Quiet);  bool inTarget = true;  std::string source;  std::string line;  std::vector<int> targetLabels;  while (cmSystemTools::GetLineFromStream(fin, line)) {    if (line.empty() || line[0] == '#') {      // Ignore blank and comment lines.      continue;    }    if (line[0] == ' ') {      // Label lines appear indented by one space.      std::string label = line.substr(1);      int id = this->GetLabelId(label);      dirLabels.insert(id);      if (inTarget) {        targetLabels.push_back(id);      } else {        this->SourceLabels[source].insert(id);      }    } else {      // Non-indented lines specify a source file name.  The first one      // is the end of the target-wide labels.      inTarget = false;      source = this->CTest->GetShortPathToFile(line.c_str());      // Label the source with the target labels.      LabelSet& labelSet = this->SourceLabels[source];      labelSet.insert(targetLabels.begin(), targetLabels.end());    }  }}void cmCTestCoverageHandler::WriteXMLLabels(cmXMLWriter& xml,                                            std::string const& source){  LabelMapType::const_iterator li = this->SourceLabels.find(source);  if (li != this->SourceLabels.end() && !li->second.empty()) {    xml.StartElement("Labels");    for (auto const& ls : li->second) {      xml.Element("Label", this->Labels[ls]);    }    xml.EndElement(); // Labels  }}void cmCTestCoverageHandler::SetLabelFilter(  std::set<std::string> const& labels){  this->LabelFilter.clear();  for (std::string const& l : labels) {    this->LabelFilter.insert(this->GetLabelId(l));  }}bool cmCTestCoverageHandler::IntersectsFilter(LabelSet const& labels){  // If there is no label filter then nothing is filtered out.  if (this->LabelFilter.empty()) {    return true;  }  std::vector<int> ids;  std::set_intersection(labels.begin(), labels.end(),                        this->LabelFilter.begin(), this->LabelFilter.end(),                        std::back_inserter(ids));  return !ids.empty();}bool cmCTestCoverageHandler::IsFilteredOut(std::string const& source){  // If there is no label filter then nothing is filtered out.  if (this->LabelFilter.empty()) {    return false;  }  // The source is filtered out if it does not have any labels in  // common with the filter set.  std::string shortSrc = this->CTest->GetShortPathToFile(source.c_str());  LabelMapType::const_iterator li = this->SourceLabels.find(shortSrc);  if (li != this->SourceLabels.end()) {    return !this->IntersectsFilter(li->second);  }  return true;}std::set<std::string> cmCTestCoverageHandler::FindUncoveredFiles(  cmCTestCoverageHandlerContainer* cont){  std::set<std::string> extraMatches;  for (std::string const& ecg : this->ExtraCoverageGlobs) {    cmsys::Glob gl;    gl.RecurseOn();    gl.RecurseThroughSymlinksOff();    std::string glob = cont->SourceDir + "/" + ecg;    gl.FindFiles(glob);    std::vector<std::string> files = gl.GetFiles();    for (std::string const& f : files) {      if (this->ShouldIDoCoverage(f, cont->SourceDir, cont->BinaryDir)) {        extraMatches.insert(this->CTest->GetShortPathToFile(f.c_str()));      }    }  }  if (!extraMatches.empty()) {    for (auto const& i : cont->TotalCoverage) {      std::string shortPath = this->CTest->GetShortPathToFile(i.first.c_str());      extraMatches.erase(shortPath);    }  }  return extraMatches;}
 |