| /*============================================================================ |
| CMake - Cross Platform Makefile Generator |
| Copyright 2000-2009 Kitware, Inc., Insight Software Consortium |
| |
| Distributed under the OSI-approved BSD License (the "License"); |
| see accompanying file Copyright.txt for details. |
| |
| This software is distributed WITHOUT ANY WARRANTY; without even the |
| implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the License for more information. |
| ============================================================================*/ |
| #include "cmCTestCoverageHandler.h" |
| #include "cmParsePHPCoverage.h" |
| #include "cmCTest.h" |
| #include "cmake.h" |
| #include "cmMakefile.h" |
| #include "cmSystemTools.h" |
| #include "cmGeneratedFileStream.h" |
| #include "cmXMLSafe.h" |
| |
| #include <cmsys/Process.h> |
| #include <cmsys/RegularExpression.hxx> |
| #include <cmsys/Glob.hxx> |
| #include <cmsys/stl/iterator> |
| #include <cmsys/stl/algorithm> |
| |
| #include <stdlib.h> |
| #include <math.h> |
| #include <float.h> |
| |
| #define SAFEDIV(x,y) (((y)!=0)?((x)/(y)):(0)) |
| |
| class cmCTestRunProcess |
| { |
| public: |
| cmCTestRunProcess() |
| { |
| this->Process = cmsysProcess_New(); |
| this->PipeState = -1; |
| this->TimeOut = -1; |
| } |
| ~cmCTestRunProcess() |
| { |
| if(!(this->PipeState == -1) |
| && !(this->PipeState == cmsysProcess_Pipe_None ) |
| && !(this->PipeState == cmsysProcess_Pipe_Timeout)) |
| { |
| this->WaitForExit(); |
| } |
| cmsysProcess_Delete(this->Process); |
| } |
| void SetCommand(const char* command) |
| { |
| this->CommandLineStrings.clear(); |
| this->CommandLineStrings.push_back(command);; |
| } |
| void AddArgument(const char* arg) |
| { |
| if(arg) |
| { |
| this->CommandLineStrings.push_back(arg); |
| } |
| } |
| void SetWorkingDirectory(const char* dir) |
| { |
| this->WorkingDirectory = dir; |
| } |
| void SetTimeout(double t) |
| { |
| this->TimeOut = t; |
| } |
| bool StartProcess() |
| { |
| std::vector<const char*> args; |
| for(std::vector<std::string>::iterator i = |
| this->CommandLineStrings.begin(); |
| i != this->CommandLineStrings.end(); ++i) |
| { |
| args.push_back(i->c_str()); |
| } |
| args.push_back(0); // null terminate |
| cmsysProcess_SetCommand(this->Process, &*args.begin()); |
| if(this->WorkingDirectory.size()) |
| { |
| cmsysProcess_SetWorkingDirectory(this->Process, |
| this->WorkingDirectory.c_str()); |
| } |
| |
| cmsysProcess_SetOption(this->Process, |
| cmsysProcess_Option_HideWindow, 1); |
| if(this->TimeOut != -1) |
| { |
| cmsysProcess_SetTimeout(this->Process, this->TimeOut); |
| } |
| cmsysProcess_Execute(this->Process); |
| this->PipeState = cmsysProcess_GetState(this->Process); |
| // if the process is running or exited return true |
| if(this->PipeState == cmsysProcess_State_Executing |
| || this->PipeState == cmsysProcess_State_Exited) |
| { |
| return true; |
| } |
| return false; |
| } |
| 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 =0) |
| { |
| 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; |
| double TimeOut; |
| }; |
| |
| |
| //---------------------------------------------------------------------- |
| |
| //---------------------------------------------------------------------- |
| cmCTestCoverageHandler::cmCTestCoverageHandler() |
| { |
| } |
| |
| //---------------------------------------------------------------------- |
| void cmCTestCoverageHandler::Initialize() |
| { |
| this->Superclass::Initialize(); |
| this->CustomCoverageExclude.clear(); |
| this->SourceLabels.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.c_str()); |
| std::vector<std::string> const& files = gl.GetFiles(); |
| for(std::vector<std::string>::const_iterator fi = files.begin(); |
| fi != files.end(); ++fi) |
| { |
| log << "Removing old coverage log: " << *fi << "\n"; |
| cmSystemTools::RemoveFile(fi->c_str()); |
| } |
| } |
| |
| //---------------------------------------------------------------------- |
| bool cmCTestCoverageHandler::StartCoverageLogFile( |
| cmGeneratedFileStream& covLogFile, int logFileCount) |
| { |
| char covLogFilename[1024]; |
| sprintf(covLogFilename, "CoverageLog-%d", logFileCount); |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Open file: " |
| << covLogFilename << std::endl); |
| if(!this->StartResultingXML(cmCTest::PartCoverage, |
| covLogFilename, covLogFile)) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot open log file: " |
| << covLogFilename << std::endl); |
| return false; |
| } |
| std::string local_start_time = this->CTest->CurrentTime(); |
| this->CTest->StartXML(covLogFile, this->AppendXML); |
| covLogFile << "<CoverageLog>" << std::endl |
| << "\t<StartDateTime>" << local_start_time << "</StartDateTime>" |
| << "\t<StartTime>" |
| << static_cast<unsigned int>(cmSystemTools::GetTime()) |
| << "</StartTime>" |
| << std::endl; |
| return true; |
| } |
| |
| //---------------------------------------------------------------------- |
| void cmCTestCoverageHandler::EndCoverageLogFile(cmGeneratedFileStream& ostr, |
| int logFileCount) |
| { |
| std::string local_end_time = this->CTest->CurrentTime(); |
| ostr << "\t<EndDateTime>" << local_end_time << "</EndDateTime>" << std::endl |
| << "\t<EndTime>" << |
| static_cast<unsigned int>(cmSystemTools::GetTime()) |
| << "</EndTime>" << std::endl |
| << "</CoverageLog>" << std::endl; |
| this->CTest->EndXML(ostr); |
| char covLogFilename[1024]; |
| sprintf(covLogFilename, "CoverageLog-%d.xml", logFileCount); |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Close file: " |
| << covLogFilename << std::endl); |
| ostr.Close(); |
| } |
| |
| //---------------------------------------------------------------------- |
| bool cmCTestCoverageHandler::ShouldIDoCoverage(const char* file, |
| const char* srcDir, |
| const char* binDir) |
| { |
| if(this->IsFilteredOut(file)) |
| { |
| return false; |
| } |
| |
| std::vector<cmsys::RegularExpression>::iterator sit; |
| for ( sit = this->CustomCoverageExcludeRegex.begin(); |
| sit != this->CustomCoverageExcludeRegex.end(); ++ sit ) |
| { |
| if ( sit->find(file) ) |
| { |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " File " << file |
| << " is excluded in CTestCustom.ctest" << std::endl;); |
| 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.c_str(), |
| fSrcDir.c_str()); |
| bool buildSubDir = cmSystemTools::IsSubDirectory(fFile.c_str(), |
| fBinDir.c_str()); |
| // Always check parent directory of the file. |
| std::string fileDir = cmSystemTools::GetFilenamePath(fFile.c_str()); |
| std::string checkDir; |
| |
| // We also need to check the binary/source directory pair. |
| if ( sourceSubDir && buildSubDir ) |
| { |
| if ( fSrcDir.size() > fBinDir.size() ) |
| { |
| checkDir = fSrcDir; |
| } |
| else |
| { |
| checkDir = fBinDir; |
| } |
| } |
| else if ( sourceSubDir ) |
| { |
| checkDir = fSrcDir; |
| } |
| else if ( buildSubDir ) |
| { |
| checkDir = fBinDir; |
| } |
| std::string ndc |
| = cmSystemTools::FileExistsInParentDirectories(".NoDartCoverage", |
| fFile.c_str(), checkDir.c_str()); |
| if ( ndc.size() ) |
| { |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Found: " << ndc.c_str() |
| << " so skip coverage of " << file << std::endl); |
| 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.size() ) |
| { |
| relPath = cmSystemTools::RelativePath(checkDir.c_str(), |
| fFile.c_str()); |
| } |
| else |
| { |
| relPath = fFile; |
| } |
| if ( checkDir == fSrcDir ) |
| { |
| checkDir = fBinDir; |
| } |
| else |
| { |
| checkDir = fSrcDir; |
| } |
| fFile = checkDir + "/" + relPath; |
| fFile = cmSystemTools::GetFilenamePath(fFile.c_str()); |
| |
| if ( fileDir == fFile ) |
| { |
| // This is in-source build, so we trust the previous check. |
| return true; |
| } |
| |
| ndc = cmSystemTools::FileExistsInParentDirectories(".NoDartCoverage", |
| fFile.c_str(), checkDir.c_str()); |
| if ( ndc.size() ) |
| { |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Found: " << ndc.c_str() |
| << " so skip coverage of: " << file << std::endl); |
| 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() < 120) |
| { |
| return error; |
| } |
| |
| std::string coverage_start_time = this->CTest->CurrentTime(); |
| unsigned int coverage_start_time_time = static_cast<unsigned int>( |
| cmSystemTools::GetTime()); |
| std::string sourceDir |
| = this->CTest->GetCTestConfiguration("SourceDirectory"); |
| std::string binaryDir |
| = this->CTest->GetCTestConfiguration("BuildDirectory"); |
| |
| this->LoadLabels(); |
| |
| cmGeneratedFileStream ofs; |
| double elapsed_time_start = cmSystemTools::GetTime(); |
| if ( !this->StartLogFile("Coverage", ofs) ) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, |
| "Cannot create LastCoverage.log file" << std::endl); |
| } |
| |
| ofs << "Performing coverage: " << elapsed_time_start << std::endl; |
| this->CleanCoverageLogFiles(ofs); |
| |
| cmSystemTools::ConvertToUnixSlashes(sourceDir); |
| cmSystemTools::ConvertToUnixSlashes(binaryDir); |
| |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, "Performing coverage" << std::endl); |
| |
| cmCTestCoverageHandlerContainer cont; |
| cont.Error = error; |
| cont.SourceDir = sourceDir; |
| cont.BinaryDir = binaryDir; |
| cont.OFS = &ofs; |
| |
| // setup the regex exclude stuff |
| this->CustomCoverageExcludeRegex.clear(); |
| std::vector<cmStdString>::iterator rexIt; |
| for ( rexIt = this->CustomCoverageExclude.begin(); |
| rexIt != this->CustomCoverageExclude.end(); |
| ++ rexIt ) |
| { |
| this->CustomCoverageExcludeRegex.push_back( |
| cmsys::RegularExpression(rexIt->c_str())); |
| } |
| |
| if(this->HandleBullseyeCoverage(&cont)) |
| { |
| return cont.Error; |
| } |
| int file_count = 0; |
| file_count += this->HandleGCovCoverage(&cont); |
| if ( file_count < 0 ) |
| { |
| return error; |
| } |
| file_count += this->HandleTracePyCoverage(&cont); |
| if ( file_count < 0 ) |
| { |
| return error; |
| } |
| file_count += this->HandlePHPCoverage(&cont); |
| if ( file_count < 0 ) |
| { |
| return error; |
| } |
| error = cont.Error; |
| |
| std::set<std::string> uncovered = this->FindUncoveredFiles(&cont); |
| |
| if ( file_count == 0 ) |
| { |
| cmCTestLog(this->CTest, WARNING, |
| " Cannot find any coverage files. Ignoring Coverage request." |
| << std::endl); |
| return error; |
| } |
| cmGeneratedFileStream covSumFile; |
| cmGeneratedFileStream covLogFile; |
| |
| if(!this->StartResultingXML(cmCTest::PartCoverage, "Coverage", covSumFile)) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, |
| "Cannot open coverage summary file." << std::endl); |
| return -1; |
| } |
| |
| this->CTest->StartXML(covSumFile, this->AppendXML); |
| // Produce output xml files |
| |
| covSumFile << "<Coverage>" << std::endl |
| << "\t<StartDateTime>" << coverage_start_time << "</StartDateTime>" |
| << std::endl |
| << "\t<StartTime>" << coverage_start_time_time << "</StartTime>" |
| << std::endl; |
| int logFileCount = 0; |
| if ( !this->StartCoverageLogFile(covLogFile, logFileCount) ) |
| { |
| return -1; |
| } |
| cmCTestCoverageHandlerContainer::TotalCoverageMap::iterator fileIterator; |
| int cnt = 0; |
| long total_tested = 0; |
| long total_untested = 0; |
| //std::string fullSourceDir = sourceDir + "/"; |
| //std::string fullBinaryDir = binaryDir + "/"; |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, std::endl); |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, |
| " Accumulating results (each . represents one file):" << std::endl); |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, " "); |
| |
| std::vector<std::string> errorsWhileAccumulating; |
| |
| file_count = 0; |
| for ( fileIterator = cont.TotalCoverage.begin(); |
| fileIterator != cont.TotalCoverage.end(); |
| ++fileIterator ) |
| { |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, "." << std::flush); |
| file_count ++; |
| if ( file_count % 50 == 0 ) |
| { |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, " processed: " << file_count |
| << " out of " |
| << cont.TotalCoverage.size() << std::endl); |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, " "); |
| } |
| |
| const std::string fullFileName = fileIterator->first; |
| bool shouldIDoCoverage |
| = this->ShouldIDoCoverage(fullFileName.c_str(), |
| sourceDir.c_str(), binaryDir.c_str()); |
| if ( !shouldIDoCoverage ) |
| { |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, |
| ".NoDartCoverage found, so skip coverage check for: " |
| << fullFileName.c_str() |
| << std::endl); |
| continue; |
| } |
| |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, |
| "Process file: " << fullFileName << std::endl); |
| |
| if ( !cmSystemTools::FileExists(fullFileName.c_str()) ) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot find file: " |
| << fullFileName.c_str() << std::endl); |
| continue; |
| } |
| |
| if ( ++cnt % 100 == 0 ) |
| { |
| this->EndCoverageLogFile(covLogFile, logFileCount); |
| logFileCount ++; |
| if ( !this->StartCoverageLogFile(covLogFile, logFileCount) ) |
| { |
| return -1; |
| } |
| } |
| |
| const std::string fileName |
| = cmSystemTools::GetFilenameName(fullFileName.c_str()); |
| std::string shortFileName = |
| this->CTest->GetShortPathToFile(fullFileName.c_str()); |
| const cmCTestCoverageHandlerContainer::SingleFileCoverageVector& fcov |
| = fileIterator->second; |
| covLogFile << "\t<File Name=\"" << cmXMLSafe(fileName) |
| << "\" FullPath=\"" << cmXMLSafe(shortFileName) << "\">\n" |
| << "\t\t<Report>" << std::endl; |
| |
| std::ifstream ifs(fullFileName.c_str()); |
| if ( !ifs) |
| { |
| cmOStringStream ostr; |
| ostr << "Cannot open source file: " << fullFileName.c_str(); |
| errorsWhileAccumulating.push_back(ostr.str()); |
| error ++; |
| continue; |
| } |
| |
| int tested = 0; |
| int untested = 0; |
| |
| cmCTestCoverageHandlerContainer::SingleFileCoverageVector::size_type cc; |
| std::string line; |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, |
| "Actually performing coverage for: " << fullFileName << std::endl); |
| for ( cc= 0; cc < fcov.size(); cc ++ ) |
| { |
| if ( !cmSystemTools::GetLineFromStream(ifs, line) && |
| cc != fcov.size() -1 ) |
| { |
| cmOStringStream ostr; |
| ostr << "Problem reading source file: " << fullFileName.c_str() |
| << " line:" << cc << " out total: " << fcov.size()-1; |
| errorsWhileAccumulating.push_back(ostr.str()); |
| error ++; |
| break; |
| } |
| covLogFile << "\t\t<Line Number=\"" << cc << "\" Count=\"" << fcov[cc] |
| << "\">" |
| << cmXMLSafe(line) << "</Line>" << std::endl; |
| if ( fcov[cc] == 0 ) |
| { |
| untested ++; |
| } |
| else if ( fcov[cc] > 0 ) |
| { |
| tested ++; |
| } |
| } |
| if ( cmSystemTools::GetLineFromStream(ifs, line) ) |
| { |
| cmOStringStream ostr; |
| ostr << "Looks like there are more lines in the file: " << line; |
| 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; |
| covLogFile << "\t\t</Report>" << std::endl |
| << "\t</File>" << std::endl; |
| covSumFile << "\t<File Name=\"" << cmXMLSafe(fileName) |
| << "\" FullPath=\"" << cmXMLSafe( |
| this->CTest->GetShortPathToFile(fullFileName.c_str())) |
| << "\" Covered=\"" << (tested > 0 ? "true":"false") << "\">\n" |
| << "\t\t<LOCTested>" << tested << "</LOCTested>\n" |
| << "\t\t<LOCUnTested>" << untested << "</LOCUnTested>\n" |
| << "\t\t<PercentCoverage>"; |
| covSumFile.setf(std::ios::fixed, std::ios::floatfield); |
| covSumFile.precision(2); |
| covSumFile << (cper) << "</PercentCoverage>\n" |
| << "\t\t<CoverageMetric>"; |
| covSumFile.setf(std::ios::fixed, std::ios::floatfield); |
| covSumFile.precision(2); |
| covSumFile << (cmet) << "</CoverageMetric>\n"; |
| this->WriteXMLLabels(covSumFile, shortFileName); |
| covSumFile << "\t</File>" << std::endl; |
| } |
| |
| //Handle all the files in the extra coverage globs that have no cov data |
| for(std::set<std::string>::iterator i = uncovered.begin(); |
| i != uncovered.end(); ++i) |
| { |
| std::string fileName = cmSystemTools::GetFilenameName(*i); |
| std::string fullPath = cont.SourceDir + "/" + *i; |
| |
| covLogFile << "\t<File Name=\"" << cmXMLSafe(fileName) |
| << "\" FullPath=\"" << cmXMLSafe(*i) << "\">\n" |
| << "\t\t<Report>" << std::endl; |
| |
| std::ifstream ifs(fullPath.c_str()); |
| if (!ifs) |
| { |
| cmOStringStream ostr; |
| ostr << "Cannot open source file: " << fullPath.c_str(); |
| errorsWhileAccumulating.push_back(ostr.str()); |
| error ++; |
| continue; |
| } |
| int untested = 0; |
| std::string line; |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, |
| "Actually performing coverage for: " << i->c_str() << std::endl); |
| while (cmSystemTools::GetLineFromStream(ifs, line)) |
| { |
| covLogFile << "\t\t<Line Number=\"" << untested << "\" Count=\"0\">" |
| << cmXMLSafe(line) << "</Line>" << std::endl; |
| untested ++; |
| } |
| covLogFile << "\t\t</Report>\n\t</File>" << std::endl; |
| |
| total_untested += untested; |
| covSumFile << "\t<File Name=\"" << cmXMLSafe(fileName) |
| << "\" FullPath=\"" << cmXMLSafe(i->c_str()) |
| << "\" Covered=\"true\">\n" |
| << "\t\t<LOCTested>0</LOCTested>\n" |
| << "\t\t<LOCUnTested>" << untested << "</LOCUnTested>\n" |
| << "\t\t<PercentCoverage>0</PercentCoverage>\n" |
| << "\t\t<CoverageMetric>0</CoverageMetric>\n"; |
| this->WriteXMLLabels(covSumFile, *i); |
| covSumFile << "\t</File>" << std::endl; |
| } |
| |
| this->EndCoverageLogFile(covLogFile, logFileCount); |
| |
| if ( errorsWhileAccumulating.size() > 0 ) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, std::endl); |
| cmCTestLog(this->CTest, ERROR_MESSAGE, |
| "Error(s) while accumulating results:" << std::endl); |
| std::vector<std::string>::iterator erIt; |
| for ( erIt = errorsWhileAccumulating.begin(); |
| erIt != errorsWhileAccumulating.end(); |
| ++ erIt ) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, |
| " " << erIt->c_str() << 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(); |
| |
| covSumFile << "\t<LOCTested>" << total_tested << "</LOCTested>\n" |
| << "\t<LOCUntested>" << total_untested << "</LOCUntested>\n" |
| << "\t<LOC>" << total_lines << "</LOC>\n" |
| << "\t<PercentCoverage>"; |
| covSumFile.setf(std::ios::fixed, std::ios::floatfield); |
| covSumFile.precision(2); |
| covSumFile << (percent_coverage)<< "</PercentCoverage>\n" |
| << "\t<EndDateTime>" << end_time << "</EndDateTime>\n" |
| << "\t<EndTime>" << |
| static_cast<unsigned int>(cmSystemTools::GetTime()) |
| << "</EndTime>\n"; |
| covSumFile << "<ElapsedMinutes>" << |
| static_cast<int>((cmSystemTools::GetTime() - elapsed_time_start)/6)/10.0 |
| << "</ElapsedMinutes>" |
| << "</Coverage>" << std::endl; |
| this->CTest->EndXML(covSumFile); |
| |
| 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) |
| { |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, |
| " Add coverage exclude regular expressions." << std::endl); |
| this->CTest->PopulateCustomVector(mf, "CTEST_CUSTOM_COVERAGE_EXCLUDE", |
| this->CustomCoverageExclude); |
| this->CTest->PopulateCustomVector(mf, "CTEST_EXTRA_COVERAGE_GLOB", |
| this->ExtraCoverageGlobs); |
| std::vector<cmStdString>::iterator it; |
| for ( it = this->CustomCoverageExclude.begin(); |
| it != this->CustomCoverageExclude.end(); |
| ++ it ) |
| { |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " Add coverage exclude: " |
| << it->c_str() << std::endl); |
| } |
| for ( it = this->ExtraCoverageGlobs.begin(); |
| it != this->ExtraCoverageGlobs.end(); ++it) |
| { |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " Add coverage glob: " |
| << it->c_str() << std::endl); |
| } |
| } |
| |
| //---------------------------------------------------------------------- |
| // 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 |
| #endif |
| |
| //---------------------------------------------------------------------- |
| bool IsFileInDir(const std::string &infile, const std::string &indir) |
| { |
| std::string file = cmSystemTools::CollapseFullPath(infile.c_str()); |
| std::string dir = cmSystemTools::CollapseFullPath(indir.c_str()); |
| |
| if ( |
| file.size() > dir.size() && |
| (fnc(file.substr(0, dir.size())) == fnc(dir)) && |
| file[dir.size()] == '/' |
| ) |
| { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| //---------------------------------------------------------------------- |
| int cmCTestCoverageHandler::HandlePHPCoverage( |
| cmCTestCoverageHandlerContainer* cont) |
| { |
| cmParsePHPCoverage cov(*cont, this->CTest); |
| std::string coverageDir = this->CTest->GetBinaryDir() + "/xdebugCoverage"; |
| if(cmSystemTools::FileIsDirectory(coverageDir.c_str())) |
| { |
| cov.ReadPHPCoverageDirectory(coverageDir.c_str()); |
| } |
| return static_cast<int>(cont->TotalCoverage.size()); |
| } |
| //---------------------------------------------------------------------- |
| int cmCTestCoverageHandler::HandleGCovCoverage( |
| cmCTestCoverageHandlerContainer* cont) |
| { |
| std::string gcovCommand |
| = this->CTest->GetCTestConfiguration("CoverageCommand"); |
| |
| // 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 = "^(.*):creating [`'](.*\\.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); |
| std::vector<std::string>::iterator it; |
| |
| if ( files.size() == 0 ) |
| { |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, |
| " Cannot find any GCov coverage files." |
| << std::endl); |
| // 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"; |
| std::string currentDirectory = cmSystemTools::GetCurrentWorkingDirectory(); |
| cmSystemTools::MakeDirectory(tempDir.c_str()); |
| cmSystemTools::ChangeDirectory(tempDir.c_str()); |
| |
| int gcovStyle = 0; |
| |
| std::set<std::string> missingFiles; |
| |
| std::string actualSourceFile = ""; |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, |
| " Processing coverage (each . represents one file):" << std::endl); |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, " "); |
| int file_count = 0; |
| |
| // make sure output from gcov is in English! |
| cmSystemTools::PutEnv("LC_ALL=POSIX"); |
| |
| // 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 ( it = files.begin(); it != files.end(); ++ it ) |
| { |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, "." << std::flush); |
| |
| // Call gcov to get coverage data for this *.gcda file: |
| // |
| std::string fileDir = cmSystemTools::GetFilenamePath(it->c_str()); |
| std::string command = "\"" + gcovCommand + "\" -l -p -o \"" + fileDir |
| + "\" \"" + *it + "\""; |
| |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, command.c_str() |
| << std::endl); |
| |
| std::string output = ""; |
| std::string errors = ""; |
| int retVal = 0; |
| *cont->OFS << "* Run coverage for: " << fileDir.c_str() << std::endl; |
| *cont->OFS << " Command: " << command.c_str() << std::endl; |
| int res = this->CTest->RunCommand(command.c_str(), &output, &errors, |
| &retVal, tempDir.c_str(), 0 /*this->TimeOut*/); |
| |
| *cont->OFS << " Output: " << output.c_str() << std::endl; |
| *cont->OFS << " Errors: " << errors.c_str() << std::endl; |
| if ( ! res ) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, |
| "Problem running coverage on file: " << it->c_str() << 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: " << it->c_str() << std::endl); |
| cmCTestLog(this->CTest, ERROR_MESSAGE, |
| "Command produced error: " << cont->Error << std::endl); |
| } |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, |
| "--------------------------------------------------------------" |
| << std::endl |
| << output << std::endl |
| << "--------------------------------------------------------------" |
| << std::endl); |
| |
| std::vector<cmStdString> lines; |
| std::vector<cmStdString>::iterator line; |
| |
| cmSystemTools::Split(output.c_str(), lines); |
| |
| for ( line = lines.begin(); line != lines.end(); ++line) |
| { |
| std::string sourceFile; |
| std::string gcovFile; |
| |
| cmCTestLog(this->CTest, DEBUG, "Line: [" << line->c_str() << "]" |
| << std::endl); |
| |
| if ( line->size() == 0 ) |
| { |
| // Ignore empty line; probably style 2 |
| } |
| else if ( st1re1.find(line->c_str()) ) |
| { |
| if ( gcovStyle == 0 ) |
| { |
| gcovStyle = 1; |
| } |
| if ( gcovStyle != 1 ) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style e1" |
| << std::endl); |
| cont->Error ++; |
| break; |
| } |
| |
| actualSourceFile = ""; |
| sourceFile = st1re1.match(2); |
| } |
| else if ( st1re2.find(line->c_str() ) ) |
| { |
| 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->c_str() ) ) |
| { |
| if ( gcovStyle == 0 ) |
| { |
| gcovStyle = 2; |
| } |
| if ( gcovStyle != 2 ) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style e3" |
| << std::endl); |
| cont->Error ++; |
| break; |
| } |
| |
| actualSourceFile = ""; |
| sourceFile = st2re1.match(1); |
| } |
| else if ( st2re2.find(line->c_str() ) ) |
| { |
| 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->c_str() ) ) |
| { |
| 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->c_str() ) ) |
| { |
| if ( gcovStyle == 0 ) |
| { |
| gcovStyle = 2; |
| } |
| if ( gcovStyle != 2 ) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style e6" |
| << std::endl); |
| cont->Error ++; |
| break; |
| } |
| |
| cmCTestLog(this->CTest, WARNING, "Warning: " << st2re4.match(1) |
| << " had unexpected EOF" << std::endl); |
| } |
| else if ( st2re5.find(line->c_str() ) ) |
| { |
| if ( gcovStyle == 0 ) |
| { |
| gcovStyle = 2; |
| } |
| if ( gcovStyle != 2 ) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style e7" |
| << std::endl); |
| cont->Error ++; |
| break; |
| } |
| |
| cmCTestLog(this->CTest, WARNING, "Warning: Cannot open file: " |
| << st2re5.match(1) << std::endl); |
| } |
| else if ( st2re6.find(line->c_str() ) ) |
| { |
| if ( gcovStyle == 0 ) |
| { |
| gcovStyle = 2; |
| } |
| if ( gcovStyle != 2 ) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, "Unknown gcov output style e8" |
| << std::endl); |
| cont->Error ++; |
| break; |
| } |
| |
| cmCTestLog(this->CTest, WARNING, "Warning: File: " << st2re6.match(1) |
| << " is newer than " << st2re6.match(2) << std::endl); |
| } |
| else |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, |
| "Unknown gcov output line: [" << line->c_str() << "]" << 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]; |
| |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " in gcovFile: " |
| << gcovFile << std::endl); |
| |
| std::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.size() ) |
| { |
| 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("#") != prefix.npos ) |
| { |
| vec[lineIdx] = 0; |
| } |
| } |
| |
| vec[lineIdx] += cov; |
| } |
| } |
| } |
| |
| actualSourceFile = ""; |
| } |
| |
| |
| if ( !sourceFile.empty() && actualSourceFile.empty() ) |
| { |
| gcovFile = ""; |
| |
| // Is it in the source dir or the binary dir? |
| // |
| if ( IsFileInDir(sourceFile, cont->SourceDir) ) |
| { |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " produced s: " |
| << sourceFile.c_str() << std::endl); |
| *cont->OFS << " produced in source dir: " << sourceFile.c_str() |
| << std::endl; |
| actualSourceFile |
| = cmSystemTools::CollapseFullPath(sourceFile.c_str()); |
| } |
| else if ( IsFileInDir(sourceFile, cont->BinaryDir) ) |
| { |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " produced b: " |
| << sourceFile.c_str() << std::endl); |
| *cont->OFS << " produced in binary dir: " << sourceFile.c_str() |
| << std::endl; |
| actualSourceFile |
| = cmSystemTools::CollapseFullPath(sourceFile.c_str()); |
| } |
| |
| if ( actualSourceFile.empty() ) |
| { |
| if ( missingFiles.find(sourceFile) == missingFiles.end() ) |
| { |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, |
| "Something went wrong" << std::endl); |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, |
| "Cannot find file: [" |
| << sourceFile.c_str() << "]" << std::endl); |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, |
| " in source dir: [" |
| << cont->SourceDir.c_str() << "]" |
| << std::endl); |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, |
| " or binary dir: [" |
| << cont->BinaryDir.size() << "]" |
| << std::endl); |
| *cont->OFS << " Something went wrong. Cannot find file: " |
| << sourceFile.c_str() |
| << " in source dir: " << cont->SourceDir.c_str() |
| << " or binary dir: " << cont->BinaryDir.c_str() << std::endl; |
| |
| missingFiles.insert(sourceFile); |
| } |
| } |
| } |
| } |
| |
| file_count++; |
| |
| if ( file_count % 50 == 0 ) |
| { |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, " processed: " << file_count |
| << " out of " << files.size() << std::endl); |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, " "); |
| } |
| } |
| |
| cmSystemTools::ChangeDirectory(currentDirectory.c_str()); |
| return file_count; |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmCTestCoverageHandler::FindGCovFiles(std::vector<std::string>& files) |
| { |
| cmsys::Glob gl; |
| gl.RecurseOn(); |
| gl.RecurseThroughSymlinksOff(); |
| |
| for(LabelMapType::const_iterator lmi = this->TargetDirs.begin(); |
| lmi != this->TargetDirs.end(); ++lmi) |
| { |
| // Skip targets containing no interesting labels. |
| if(!this->IntersectsFilter(lmi->second)) |
| { |
| continue; |
| } |
| |
| // Coverage files appear next to their object files in the target |
| // support directory. |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, |
| " globbing for coverage in: " << lmi->first << std::endl); |
| std::string daGlob = lmi->first; |
| daGlob += "/*.da"; |
| gl.FindFiles(daGlob); |
| files.insert(files.end(), gl.GetFiles().begin(), gl.GetFiles().end()); |
| daGlob = lmi->first; |
| daGlob += "/*.gcda"; |
| gl.FindFiles(daGlob); |
| files.insert(files.end(), gl.GetFiles().begin(), gl.GetFiles().end()); |
| } |
| } |
| |
| //---------------------------------------------------------------------- |
| 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.size() == 0 ) |
| { |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, |
| " Cannot find any Python Trace.py coverage files." |
| << std::endl); |
| // 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"; |
| std::string currentDirectory = cmSystemTools::GetCurrentWorkingDirectory(); |
| cmSystemTools::MakeDirectory(tempDir.c_str()); |
| cmSystemTools::ChangeDirectory(tempDir.c_str()); |
| |
| cmSystemTools::ChangeDirectory(currentDirectory.c_str()); |
| |
| std::vector<std::string>::iterator fileIt; |
| int file_count = 0; |
| for ( fileIt = files.begin(); fileIt != files.end(); ++ fileIt ) |
| { |
| std::string fileName = this->FindFile(cont, *fileIt); |
| if ( fileName.empty() ) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, |
| "Cannot find source Python file corresponding to: " |
| << fileIt->c_str() << std::endl); |
| continue; |
| } |
| |
| std::string actualSourceFile |
| = cmSystemTools::CollapseFullPath(fileName.c_str()); |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, |
| " Check coverage for file: " << actualSourceFile.c_str() |
| << std::endl); |
| cmCTestCoverageHandlerContainer::SingleFileCoverageVector* vec |
| = &cont->TotalCoverage[actualSourceFile]; |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, |
| " in file: " << fileIt->c_str() << std::endl); |
| std::ifstream ifile(fileIt->c_str()); |
| if ( ! ifile ) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot open file: " |
| << fileIt->c_str() << std::endl); |
| } |
| else |
| { |
| long cnt = -1; |
| std::string nl; |
| while ( cmSystemTools::GetLineFromStream(ifile, nl) ) |
| { |
| cnt ++; |
| |
| // Skip empty lines |
| if ( !nl.size() ) |
| { |
| 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; |
| } |
| cmCTestLog(this->CTest, DEBUG, "Prefix: " << prefix.c_str() |
| << " cov: " << cov |
| << std::endl); |
| // 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; |
| } |
| cmSystemTools::ChangeDirectory(currentDirectory.c_str()); |
| return file_count; |
| } |
| |
| //---------------------------------------------------------------------- |
| std::string cmCTestCoverageHandler::FindFile( |
| cmCTestCoverageHandlerContainer* cont, |
| std::string 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.c_str()) ) |
| { |
| return fullName; |
| } |
| fullName = cont->BinaryDir + "/" + fileNameNoE + ".py"; |
| if ( cmSystemTools::FileExists(fullName.c_str()) ) |
| { |
| return fullName; |
| } |
| return ""; |
| } |
| |
| // This is a header put on each marked up source file |
| namespace |
| { |
| 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. ", |
| 0}; |
| } |
| |
| //---------------------------------------------------------------------- |
| int cmCTestCoverageHandler::RunBullseyeCoverageBranch( |
| cmCTestCoverageHandlerContainer* cont, |
| std::set<cmStdString>& 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; |
| int logFileCount = 0; |
| if ( !this->StartCoverageLogFile(covLogFile, logFileCount) ) |
| { |
| return -1; |
| } |
| // for each file run covbr on that file to get the coverage |
| // information for that file |
| std::string outputFile; |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, |
| "run covbr: " |
| << std::endl); |
| |
| if(!this->RunBullseyeCommand(cont, "covbr", 0, outputFile)) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, "error running covbr for." << "\n"); |
| return -1; |
| } |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, |
| "covbr output in " << outputFile |
| << std::endl); |
| // open the output file |
| std::ifstream fin(outputFile.c_str()); |
| if(!fin) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, |
| "Cannot open coverage file: " << |
| outputFile.c_str() << std::endl); |
| return 0; |
| } |
| std::map<cmStdString, cmStdString> 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 |
| cmStdString 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) |
| { |
| covLogFile << "\t\t</Report>" << std::endl |
| << "\t</File>" << std::endl; |
| } |
| // only allow 100 files in each log file |
| if ( count != 0 && count % 100 == 0 ) |
| { |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, |
| "start a new log file: " |
| << count |
| << std::endl); |
| this->EndCoverageLogFile(covLogFile, logFileCount); |
| logFileCount ++; |
| if ( !this->StartCoverageLogFile(covLogFile, logFileCount) ) |
| { |
| return -1; |
| } |
| count++; // move on one |
| } |
| std::map<cmStdString, cmStdString>::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++; |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, |
| "Produce coverage for file: " |
| << file.c_str() << " " << count |
| << std::endl); |
| // start the file output |
| covLogFile << "\t<File Name=\"" |
| << cmXMLSafe(i->first) |
| << "\" FullPath=\"" << cmXMLSafe( |
| this->CTest->GetShortPathToFile( |
| i->second.c_str())) << "\">" << std::endl |
| << "\t\t<Report>" << std::endl; |
| // write the bullseye header |
| line =0; |
| for(int k =0; bullseyeHelp[k] != 0; ++k) |
| { |
| covLogFile << "\t\t<Line Number=\"" << line << "\" Count=\"-1\">" |
| << cmXMLSafe(bullseyeHelp[k]) |
| << "</Line>" << std::endl; |
| 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) |
| { |
| covLogFile << "\t\t<Line Number=\"" << line << "\" Count=\"-1\">" |
| << cmXMLSafe(lineIn) |
| << "</Line>" << std::endl; |
| line++; |
| } |
| } |
| // if we ran out of lines a valid file then close that file |
| if(valid) |
| { |
| covLogFile << "\t\t</Report>" << std::endl |
| << "\t</File>" << std::endl; |
| } |
| 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.size() == 0) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot find :" << cmd << "\n"); |
| return 0; |
| } |
| if(arg) |
| { |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, |
| "Run : " << program.c_str() << " " << arg << "\n"); |
| } |
| else |
| { |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, |
| "Run : " << program.c_str() << "\n"); |
| } |
| // 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.c_str() << " " << 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; |
| if(!this->StartResultingXML(cmCTest::PartCoverage, "Coverage", covSumFile)) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, |
| "Cannot open coverage summary file." << std::endl); |
| return 0; |
| } |
| this->CTest->StartXML(covSumFile, this->AppendXML); |
| double elapsed_time_start = cmSystemTools::GetTime(); |
| std::string coverage_start_time = this->CTest->CurrentTime(); |
| covSumFile << "<Coverage>" << std::endl |
| << "\t<StartDateTime>" |
| << coverage_start_time << "</StartDateTime>" |
| << std::endl |
| << "\t<StartTime>" |
| << static_cast<unsigned int>(cmSystemTools::GetTime()) |
| << "</StartTime>" |
| << std::endl; |
| 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 |
| std::ifstream fin(outputFile.c_str()); |
| if(!fin) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, |
| "Cannot open coverage summary file: " << |
| outputFile.c_str() << std::endl); |
| return 0; |
| } |
| std::set<cmStdString> coveredFileNames; |
| while(cmSystemTools::GetLineFromStream(fin, stdline)) |
| { |
| // if we have a line of output from stdout |
| if(stdline.size()) |
| { |
| // 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.c_str())) |
| { |
| // file will be relative to the binary dir |
| file = cont->BinaryDir; |
| file += "/"; |
| file += sourceFile; |
| } |
| file = cmSystemTools::CollapseFullPath(file.c_str()); |
| bool shouldIDoCoverage |
| = this->ShouldIDoCoverage(file.c_str(), |
| cont->SourceDir.c_str(), |
| cont->BinaryDir.c_str()); |
| if ( !shouldIDoCoverage ) |
| { |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, |
| ".NoDartCoverage found, so skip coverage check for: " |
| << file.c_str() |
| << std::endl); |
| continue; |
| } |
| |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, |
| "Doing coverage for: " |
| << file.c_str() |
| << std::endl); |
| |
| 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.c_str()); |
| std::string shortFileName = |
| this->CTest->GetShortPathToFile(file.c_str()); |
| |
| float cper = static_cast<float>(percentBranch + percentFunction); |
| if(totalBranches > 0) |
| { |
| cper /= 2.0f; |
| } |
| percent_coverage += cper; |
| float cmet = static_cast<float>(percentFunction + percentBranch); |
| if(totalBranches > 0) |
| { |
| cmet /= 2.0f; |
| } |
| cmet /= 100.0f; |
| tmpLog << stdline.c_str() << "\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"; |
| covSumFile << "\t<File Name=\"" << cmXMLSafe(sourceFile) |
| << "\" FullPath=\"" << cmXMLSafe(shortFileName) |
| << "\" Covered=\"" << (cmet>0?"true":"false") << "\">\n" |
| << "\t\t<BranchesTested>" |
| << branchCovered |
| << "</BranchesTested>\n" |
| << "\t\t<BranchesUnTested>" |
| << totalBranches - branchCovered |
| << "</BranchesUnTested>\n" |
| << "\t\t<FunctionsTested>" |
| << functionsCalled |
| << "</FunctionsTested>\n" |
| << "\t\t<FunctionsUnTested>" |
| << totalFunctions - functionsCalled |
| << "</FunctionsUnTested>\n" |
| // Hack for conversion of function to loc assume a function |
| // has 100 lines of code |
| << "\t\t<LOCTested>" << functionsCalled *100 |
| << "</LOCTested>\n" |
| << "\t\t<LOCUnTested>" |
| << (totalFunctions - functionsCalled)*100 |
| << "</LOCUnTested>\n" |
| << "\t\t<PercentCoverage>"; |
| covSumFile.setf(std::ios::fixed, std::ios::floatfield); |
| covSumFile.precision(2); |
| covSumFile << (cper) << "</PercentCoverage>\n" |
| << "\t\t<CoverageMetric>"; |
| covSumFile.setf(std::ios::fixed, std::ios::floatfield); |
| covSumFile.precision(2); |
| covSumFile << (cmet) << "</CoverageMetric>\n"; |
| this->WriteXMLLabels(covSumFile, shortFileName); |
| covSumFile << "\t</File>" << std::endl; |
| } |
| } |
| std::string end_time = this->CTest->CurrentTime(); |
| covSumFile << "\t<LOCTested>" << total_tested << "</LOCTested>\n" |
| << "\t<LOCUntested>" << total_untested << "</LOCUntested>\n" |
| << "\t<LOC>" << total_functions << "</LOC>\n" |
| << "\t<PercentCoverage>"; |
| covSumFile.setf(std::ios::fixed, std::ios::floatfield); |
| covSumFile.precision(2); |
| covSumFile |
| << SAFEDIV(percent_coverage,number_files)<< "</PercentCoverage>\n" |
| << "\t<EndDateTime>" << end_time << "</EndDateTime>\n" |
| << "\t<EndTime>" << static_cast<unsigned int>(cmSystemTools::GetTime()) |
| << "</EndTime>\n"; |
| covSumFile |
| << "<ElapsedMinutes>" << |
| static_cast<int>((cmSystemTools::GetTime() - elapsed_time_start)/6)/10.0 |
| << "</ElapsedMinutes>" |
| << "</Coverage>" << std::endl; |
| this->CTest->EndXML(covSumFile); |
| |
| // Now create the coverage information for each file |
| return this->RunBullseyeCoverageBranch(cont, |
| coveredFileNames, |
| coveredFiles, |
| coveredFilesFullPath); |
| } |
| |
| //---------------------------------------------------------------------- |
| int cmCTestCoverageHandler::HandleBullseyeCoverage( |
| cmCTestCoverageHandlerContainer* cont) |
| { |
| const char* covfile = cmSystemTools::GetEnv("COVFILE"); |
| if(!covfile) |
| { |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, |
| " COVFILE environment variable not found, not running " |
| " bullseye\n"); |
| return 0; |
| } |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, |
| " run covsrc with COVFILE=[" |
| << covfile |
| << "]" << std::endl); |
| if(!this->RunBullseyeSourceSummary(cont)) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, |
| "Error running bullseye summary.\n"); |
| return 0; |
| } |
| cmCTestLog(this->CTest, DEBUG, "HandleBullseyeCoverage return 1 " |
| << std::endl); |
| 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 == inputLine.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 == inputLine.npos) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, "Error parsing string : " |
| << inputLine.c_str() << "\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 != inputLine.npos) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, "Error parsing input : " |
| << inputLine.c_str() << " last pos not npos = " << pos << |
| "\n"); |
| } |
| return true; |
| } |
| |
| //---------------------------------------------------------------------- |
| int cmCTestCoverageHandler::GetLabelId(std::string const& label) |
| { |
| LabelIdMapType::iterator i = this->LabelIdMap.find(label); |
| if(i == this->LabelIdMap.end()) |
| { |
| int n = int(this->Labels.size()); |
| this->Labels.push_back(label); |
| LabelIdMapType::value_type entry(label, n); |
| i = this->LabelIdMap.insert(entry).first; |
| } |
| return i->second; |
| } |
| |
| //---------------------------------------------------------------------- |
| void cmCTestCoverageHandler::LoadLabels() |
| { |
| std::string fileList = this->CTest->GetBinaryDir(); |
| fileList += cmake::GetCMakeFilesDirectory(); |
| fileList += "/TargetDirectories.txt"; |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, |
| " target directory list [" << fileList << "]\n"); |
| std::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"; |
| std::ifstream fin(fname.c_str()); |
| if(!fin) |
| { |
| return; |
| } |
| |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, |
| " loading labels from [" << fname << "]\n"); |
| 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; |
| } |
| else 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]; |
| for(std::vector<int>::const_iterator li = targetLabels.begin(); |
| li != targetLabels.end(); ++li) |
| { |
| labelSet.insert(*li); |
| } |
| } |
| } |
| } |
| |
| //---------------------------------------------------------------------- |
| void cmCTestCoverageHandler::WriteXMLLabels(std::ofstream& os, |
| std::string const& source) |
| { |
| LabelMapType::const_iterator li = this->SourceLabels.find(source); |
| if(li != this->SourceLabels.end() && !li->second.empty()) |
| { |
| os << "\t\t<Labels>\n"; |
| for(LabelSet::const_iterator lsi = li->second.begin(); |
| lsi != li->second.end(); ++lsi) |
| { |
| os << "\t\t\t<Label>" << cmXMLSafe(this->Labels[*lsi]) << "</Label>\n"; |
| } |
| os << "\t\t</Labels>\n"; |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| void |
| cmCTestCoverageHandler::SetLabelFilter(std::set<cmStdString> const& labels) |
| { |
| this->LabelFilter.clear(); |
| for(std::set<cmStdString>::const_iterator li = labels.begin(); |
| li != labels.end(); ++li) |
| { |
| this->LabelFilter.insert(this->GetLabelId(*li)); |
| } |
| } |
| |
| //---------------------------------------------------------------------- |
| 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; |
| cmsys_stl::set_intersection |
| (labels.begin(), labels.end(), |
| this->LabelFilter.begin(), this->LabelFilter.end(), |
| cmsys_stl::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::vector<cmStdString>::iterator i = this->ExtraCoverageGlobs.begin(); |
| i != this->ExtraCoverageGlobs.end(); ++i) |
| { |
| cmsys::Glob gl; |
| gl.RecurseOn(); |
| gl.RecurseThroughSymlinksOff(); |
| std::string glob = cont->SourceDir + "/" + *i; |
| gl.FindFiles(glob); |
| std::vector<std::string> files = gl.GetFiles(); |
| for(std::vector<std::string>::iterator f = files.begin(); |
| f != files.end(); ++f) |
| { |
| if(this->ShouldIDoCoverage(f->c_str(), |
| cont->SourceDir.c_str(), cont->BinaryDir.c_str())) |
| { |
| extraMatches.insert(this->CTest->GetShortPathToFile( |
| f->c_str())); |
| } |
| } |
| } |
| |
| if(extraMatches.size()) |
| { |
| for(cmCTestCoverageHandlerContainer::TotalCoverageMap::iterator i = |
| cont->TotalCoverage.begin(); i != cont->TotalCoverage.end(); ++i) |
| { |
| std::string shortPath = this->CTest->GetShortPathToFile( |
| i->first.c_str()); |
| extraMatches.erase(shortPath); |
| } |
| } |
| return extraMatches; |
| } |