|  | #include "cmParseCacheCoverage.h" | 
|  |  | 
|  | #include <cstdio> | 
|  | #include <cstdlib> | 
|  | #include <map> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include "cmsys/Directory.hxx" | 
|  | #include "cmsys/FStream.hxx" | 
|  |  | 
|  | #include "cmCTest.h" | 
|  | #include "cmCTestCoverageHandler.h" | 
|  | #include "cmStringAlgorithms.h" | 
|  | #include "cmSystemTools.h" | 
|  |  | 
|  | cmParseCacheCoverage::cmParseCacheCoverage( | 
|  | cmCTestCoverageHandlerContainer& cont, cmCTest* ctest) | 
|  | : cmParseMumpsCoverage(cont, ctest) | 
|  | { | 
|  | } | 
|  |  | 
|  | bool cmParseCacheCoverage::LoadCoverageData(std::string const& d) | 
|  | { | 
|  | // load all the .mcov files in the specified directory | 
|  | cmsys::Directory dir; | 
|  | if (!dir.Load(d)) { | 
|  | return false; | 
|  | } | 
|  | size_t numf; | 
|  | unsigned int i; | 
|  | numf = dir.GetNumberOfFiles(); | 
|  | for (i = 0; i < numf; i++) { | 
|  | std::string file = dir.GetFile(i); | 
|  | if (file != "." && file != ".." && !cmSystemTools::FileIsDirectory(file)) { | 
|  | std::string path = cmStrCat(d, '/', file); | 
|  | if (cmSystemTools::GetFilenameLastExtension(path) == ".cmcov") { | 
|  | if (!this->ReadCMCovFile(path.c_str())) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // not currently used, but leave it in case we want it in the future | 
|  | void cmParseCacheCoverage::RemoveUnCoveredFiles() | 
|  | { | 
|  | // loop over the coverage data computed and remove all files | 
|  | // that only have -1 or 0 for the lines. | 
|  | auto ci = this->Coverage.TotalCoverage.begin(); | 
|  | while (ci != this->Coverage.TotalCoverage.end()) { | 
|  | cmCTestCoverageHandlerContainer::SingleFileCoverageVector& v = ci->second; | 
|  | bool nothing = true; | 
|  | for (int i : v) { | 
|  | if (i > 0) { | 
|  | nothing = false; | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (nothing) { | 
|  | cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, | 
|  | "No coverage found in: " << ci->first << std::endl, | 
|  | this->Coverage.Quiet); | 
|  | this->Coverage.TotalCoverage.erase(ci++); | 
|  | } else { | 
|  | ++ci; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | bool cmParseCacheCoverage::ReadCMCovFile(const char* file) | 
|  | { | 
|  | cmsys::ifstream in(file); | 
|  | if (!in) { | 
|  | cmCTestLog(this->CTest, ERROR_MESSAGE, "Can not open : " << file << "\n"); | 
|  | return false; | 
|  | } | 
|  | std::string line; | 
|  | if (!cmSystemTools::GetLineFromStream(in, line)) { | 
|  | cmCTestLog(this->CTest, ERROR_MESSAGE, | 
|  | "Empty file : " << file | 
|  | << "  referenced in this line of cmcov data:\n" | 
|  | "[" | 
|  | << line << "]\n"); | 
|  | return false; | 
|  | } | 
|  | std::vector<std::string> separateLine = | 
|  | cmSystemTools::SplitString(line, ','); | 
|  | if (separateLine.size() != 4 || separateLine[0] != "Routine" || | 
|  | separateLine[1] != "Line" || separateLine[2] != "RtnLine" || | 
|  | separateLine[3] != "Code") { | 
|  | cmCTestLog(this->CTest, ERROR_MESSAGE, | 
|  | "Bad first line of cmcov file : " << file | 
|  | << "  line:\n" | 
|  | "[" | 
|  | << line << "]\n"); | 
|  | } | 
|  | std::string routine; | 
|  | std::string filepath; | 
|  | while (cmSystemTools::GetLineFromStream(in, line)) { | 
|  | // parse the comma separated line | 
|  | separateLine = cmSystemTools::SplitString(line, ','); | 
|  | // might have more because code could have a quoted , in it | 
|  | // but we only care about the first 3 args anyway | 
|  | if (separateLine.size() < 4) { | 
|  | cmCTestLog(this->CTest, ERROR_MESSAGE, | 
|  | "Bad line of cmcov file expected at least 4 found: " | 
|  | << separateLine.size() << " " << file | 
|  | << "  line:\n" | 
|  | "[" | 
|  | << line << "]\n"); | 
|  | for (std::string::size_type i = 0; i < separateLine.size(); ++i) { | 
|  | cmCTestLog(this->CTest, ERROR_MESSAGE, "" << separateLine[1] << " "); | 
|  | } | 
|  | cmCTestLog(this->CTest, ERROR_MESSAGE, "\n"); | 
|  | return false; | 
|  | } | 
|  | // if we do not have a routine yet, then it should be | 
|  | // the first argument in the vector | 
|  | if (routine.empty()) { | 
|  | routine = separateLine[0]; | 
|  | // Find the full path to the file | 
|  | if (!this->FindMumpsFile(routine, filepath)) { | 
|  | cmCTestLog(this->CTest, ERROR_MESSAGE, | 
|  | "Could not find mumps file for routine: " << routine | 
|  | << "\n"); | 
|  | filepath.clear(); | 
|  | continue; // move to next line | 
|  | } | 
|  | } | 
|  | // if we have a routine name, check for end of routine | 
|  | else { | 
|  | // Totals in arg 0 marks the end of a routine | 
|  | if (cmHasLiteralPrefix(separateLine[0], "Totals")) { | 
|  | routine.clear(); // at the end of this routine | 
|  | filepath.clear(); | 
|  | continue; // move to next line | 
|  | } | 
|  | } | 
|  | // if the file path was not found for the routine | 
|  | // move to next line. We should have already warned | 
|  | // after the call to FindMumpsFile that we did not find | 
|  | // it, so don't report again to cut down on output | 
|  | if (filepath.empty()) { | 
|  | continue; | 
|  | } | 
|  | // now we are ready to set the coverage from the line of data | 
|  | cmCTestCoverageHandlerContainer::SingleFileCoverageVector& coverageVector = | 
|  | this->Coverage.TotalCoverage[filepath]; | 
|  | std::string::size_type linenumber = atoi(separateLine[1].c_str()) - 1; | 
|  | int count = atoi(separateLine[2].c_str()); | 
|  | if (linenumber > coverageVector.size()) { | 
|  | cmCTestLog(this->CTest, ERROR_MESSAGE, | 
|  | "Parse error line is greater than number of lines in file: " | 
|  | << linenumber << " " << filepath << "\n"); | 
|  | continue; // skip setting count to avoid crash | 
|  | } | 
|  | // now add to count for linenumber | 
|  | // for some reason the cache coverage adds extra lines to the | 
|  | // end of the file in some cases. Since they do not exist, we will | 
|  | // mark them as non executable | 
|  | while (linenumber >= coverageVector.size()) { | 
|  | coverageVector.push_back(-1); | 
|  | } | 
|  | // Accounts for lines that were previously marked | 
|  | // as non-executable code (-1). if the parser comes back with | 
|  | // a non-zero count, increase the count by 1 to push the line | 
|  | // into the executable code set in addition to the count found. | 
|  | if (coverageVector[linenumber] == -1 && count > 0) { | 
|  | coverageVector[linenumber] += count + 1; | 
|  | } else { | 
|  | coverageVector[linenumber] += count; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } |