Adding support for the Python coverage.py tool.
This assumes that coverage.py has been run in such a way to produce its standard XML output. This uses the Cobertura schema and should be somewhat generalizable.
This commit is contained in:
parent
6ed8504ea5
commit
d0ec3a01a6
|
@ -439,6 +439,7 @@ set(CTEST_SRCS cmCTest.cxx
|
|||
CTest/cmParseCacheCoverage.cxx
|
||||
CTest/cmParseGTMCoverage.cxx
|
||||
CTest/cmParsePHPCoverage.cxx
|
||||
CTest/cmParsePythonCoverage.cxx
|
||||
CTest/cmCTestEmptyBinaryDirectoryCommand.cxx
|
||||
CTest/cmCTestGenericHandler.cxx
|
||||
CTest/cmCTestHandlerCommand.cxx
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
============================================================================*/
|
||||
#include "cmCTestCoverageHandler.h"
|
||||
#include "cmParsePHPCoverage.h"
|
||||
#include "cmParsePythonCoverage.h"
|
||||
#include "cmParseGTMCoverage.h"
|
||||
#include "cmParseCacheCoverage.h"
|
||||
#include "cmCTest.h"
|
||||
|
@ -392,6 +393,13 @@ int cmCTestCoverageHandler::ProcessHandler()
|
|||
{
|
||||
return error;
|
||||
}
|
||||
file_count += this->HandlePythonCoverage(&cont);
|
||||
error = cont.Error;
|
||||
if ( file_count < 0 )
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
file_count += this->HandleMumpsCoverage(&cont);
|
||||
error = cont.Error;
|
||||
if ( file_count < 0 )
|
||||
|
@ -761,6 +769,32 @@ int cmCTestCoverageHandler::HandlePHPCoverage(
|
|||
}
|
||||
return static_cast<int>(cont->TotalCoverage.size());
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
int cmCTestCoverageHandler::HandlePythonCoverage(
|
||||
cmCTestCoverageHandlerContainer* cont)
|
||||
{
|
||||
cmParsePythonCoverage cov(*cont, this->CTest);
|
||||
|
||||
// Assume the coverage.xml is in the source directory
|
||||
std::string coverageXMLFile = this->CTest->GetBinaryDir() + "/coverage.xml";
|
||||
|
||||
if(cmSystemTools::FileExists(coverageXMLFile.c_str()))
|
||||
{
|
||||
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
|
||||
"Parsing coverage.py XML file: " << coverageXMLFile
|
||||
<< std::endl);
|
||||
cov.ReadCoverageXML(coverageXMLFile.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
|
||||
"Cannot find coverage.py XML file: " << coverageXMLFile
|
||||
<< std::endl);
|
||||
}
|
||||
return static_cast<int>(cont->TotalCoverage.size());
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
int cmCTestCoverageHandler::HandleMumpsCoverage(
|
||||
cmCTestCoverageHandlerContainer* cont)
|
||||
|
|
|
@ -70,6 +70,10 @@ private:
|
|||
|
||||
//! Handle coverage using xdebug php coverage
|
||||
int HandlePHPCoverage(cmCTestCoverageHandlerContainer* cont);
|
||||
|
||||
//! Handle coverage for Python with coverage.py
|
||||
int HandlePythonCoverage(cmCTestCoverageHandlerContainer* cont);
|
||||
|
||||
//! Handle coverage for mumps
|
||||
int HandleMumpsCoverage(cmCTestCoverageHandlerContainer* cont);
|
||||
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
#include "cmStandardIncludes.h"
|
||||
#include "cmSystemTools.h"
|
||||
#include "cmXMLParser.h"
|
||||
#include "cmParsePythonCoverage.h"
|
||||
#include <cmsys/Directory.hxx>
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
class cmParsePythonCoverage::XMLParser: public cmXMLParser
|
||||
{
|
||||
public:
|
||||
XMLParser(cmCTest* ctest, cmCTestCoverageHandlerContainer& cont)
|
||||
: CTest(ctest), Coverage(cont)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~XMLParser()
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
virtual void StartElement(const char* name, const char** atts)
|
||||
{
|
||||
if(strcmp(name, "class") == 0)
|
||||
{
|
||||
int tagCount = 0;
|
||||
while(true)
|
||||
{
|
||||
if(strcmp(atts[tagCount], "filename") == 0)
|
||||
{
|
||||
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Reading file: "
|
||||
<< atts[tagCount+1] << std::endl);
|
||||
this->CurFileName = this->Coverage.SourceDir + "/" +
|
||||
atts[tagCount+1];
|
||||
FileLinesType& curFileLines =
|
||||
this->Coverage.TotalCoverage[this->CurFileName];
|
||||
std::ifstream fin(this->CurFileName.c_str());
|
||||
if(!fin)
|
||||
{
|
||||
cmCTestLog(this->CTest, ERROR_MESSAGE,
|
||||
"Python Coverage: Error opening " << this->CurFileName
|
||||
<< std::endl);
|
||||
this->Coverage.Error++;
|
||||
break;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
curFileLines.push_back(-1);
|
||||
while(cmSystemTools::GetLineFromStream(fin, line))
|
||||
{
|
||||
curFileLines.push_back(-1);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
++tagCount;
|
||||
}
|
||||
}
|
||||
else if(strcmp(name, "line") == 0)
|
||||
{
|
||||
int tagCount = 0;
|
||||
int curNumber = -1;
|
||||
int curHits = -1;
|
||||
while(true)
|
||||
{
|
||||
if(strcmp(atts[tagCount], "hits") == 0)
|
||||
{
|
||||
curHits = atoi(atts[tagCount+1]);
|
||||
}
|
||||
else if(strcmp(atts[tagCount], "number") == 0)
|
||||
{
|
||||
curNumber = atoi(atts[tagCount+1]);
|
||||
}
|
||||
|
||||
if(curHits > -1 && curNumber > -1)
|
||||
{
|
||||
FileLinesType& curFileLines =
|
||||
this->Coverage.TotalCoverage[this->CurFileName];
|
||||
curFileLines[curNumber] = curHits;
|
||||
break;
|
||||
}
|
||||
++tagCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void EndElement(const char*) {}
|
||||
|
||||
private:
|
||||
|
||||
typedef cmCTestCoverageHandlerContainer::SingleFileCoverageVector
|
||||
FileLinesType;
|
||||
cmCTest* CTest;
|
||||
cmCTestCoverageHandlerContainer& Coverage;
|
||||
std::string CurFileName;
|
||||
|
||||
};
|
||||
|
||||
|
||||
cmParsePythonCoverage::cmParsePythonCoverage(
|
||||
cmCTestCoverageHandlerContainer& cont,
|
||||
cmCTest* ctest)
|
||||
:Coverage(cont), CTest(ctest)
|
||||
{
|
||||
}
|
||||
|
||||
bool cmParsePythonCoverage::ReadCoverageXML(const char* xmlFile)
|
||||
{
|
||||
cmParsePythonCoverage::XMLParser parser(this->CTest, this->Coverage);
|
||||
parser.ParseFile(xmlFile);
|
||||
return true;
|
||||
}
|
|
@ -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 cmParsePythonCoverage_h
|
||||
#define cmParsePythonCoverage_h
|
||||
|
||||
#include "cmStandardIncludes.h"
|
||||
#include "cmCTestCoverageHandler.h"
|
||||
|
||||
/** \class cmParsePythonCoverage
|
||||
* \brief Parse coverage.py Python coverage information
|
||||
*
|
||||
* This class is used to parse the output of the coverage.py tool that
|
||||
* is currently maintained by Ned Batchelder. That tool has a command
|
||||
* that produces xml output in the format typically output by the common
|
||||
* Java-based Cobertura coverage application. This helper class parses
|
||||
* that XML file to fill the coverage-handler container.
|
||||
*/
|
||||
class cmParsePythonCoverage
|
||||
{
|
||||
public:
|
||||
|
||||
//! Create the coverage parser by passing in the coverage handler
|
||||
//! container and the cmCTest object
|
||||
cmParsePythonCoverage(cmCTestCoverageHandlerContainer& cont,
|
||||
cmCTest* ctest);
|
||||
|
||||
//! Read the XML produced by running `coverage xml`
|
||||
bool ReadCoverageXML(const char* xmlFile);
|
||||
|
||||
private:
|
||||
|
||||
class XMLParser;
|
||||
cmCTestCoverageHandlerContainer& Coverage;
|
||||
cmCTest* CTest;
|
||||
std::string CurFileName;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1959,6 +1959,25 @@ ${CMake_BINARY_DIR}/bin/cmake -DVERSION=master -P ${CMake_SOURCE_DIR}/Utilities/
|
|||
PASS_REGULAR_EXPRESSION
|
||||
"Process file.*XINDEX.m.*Total LOC:.*125.*Percentage Coverage: 85.60.*"
|
||||
ENVIRONMENT COVFILE=)
|
||||
|
||||
# Adding a test case for Python Coverage
|
||||
configure_file(
|
||||
"${CMake_SOURCE_DIR}/Tests/PythonCoverage/coverage.xml.in"
|
||||
"${CMake_BINARY_DIR}/Testing/PythonCoverage/coverage.xml")
|
||||
configure_file(
|
||||
"${CMake_SOURCE_DIR}/Tests/PythonCoverage/DartConfiguration.tcl.in"
|
||||
"${CMake_BINARY_DIR}/Testing/PythonCoverage/DartConfiguration.tcl")
|
||||
file(COPY "${CMake_SOURCE_DIR}/Tests/PythonCoverage/coveragetest"
|
||||
DESTINATION "${CMake_BINARY_DIR}/Testing/PythonCoverage")
|
||||
add_test(NAME CTestPythonCoverage
|
||||
COMMAND cmake -E chdir
|
||||
${CMake_BINARY_DIR}/Testing/PythonCoverage
|
||||
$<TARGET_FILE:ctest> -T Coverage --debug)
|
||||
set_tests_properties(CTestPythonCoverage PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION
|
||||
"Process file.*foo.py.*Total LOC:.*13.*Percentage Coverage: 84.62.*"
|
||||
ENVIRONMENT COVFILE=)
|
||||
|
||||
# Use macro, not function so that build can still be driven by CMake 2.4.
|
||||
# After 2.6 is required, this could be a function without the extra 'set'
|
||||
# calls.
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
# This file is configured by CMake automatically as DartConfiguration.tcl
|
||||
# If you choose not to use CMake, this file may be hand configured, by
|
||||
# filling in the required variables.
|
||||
|
||||
|
||||
# Configuration directories and files
|
||||
SourceDirectory: ${CMake_BINARY_DIR}/Testing/PythonCoverage/coveragetest
|
||||
BuildDirectory: ${CMake_BINARY_DIR}/Testing/PythonCoverage
|
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" ?>
|
||||
<!DOCTYPE coverage
|
||||
SYSTEM 'http://cobertura.sourceforge.net/xml/coverage-03.dtd'>
|
||||
<coverage branch-rate="0" line-rate="0.8462" timestamp="1380469411433" version="3.6">
|
||||
<!-- Generated by coverage.py: http://nedbatchelder.com/code/coverage -->
|
||||
<packages>
|
||||
<package branch-rate="0" complexity="0" line-rate="0.8462" name="">
|
||||
<classes>
|
||||
<class branch-rate="0" complexity="0" filename="foo.py" line-rate="0.6667" name="foo">
|
||||
<methods/>
|
||||
<lines>
|
||||
<line hits="1" number="2"/>
|
||||
<line hits="1" number="3"/>
|
||||
<line hits="1" number="4"/>
|
||||
<line hits="1" number="6"/>
|
||||
<line hits="0" number="7"/>
|
||||
<line hits="0" number="8"/>
|
||||
</lines>
|
||||
</class>
|
||||
<class branch-rate="0" complexity="0" filename="test_foo.py" line-rate="1" name="test_foo">
|
||||
<methods/>
|
||||
<lines>
|
||||
<line hits="1" number="2"/>
|
||||
<line hits="1" number="3"/>
|
||||
<line hits="1" number="5"/>
|
||||
<line hits="1" number="7"/>
|
||||
<line hits="1" number="8"/>
|
||||
<line hits="1" number="10"/>
|
||||
<line hits="1" number="11"/>
|
||||
</lines>
|
||||
</class>
|
||||
</classes>
|
||||
</package>
|
||||
</packages>
|
||||
</coverage>
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
def foo():
|
||||
x = 3 + 3
|
||||
return x
|
||||
|
||||
def bar():
|
||||
y = 2 + 2
|
||||
return y
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
import foo
|
||||
import unittest
|
||||
|
||||
class TestFoo(unittest.TestCase):
|
||||
|
||||
def testFoo(self):
|
||||
self.assertEquals(foo.foo(), 6, 'foo() == 6')
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
Loading…
Reference in New Issue