int cmCTestCoverageHandler::ProcessHandler()

in Source/CTest/cmCTestCoverageHandler.cxx [194:577]


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");

  if (binaryDir.empty()) {
    cmCTestLog(this->CTest, ERROR_MESSAGE,
               "Binary directory is not set.  "
               "No coverage checking will be performed."
                 << std::endl);
    return 0;
  }
  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->CMake, 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);
    }

    std::string const 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);
    }

    std::string const fileName = cmSystemTools::GetFilenameName(fullFileName);
    std::string const shortFileName =
      this->CTest->GetShortPathToFile(fullFileName);
    cmCTestCoverageHandlerContainer::SingleFileCoverageVector const& 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));
    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;
}