From 1d4e121d9cdb0b250c49a9d0d8d5f726ed7c69d4 Mon Sep 17 00:00:00 2001 From: Bill Hoffman Date: Tue, 25 May 2010 09:23:25 -0400 Subject: [PATCH] Add php coverage to ctest. --- Source/CMakeLists.txt | 2 +- Source/CTest/cmCTestCoverageHandler.cxx | 34 ++-- Source/CTest/cmCTestCoverageHandler.h | 60 ++---- Source/CTest/cmParsePHPCoverage.cxx | 252 ++++++++++++++++++++++++ Source/CTest/cmParsePHPCoverage.h | 48 +++++ 5 files changed, 334 insertions(+), 62 deletions(-) create mode 100644 Source/CTest/cmParsePHPCoverage.cxx create mode 100644 Source/CTest/cmParsePHPCoverage.h diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 49cbda74f..dc73cecd2 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -366,6 +366,7 @@ SET(CTEST_SRCS cmCTest.cxx CTest/cmCTestConfigureHandler.cxx CTest/cmCTestCoverageCommand.cxx CTest/cmCTestCoverageHandler.cxx + CTest/cmParsePHPCoverage.cxx CTest/cmCTestEmptyBinaryDirectoryCommand.cxx CTest/cmCTestGenericHandler.cxx CTest/cmCTestHandlerCommand.cxx @@ -502,4 +503,3 @@ IF(APPLE) ENDIF(APPLE) INSTALL_FILES(${CMAKE_DATA_DIR}/include cmCPluginAPI.h) - diff --git a/Source/CTest/cmCTestCoverageHandler.cxx b/Source/CTest/cmCTestCoverageHandler.cxx index da5aed0e7..3235bfdce 100644 --- a/Source/CTest/cmCTestCoverageHandler.cxx +++ b/Source/CTest/cmCTestCoverageHandler.cxx @@ -10,6 +10,7 @@ See the License for more information. ============================================================================*/ #include "cmCTestCoverageHandler.h" +#include "cmParsePHPCoverage.h" #include "cmCTest.h" #include "cmake.h" #include "cmMakefile.h" @@ -125,20 +126,6 @@ private: }; -//---------------------------------------------------------------------- -//********************************************************************** -class cmCTestCoverageHandlerContainer -{ -public: - int Error; - std::string SourceDir; - std::string BinaryDir; - typedef std::vector SingleFileCoverageVector; - typedef std::map TotalCoverageMap; - TotalCoverageMap TotalCoverage; - std::ostream* OFS; -}; -//********************************************************************** //---------------------------------------------------------------------- //---------------------------------------------------------------------- @@ -391,6 +378,11 @@ int cmCTestCoverageHandler::ProcessHandler() return error; } file_count += this->HandleTracePyCoverage(&cont); + if ( file_count < 0 ) + { + return error; + } + file_count += this->HandlePHPCoverage(&cont); if ( file_count < 0 ) { return error; @@ -524,7 +516,7 @@ int cmCTestCoverageHandler::ProcessHandler() { cmOStringStream ostr; ostr << "Problem reading source file: " << fullFileName.c_str() - << " line:" << cc; + << " line:" << cc << " out total: " << fcov.size()-1; errorsWhileAccumulating.push_back(ostr.str()); error ++; break; @@ -747,6 +739,18 @@ bool IsFileInDir(const std::string &infile, const std::string &indir) 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(cont->TotalCoverage.size()); +} //---------------------------------------------------------------------- int cmCTestCoverageHandler::HandleGCovCoverage( cmCTestCoverageHandlerContainer* cont) diff --git a/Source/CTest/cmCTestCoverageHandler.h b/Source/CTest/cmCTestCoverageHandler.h index f0564e81e..d3e8503e1 100644 --- a/Source/CTest/cmCTestCoverageHandler.h +++ b/Source/CTest/cmCTestCoverageHandler.h @@ -20,8 +20,17 @@ #include class cmGeneratedFileStream; -class cmCTestCoverageHandlerContainer; - +class cmCTestCoverageHandlerContainer +{ +public: + int Error; + std::string SourceDir; + std::string BinaryDir; + typedef std::vector SingleFileCoverageVector; + typedef std::map TotalCoverageMap; + TotalCoverageMap TotalCoverage; + std::ostream* OFS; +}; /** \class cmCTestCoverageHandler * \brief A class that handles coverage computaiton for ctest * @@ -59,6 +68,9 @@ private: int HandleGCovCoverage(cmCTestCoverageHandlerContainer* cont); void FindGCovFiles(std::vector& files); + //! Handle coverage using xdebug php coverage + int HandlePHPCoverage(cmCTestCoverageHandlerContainer* cont); + //! Handle coverage using Bullseye int HandleBullseyeCoverage(cmCTestCoverageHandlerContainer* cont); int RunBullseyeSourceSummary(cmCTestCoverageHandlerContainer* cont); @@ -94,54 +106,10 @@ private: std::set FindUncoveredFiles( cmCTestCoverageHandlerContainer* cont); - - struct cmCTestCoverage - { - cmCTestCoverage() - { - this->AbsolutePath = ""; - this->FullPath = ""; - this->Covered = false; - this->Tested = 0; - this->UnTested = 0; - this->Lines.clear(); - this->Show = false; - } - cmCTestCoverage(const cmCTestCoverage& rhs) : - AbsolutePath(rhs.AbsolutePath), - FullPath(rhs.FullPath), - Covered(rhs.Covered), - Tested(rhs.Tested), - UnTested(rhs.UnTested), - Lines(rhs.Lines), - Show(rhs.Show) - { - } - cmCTestCoverage& operator=(const cmCTestCoverage& rhs) - { - this->AbsolutePath = rhs.AbsolutePath; - this->FullPath = rhs.FullPath; - this->Covered = rhs.Covered; - this->Tested = rhs.Tested; - this->UnTested = rhs.UnTested; - this->Lines = rhs.Lines; - this->Show = rhs.Show; - return *this; - } - std::string AbsolutePath; - std::string FullPath; - bool Covered; - int Tested; - int UnTested; - std::vector Lines; - bool Show; - }; - std::vector CustomCoverageExclude; std::vector CustomCoverageExcludeRegex; std::vector ExtraCoverageGlobs; - typedef std::map CoverageMap; // Map from source file to label ids. class LabelSet: public std::set {}; diff --git a/Source/CTest/cmParsePHPCoverage.cxx b/Source/CTest/cmParsePHPCoverage.cxx new file mode 100644 index 000000000..32c1ec110 --- /dev/null +++ b/Source/CTest/cmParsePHPCoverage.cxx @@ -0,0 +1,252 @@ +#include "cmStandardIncludes.h" +#include "cmSystemTools.h" +#include "cmParsePHPCoverage.h" +#include + +/* + 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(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 != '"') + { + 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; +} diff --git a/Source/CTest/cmParsePHPCoverage.h b/Source/CTest/cmParsePHPCoverage.h new file mode 100644 index 000000000..ce5741d0b --- /dev/null +++ b/Source/CTest/cmParsePHPCoverage.h @@ -0,0 +1,48 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + 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. +============================================================================*/ + +#ifndef cmParsePHPCoverage_h +#define cmParsePHPCoverage_h + +#include "cmStandardIncludes.h" +#include "cmCTestCoverageHandler.h" + +/** \class cmParsePHPCoverage + * \brief Parse xdebug PHP coverage information + * + * This class is used to parse php coverage information produced + * by xdebug. The data is stored as a php dump of the array + * return by xdebug coverage. It is an array of arrays. + */ +class cmParsePHPCoverage +{ +public: + cmParsePHPCoverage(cmCTestCoverageHandlerContainer& cont, + cmCTest* ctest); + bool ReadPHPCoverageDirectory(const char* dir); + void PrintCoverage(); +private: + bool ReadPHPData(const char* file); + bool ReadArraySize(std::ifstream& in, int& size); + bool ReadFileInformation(std::ifstream& in); + bool ReadInt(std::ifstream& in, int& v); + bool ReadCoverageArray(std::ifstream& in, cmStdString const&); + bool ReadUntil(std::ifstream& in, char until); + typedef std::map FileLineCoverage; + std::map FileToCoverage; + std::map FileCoverage; + cmCTestCoverageHandlerContainer& Coverage; + cmCTest* CTest; +}; + + +#endif