| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361 | /* 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 "cmAlgorithms.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 "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);  }  cmCTestRunProcess(const cmCTestRunProcess&) = delete;  cmCTestRunProcess& operator=(const cmCTestRunProcess&) = delete;  void SetCommand(const char* command)  {    this->CommandLineStrings.clear();    this->CommandLineStrings.emplace_back(command);  }  void AddArgument(const char* arg)  {    if (arg) {      this->CommandLineStrings.emplace_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.data());    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() = default;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, checkDir);  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,                                                     checkDir);  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.emplace_back(rex);  }  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");    }  }  cmCTestCoverageHandlerLocale(const cmCTestCoverageHandlerLocale&) = delete;  cmCTestCoverageHandlerLocale& operator=(    const cmCTestCoverageHandlerLocale&) = delete;  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;  g2.SetRecurse(true);  std::string binaryDir = this->CTest->GetCTestConfiguration("BuildDirectory");  std::string binCoverageFile = binaryDir + "/*jacoco.xml";  g2.FindFiles(binCoverageFile);  cmAppend(files, g2.GetFiles());  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);  basecovargs.insert(basecovargs.begin(), gcovCommand);  basecovargs.emplace_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;    cmsys::SystemTools::Split(output, 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);  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;    cmsys::SystemTools::Split(output, 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      cmAppend(lcovFiles, gl.GetFiles());      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);    cmAppend(files, gl.GetFiles());    daGlob = lm.first;    daGlob += "/*.gcda";    gl.FindFiles(daGlob);    cmAppend(files, gl.GetFiles());  }}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;  }  cmAppend(files, gl.GetFiles());  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 += "/CMakeFiles";  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;}
 |