| #include "cmStandardIncludes.h" |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include "cmSystemTools.h" |
| #include "cmParseCacheCoverage.h" |
| #include <cmsys/Directory.hxx> |
| #include <cmsys/Glob.hxx> |
| #include <cmsys/FStream.hxx> |
| |
| |
| cmParseCacheCoverage::cmParseCacheCoverage( |
| cmCTestCoverageHandlerContainer& cont, |
| cmCTest* ctest) |
| :cmParseMumpsCoverage(cont, ctest) |
| { |
| } |
| |
| |
| bool cmParseCacheCoverage::LoadCoverageData(const char* 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 = d; |
| path += "/"; |
| path += 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. |
| cmCTestCoverageHandlerContainer::TotalCoverageMap::iterator ci = |
| this->Coverage.TotalCoverage.begin(); |
| while(ci != this->Coverage.TotalCoverage.end()) |
| { |
| cmCTestCoverageHandlerContainer::SingleFileCoverageVector& v = |
| ci->second; |
| bool nothing = true; |
| for(cmCTestCoverageHandlerContainer::SingleFileCoverageVector::iterator i= |
| v.begin(); i != v.end(); ++i) |
| { |
| 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::SplitString(std::vector<std::string>& args, |
| std::string const& line) |
| { |
| std::string::size_type pos1 = 0; |
| std::string::size_type pos2 = line.find(',', 0); |
| if(pos2 == std::string::npos) |
| { |
| return false; |
| } |
| std::string arg; |
| while(pos2 != std::string::npos) |
| { |
| arg = line.substr(pos1, pos2-pos1); |
| args.push_back(arg); |
| pos1 = pos2+1; |
| pos2 = line.find(',',pos1); |
| } |
| arg = line.substr(pos1); |
| args.push_back(arg); |
| return true; |
| } |
| |
| 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; |
| std::vector<std::string> separateLine; |
| 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; |
| } |
| separateLine.clear(); |
| this->SplitString(separateLine, 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)) |
| { |
| // clear out line argument vector |
| separateLine.clear(); |
| // parse the comma separated line |
| this->SplitString(separateLine, 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 = ""; |
| 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(separateLine[0].substr(0, 6) == "Totals") |
| { |
| routine = ""; // at the end of this routine |
| filepath = ""; |
| 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; |
| } |