summaryrefslogtreecommitdiffstats
path: root/Source/CTest
diff options
context:
space:
mode:
Diffstat (limited to 'Source/CTest')
-rw-r--r--Source/CTest/cmCTestCoverageHandler.cxx34
-rw-r--r--Source/CTest/cmCTestCoverageHandler.h4
-rw-r--r--Source/CTest/cmCTestTestHandler.cxx140
-rw-r--r--Source/CTest/cmCTestTestHandler.h15
-rw-r--r--Source/CTest/cmParsePythonCoverage.cxx113
-rw-r--r--Source/CTest/cmParsePythonCoverage.h48
6 files changed, 350 insertions, 4 deletions
diff --git a/Source/CTest/cmCTestCoverageHandler.cxx b/Source/CTest/cmCTestCoverageHandler.cxx
index 20aded2..ef071b9 100644
--- a/Source/CTest/cmCTestCoverageHandler.cxx
+++ b/Source/CTest/cmCTestCoverageHandler.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)
diff --git a/Source/CTest/cmCTestCoverageHandler.h b/Source/CTest/cmCTestCoverageHandler.h
index 92b0b22..3506928 100644
--- a/Source/CTest/cmCTestCoverageHandler.h
+++ b/Source/CTest/cmCTestCoverageHandler.h
@@ -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);
diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx
index 497774d..09d343d 100644
--- a/Source/CTest/cmCTestTestHandler.cxx
+++ b/Source/CTest/cmCTestTestHandler.cxx
@@ -20,6 +20,7 @@
#include <cmsys/Process.h>
#include <cmsys/RegularExpression.hxx>
#include <cmsys/Base64.h>
+#include <cmsys/Directory.hxx>
#include "cmMakefile.h"
#include "cmGlobalGenerator.h"
#include "cmLocalGenerator.h"
@@ -249,7 +250,7 @@ public:
/**
* The name of the command as specified in CMakeList.txt.
*/
- virtual const char* GetName() const { return "ADD_TEST";}
+ virtual const char* GetName() const { return "add_test";}
// Unused methods
virtual const char* GetTerseDocumentation() const { return ""; }
@@ -297,7 +298,7 @@ public:
/**
* The name of the command as specified in CMakeList.txt.
*/
- virtual const char* GetName() const { return "SET_TESTS_PROPERTIES";}
+ virtual const char* GetName() const { return "set_tests_properties";}
// Unused methods
virtual const char* GetTerseDocumentation() const { return ""; }
@@ -537,6 +538,7 @@ int cmCTestTestHandler::ProcessHandler()
this->UseExcludeRegExp();
this->SetExcludeRegExp(val);
}
+ this->SetRerunFailed(cmSystemTools::IsOn(this->GetOption("RerunFailed")));
this->TestResults.clear();
@@ -819,6 +821,13 @@ void cmCTestTestHandler::ComputeTestList()
{
this->TestList.clear(); // clear list of test
this->GetListOfTests();
+
+ if (this->RerunFailed)
+ {
+ this->ComputeTestListForRerunFailed();
+ return;
+ }
+
cmCTestTestHandler::ListOfTests::size_type tmsize = this->TestList.size();
// how many tests are in based on RegExp?
int inREcnt = 0;
@@ -881,9 +890,47 @@ void cmCTestTestHandler::ComputeTestList()
this->TotalNumberOfTests = this->TestList.size();
// Set the TestList to the final list of all test
this->TestList = finalList;
+
+ this->UpdateMaxTestNameWidth();
+}
+
+void cmCTestTestHandler::ComputeTestListForRerunFailed()
+{
+ this->ExpandTestsToRunInformationForRerunFailed();
+
+ cmCTestTestHandler::ListOfTests::iterator it;
+ ListOfTests finalList;
+ int cnt = 0;
+ for ( it = this->TestList.begin(); it != this->TestList.end(); it ++ )
+ {
+ cnt ++;
+
+ // if this test is not in our list of tests to run, then skip it.
+ if ((this->TestsToRun.size() &&
+ std::find(this->TestsToRun.begin(), this->TestsToRun.end(), cnt)
+ == this->TestsToRun.end()))
+ {
+ continue;
+ }
+
+ it->Index = cnt;
+ finalList.push_back(*it);
+ }
+
+ // Save the total number of tests before exclusions
+ this->TotalNumberOfTests = this->TestList.size();
+
+ // Set the TestList to the list of failed tests to rerun
+ this->TestList = finalList;
+
+ this->UpdateMaxTestNameWidth();
+}
+
+void cmCTestTestHandler::UpdateMaxTestNameWidth()
+{
std::string::size_type max = this->CTest->GetMaxTestNameWidth();
- for (it = this->TestList.begin();
- it != this->TestList.end(); it ++ )
+ for ( cmCTestTestHandler::ListOfTests::iterator it = this->TestList.begin();
+ it != this->TestList.end(); it ++ )
{
cmCTestTestProperties& p = *it;
if(max < p.Name.size())
@@ -1708,6 +1755,91 @@ void cmCTestTestHandler::ExpandTestsToRunInformation(size_t numTests)
this->TestsToRun.erase(new_end, this->TestsToRun.end());
}
+void cmCTestTestHandler::ExpandTestsToRunInformationForRerunFailed()
+{
+
+ std::string dirName = this->CTest->GetBinaryDir() + "/Testing/Temporary";
+
+ cmsys::Directory directory;
+ if (directory.Load(dirName.c_str()) == 0)
+ {
+ cmCTestLog(this->CTest, ERROR_MESSAGE, "Unable to read the contents of "
+ << dirName << std::endl);
+ return;
+ }
+
+ int numFiles = static_cast<int>
+ (cmsys::Directory::GetNumberOfFilesInDirectory(dirName.c_str()));
+ std::string pattern = "LastTestsFailed";
+ std::string logName = "";
+
+ for (int i = 0; i < numFiles; ++i)
+ {
+ std::string fileName = directory.GetFile(i);
+ // bcc crashes if we attempt a normal substring comparison,
+ // hence the following workaround
+ std::string fileNameSubstring = fileName.substr(0, pattern.length());
+ if (fileNameSubstring.compare(pattern) != 0)
+ {
+ continue;
+ }
+ if (logName == "")
+ {
+ logName = fileName;
+ }
+ else
+ {
+ // if multiple matching logs were found we use the most recently
+ // modified one.
+ int res;
+ cmSystemTools::FileTimeCompare(logName.c_str(), fileName.c_str(), &res);
+ if (res == -1)
+ {
+ logName = fileName;
+ }
+ }
+ }
+
+ std::string lastTestsFailedLog = this->CTest->GetBinaryDir()
+ + "/Testing/Temporary/" + logName;
+
+ if ( !cmSystemTools::FileExists(lastTestsFailedLog.c_str()) )
+ {
+ if ( !this->CTest->GetShowOnly() && !this->CTest->ShouldPrintLabels() )
+ {
+ cmCTestLog(this->CTest, ERROR_MESSAGE, lastTestsFailedLog
+ << " does not exist!" << std::endl);
+ }
+ return;
+ }
+
+ // parse the list of tests to rerun from LastTestsFailed.log
+ std::ifstream ifs(lastTestsFailedLog.c_str());
+ if ( ifs )
+ {
+ std::string line;
+ std::string::size_type pos;
+ while ( cmSystemTools::GetLineFromStream(ifs, line) )
+ {
+ pos = line.find(':', 0);
+ if (pos == line.npos)
+ {
+ continue;
+ }
+
+ int val = atoi(line.substr(0, pos).c_str());
+ this->TestsToRun.push_back(val);
+ }
+ ifs.close();
+ }
+ else if ( !this->CTest->GetShowOnly() && !this->CTest->ShouldPrintLabels() )
+ {
+ cmCTestLog(this->CTest, ERROR_MESSAGE, "Problem reading file: "
+ << lastTestsFailedLog.c_str() <<
+ " while generating list of previously failed tests." << std::endl);
+ }
+}
+
//----------------------------------------------------------------------
// Just for convenience
#define SPACE_REGEX "[ \t\r\n]"
diff --git a/Source/CTest/cmCTestTestHandler.h b/Source/CTest/cmCTestTestHandler.h
index 93b793b..398f052 100644
--- a/Source/CTest/cmCTestTestHandler.h
+++ b/Source/CTest/cmCTestTestHandler.h
@@ -44,6 +44,12 @@ public:
void SetUseUnion(bool val) { this->UseUnion = val; }
/**
+ * Set whether or not CTest should only execute the tests that failed
+ * on the previous run. By default this is false.
+ */
+ void SetRerunFailed(bool val) { this->RerunFailed = val; }
+
+ /**
* This method is called when reading CTest custom file
*/
void PopulateCustomVectors(cmMakefile *mf);
@@ -213,6 +219,12 @@ private:
// based on union regex and -I stuff
void ComputeTestList();
+ // compute the lists of tests that will actually run
+ // based on LastTestFailed.log
+ void ComputeTestListForRerunFailed();
+
+ void UpdateMaxTestNameWidth();
+
bool GetValue(const char* tag,
std::string& value,
std::ifstream& fin);
@@ -235,6 +247,7 @@ private:
const char* GetTestStatus(int status);
void ExpandTestsToRunInformation(size_t numPossibleTests);
+ void ExpandTestsToRunInformationForRerunFailed();
std::vector<cmStdString> CustomPreTest;
std::vector<cmStdString> CustomPostTest;
@@ -268,6 +281,8 @@ private:
cmsys::RegularExpression DartStuff;
std::ostream* LogFile;
+
+ bool RerunFailed;
};
#endif
diff --git a/Source/CTest/cmParsePythonCoverage.cxx b/Source/CTest/cmParsePythonCoverage.cxx
new file mode 100644
index 0000000..a086f13
--- /dev/null
+++ b/Source/CTest/cmParsePythonCoverage.cxx
@@ -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;
+}
diff --git a/Source/CTest/cmParsePythonCoverage.h b/Source/CTest/cmParsePythonCoverage.h
new file mode 100644
index 0000000..668c7f9
--- /dev/null
+++ b/Source/CTest/cmParsePythonCoverage.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 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