Sfoglia il codice sorgente

Add php coverage to ctest.

Bill Hoffman 15 anni fa
parent
commit
1d4e121d9c

+ 1 - 1
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)
-

+ 19 - 15
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<int> SingleFileCoverageVector;
-  typedef std::map<std::string, SingleFileCoverageVector> 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<int>(cont->TotalCoverage.size());
+}
 //----------------------------------------------------------------------
 int cmCTestCoverageHandler::HandleGCovCoverage(
   cmCTestCoverageHandlerContainer* cont)

+ 14 - 46
Source/CTest/cmCTestCoverageHandler.h

@@ -20,8 +20,17 @@
 #include <cmsys/RegularExpression.hxx>
 
 class cmGeneratedFileStream;
-class cmCTestCoverageHandlerContainer;
-
+class cmCTestCoverageHandlerContainer
+{
+public:
+  int Error;
+  std::string SourceDir;
+  std::string BinaryDir;
+  typedef std::vector<int> SingleFileCoverageVector;
+  typedef std::map<std::string, SingleFileCoverageVector> 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<std::string>& 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<std::string> 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<int> Lines;
-    bool             Show;
-    };
-
   std::vector<cmStdString> CustomCoverageExclude;
   std::vector<cmsys::RegularExpression> CustomCoverageExcludeRegex;
   std::vector<cmStdString> ExtraCoverageGlobs;
 
-  typedef std::map<std::string, cmCTestCoverage> CoverageMap;
 
   // Map from source file to label ids.
   class LabelSet: public std::set<int> {};

+ 252 - 0
Source/CTest/cmParsePHPCoverage.cxx

@@ -0,0 +1,252 @@
+#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 != '"')
+      {
+      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;
+}

+ 48 - 0
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<int, int> FileLineCoverage;
+  std::map<cmStdString, FileLineCoverage> FileToCoverage;
+  std::map<int, int> FileCoverage;
+  cmCTestCoverageHandlerContainer& Coverage;
+  cmCTest* CTest;
+};
+
+
+#endif