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/cmParseCacheCoverage.cxx
|
||||||
CTest/cmParseGTMCoverage.cxx
|
CTest/cmParseGTMCoverage.cxx
|
||||||
CTest/cmParsePHPCoverage.cxx
|
CTest/cmParsePHPCoverage.cxx
|
||||||
|
CTest/cmParsePythonCoverage.cxx
|
||||||
CTest/cmCTestEmptyBinaryDirectoryCommand.cxx
|
CTest/cmCTestEmptyBinaryDirectoryCommand.cxx
|
||||||
CTest/cmCTestGenericHandler.cxx
|
CTest/cmCTestGenericHandler.cxx
|
||||||
CTest/cmCTestHandlerCommand.cxx
|
CTest/cmCTestHandlerCommand.cxx
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
============================================================================*/
|
============================================================================*/
|
||||||
#include "cmCTestCoverageHandler.h"
|
#include "cmCTestCoverageHandler.h"
|
||||||
#include "cmParsePHPCoverage.h"
|
#include "cmParsePHPCoverage.h"
|
||||||
|
#include "cmParsePythonCoverage.h"
|
||||||
#include "cmParseGTMCoverage.h"
|
#include "cmParseGTMCoverage.h"
|
||||||
#include "cmParseCacheCoverage.h"
|
#include "cmParseCacheCoverage.h"
|
||||||
#include "cmCTest.h"
|
#include "cmCTest.h"
|
||||||
|
@ -392,6 +393,13 @@ int cmCTestCoverageHandler::ProcessHandler()
|
||||||
{
|
{
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
file_count += this->HandlePythonCoverage(&cont);
|
||||||
|
error = cont.Error;
|
||||||
|
if ( file_count < 0 )
|
||||||
|
{
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
file_count += this->HandleMumpsCoverage(&cont);
|
file_count += this->HandleMumpsCoverage(&cont);
|
||||||
error = cont.Error;
|
error = cont.Error;
|
||||||
if ( file_count < 0 )
|
if ( file_count < 0 )
|
||||||
|
@ -761,6 +769,32 @@ int cmCTestCoverageHandler::HandlePHPCoverage(
|
||||||
}
|
}
|
||||||
return static_cast<int>(cont->TotalCoverage.size());
|
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(
|
int cmCTestCoverageHandler::HandleMumpsCoverage(
|
||||||
cmCTestCoverageHandlerContainer* cont)
|
cmCTestCoverageHandlerContainer* cont)
|
||||||
|
|
|
@ -70,6 +70,10 @@ private:
|
||||||
|
|
||||||
//! Handle coverage using xdebug php coverage
|
//! Handle coverage using xdebug php coverage
|
||||||
int HandlePHPCoverage(cmCTestCoverageHandlerContainer* cont);
|
int HandlePHPCoverage(cmCTestCoverageHandlerContainer* cont);
|
||||||
|
|
||||||
|
//! Handle coverage for Python with coverage.py
|
||||||
|
int HandlePythonCoverage(cmCTestCoverageHandlerContainer* cont);
|
||||||
|
|
||||||
//! Handle coverage for mumps
|
//! Handle coverage for mumps
|
||||||
int HandleMumpsCoverage(cmCTestCoverageHandlerContainer* cont);
|
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
|
PASS_REGULAR_EXPRESSION
|
||||||
"Process file.*XINDEX.m.*Total LOC:.*125.*Percentage Coverage: 85.60.*"
|
"Process file.*XINDEX.m.*Total LOC:.*125.*Percentage Coverage: 85.60.*"
|
||||||
ENVIRONMENT COVFILE=)
|
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.
|
# 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'
|
# After 2.6 is required, this could be a function without the extra 'set'
|
||||||
# calls.
|
# 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