| #include "cmStandardIncludes.h" |
| #include "cmSystemTools.h" |
| #include "cmParsePHPCoverage.h" |
| #include <cmsys/Directory.hxx> |
| |
| /* |
| To setup coverage for php. |
| |
| - edit php.ini to add auto prepend and append php files from phpunit |
| auto_prepend_file = |
| auto_append_file = |
| - run the tests |
| - run this program on all the files in c:/tmp |
| |
| */ |
| |
| cmParsePHPCoverage::cmParsePHPCoverage(cmCTestCoverageHandlerContainer& cont, |
| cmCTest* ctest) |
| :Coverage(cont), CTest(ctest) |
| { |
| } |
| |
| bool cmParsePHPCoverage::ReadUntil(std::ifstream& in, char until) |
| { |
| char c = 0; |
| while(in.get(c) && c != until) |
| { |
| } |
| if(c != until) |
| { |
| return false; |
| } |
| return true; |
| } |
| bool cmParsePHPCoverage::ReadCoverageArray(std::ifstream& in, |
| cmStdString const& fileName) |
| { |
| cmCTestCoverageHandlerContainer::SingleFileCoverageVector& coverageVector |
| = this->Coverage.TotalCoverage[fileName]; |
| |
| char c; |
| char buf[4]; |
| in.read(buf, 3); |
| buf[3] = 0; |
| if(strcmp(buf, ";a:") != 0) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, |
| "failed to read start of coverage array, found : " |
| << buf << "\n"); |
| return false; |
| } |
| int size = 0; |
| if(!this->ReadInt(in, size)) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, |
| "failed to read size "); |
| return false; |
| } |
| if(!in.get(c) && c == '{') |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, |
| "failed to read open {\n"); |
| return false; |
| } |
| for(int i =0; i < size; i++) |
| { |
| this->ReadUntil(in, ':'); |
| int line = 0; |
| this->ReadInt(in, line); |
| // ok xdebug may have a bug here |
| // it seems to be 1 based but often times |
| // seems to have a 0'th line. |
| line--; |
| if(line < 0) |
| { |
| line = 0; |
| } |
| this->ReadUntil(in, ':'); |
| int value = 0; |
| this->ReadInt(in, value); |
| // make sure the vector is the right size and is |
| // initialized with -1 for each line |
| while(coverageVector.size() <= static_cast<size_t>(line) ) |
| { |
| coverageVector.push_back(-1); |
| } |
| // if value is less than 0, set it to zero |
| // TODO figure out the difference between |
| // -1 and -2 in xdebug coverage?? For now |
| // assume less than 0 is just not covered |
| // CDash expects -1 for non executable code (like comments) |
| // and 0 for uncovered code, and a positive value |
| // for number of times a line was executed |
| if(value < 0) |
| { |
| value = 0; |
| } |
| // if unset then set it to value |
| if(coverageVector[line] == -1) |
| { |
| coverageVector[line] = value; |
| } |
| // otherwise increment by value |
| else |
| { |
| coverageVector[line] += value; |
| } |
| } |
| return true; |
| } |
| |
| bool cmParsePHPCoverage::ReadInt(std::ifstream& in, int& v) |
| { |
| std::string s; |
| char c = 0; |
| while(in.get(c) && c != ':' && c != ';') |
| { |
| s += c; |
| } |
| v = atoi(s.c_str()); |
| return true; |
| } |
| |
| bool cmParsePHPCoverage::ReadArraySize(std::ifstream& in, int& size) |
| { |
| char c = 0; |
| in.get(c); |
| if(c != 'a') |
| { |
| return false; |
| } |
| if(in.get(c) && c == ':') |
| { |
| if(this->ReadInt(in, size)) |
| { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool cmParsePHPCoverage::ReadFileInformation(std::ifstream& in) |
| { |
| char buf[4]; |
| in.read(buf, 2); |
| buf[2] = 0; |
| if(strcmp(buf, "s:") != 0) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, |
| "failed to read start of file info found: [" << buf << "]\n"); |
| return false; |
| } |
| char c; |
| int size = 0; |
| if(this->ReadInt(in, size)) |
| { |
| size++; // add one for null termination |
| char* s = new char[size+1]; |
| // read open quote |
| if(in.get(c) && c != '"') |
| { |
| delete[] s; |
| return false; |
| } |
| // read the string data |
| in.read(s, size-1); |
| s[size-1] = 0; |
| cmStdString fileName = s; |
| delete [] s; |
| // read close quote |
| if(in.get(c) && c != '"') |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, |
| "failed to read close quote\n" |
| << "read [" << c << "]\n"); |
| return false; |
| } |
| if(!this->ReadCoverageArray(in, fileName) ) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, |
| "failed to read coverage array for file: " |
| << fileName << "\n"); |
| return false; |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| |
| bool cmParsePHPCoverage::ReadPHPData(const char* file) |
| { |
| std::ifstream in(file); |
| if(!in) |
| { |
| return false; |
| } |
| int size = 0; |
| this->ReadArraySize(in, size); |
| char c = 0; |
| in.get(c); |
| if(c != '{') |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, |
| "failed to read open array\n"); |
| return false; |
| } |
| for(int i =0; i < size; i++) |
| { |
| if(!this->ReadFileInformation(in)) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, |
| "Failed to read file #" << i << "\n"); |
| return false; |
| } |
| in.get(c); |
| if(c != '}') |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, |
| "failed to read close array\n"); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool cmParsePHPCoverage::ReadPHPCoverageDirectory(const char* d) |
| { |
| 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.c_str())) |
| { |
| std::string path = d; |
| path += "/"; |
| path += file; |
| if(!this->ReadPHPData(path.c_str())) |
| { |
| return false; |
| } |
| } |
| } |
| return true; |
| } |