summaryrefslogtreecommitdiffstats
path: root/Source/CTest
diff options
context:
space:
mode:
Diffstat (limited to 'Source/CTest')
-rw-r--r--Source/CTest/cmCTestBuildCommand.h26
-rw-r--r--Source/CTest/cmCTestConfigureCommand.h28
-rw-r--r--Source/CTest/cmCTestCoverageCommand.h26
-rw-r--r--Source/CTest/cmCTestCoverageHandler.cxx35
-rw-r--r--Source/CTest/cmCTestCoverageHandler.h4
-rw-r--r--Source/CTest/cmCTestEmptyBinaryDirectoryCommand.h20
-rw-r--r--Source/CTest/cmCTestMemCheckCommand.h34
-rw-r--r--Source/CTest/cmCTestMultiProcessHandler.cxx147
-rw-r--r--Source/CTest/cmCTestMultiProcessHandler.h1
-rw-r--r--Source/CTest/cmCTestP4.cxx559
-rw-r--r--Source/CTest/cmCTestP4.h71
-rw-r--r--Source/CTest/cmCTestReadCustomFilesCommand.h19
-rw-r--r--Source/CTest/cmCTestRunScriptCommand.h24
-rw-r--r--Source/CTest/cmCTestScriptHandler.cxx6
-rw-r--r--Source/CTest/cmCTestScriptHandler.h1
-rw-r--r--Source/CTest/cmCTestSleepCommand.h20
-rw-r--r--Source/CTest/cmCTestStartCommand.h24
-rw-r--r--Source/CTest/cmCTestSubmitCommand.h38
-rw-r--r--Source/CTest/cmCTestTestCommand.h39
-rw-r--r--Source/CTest/cmCTestTestHandler.cxx156
-rw-r--r--Source/CTest/cmCTestTestHandler.h15
-rw-r--r--Source/CTest/cmCTestUpdateCommand.cxx8
-rw-r--r--Source/CTest/cmCTestUpdateCommand.h22
-rw-r--r--Source/CTest/cmCTestUpdateHandler.cxx26
-rw-r--r--Source/CTest/cmCTestUpdateHandler.h1
-rw-r--r--Source/CTest/cmCTestUploadCommand.h19
-rw-r--r--Source/CTest/cmParsePythonCoverage.cxx113
-rw-r--r--Source/CTest/cmParsePythonCoverage.h48
28 files changed, 1106 insertions, 424 deletions
diff --git a/Source/CTest/cmCTestBuildCommand.h b/Source/CTest/cmCTestBuildCommand.h
index cabc39b..08887fe 100644
--- a/Source/CTest/cmCTestBuildCommand.h
+++ b/Source/CTest/cmCTestBuildCommand.h
@@ -45,34 +45,8 @@ public:
*/
virtual const char* GetName() const { return "ctest_build";}
- /**
- * Succinct documentation.
- */
- virtual const char* GetTerseDocumentation() const
- {
- return "Build the project.";
- }
virtual bool InitialPass(std::vector<std::string> const& args,
cmExecutionStatus &status);
- /**
- * More documentation.
- */
- virtual const char* GetFullDocumentation() const
- {
- return
- " ctest_build([BUILD build_dir] [TARGET target] [RETURN_VALUE res]\n"
- " [APPEND][NUMBER_ERRORS val] [NUMBER_WARNINGS val])\n"
- "Builds the given build directory and stores results in Build.xml. "
- "If no BUILD is given, the CTEST_BINARY_DIRECTORY variable is used.\n"
- "The TARGET variable can be used to specify a build target. If none "
- "is specified, the \"all\" target will be built.\n"
- "The RETURN_VALUE option specifies a variable in which to store the "
- "return value of the native build tool. "
- "The NUMBER_ERRORS and NUMBER_WARNINGS options specify variables in "
- "which to store the number of build errors and warnings detected."
- "\n"
- CTEST_COMMAND_APPEND_OPTION_DOCS;
- }
cmTypeMacro(cmCTestBuildCommand, cmCTestHandlerCommand);
diff --git a/Source/CTest/cmCTestConfigureCommand.h b/Source/CTest/cmCTestConfigureCommand.h
index b343fc1..b592c5a 100644
--- a/Source/CTest/cmCTestConfigureCommand.h
+++ b/Source/CTest/cmCTestConfigureCommand.h
@@ -40,34 +40,6 @@ public:
*/
virtual const char* GetName() const { return "ctest_configure";}
- /**
- * Succinct documentation.
- */
- virtual const char* GetTerseDocumentation() const
- {
- return "Configure the project build tree.";
- }
-
- /**
- * More documentation.
- */
- virtual const char* GetFullDocumentation() const
- {
- return
- " ctest_configure([BUILD build_dir] [SOURCE source_dir] [APPEND]\n"
- " [OPTIONS options] [RETURN_VALUE res])\n"
- "Configures the given build directory and stores results in "
- "Configure.xml. "
- "If no BUILD is given, the CTEST_BINARY_DIRECTORY variable is used. "
- "If no SOURCE is given, the CTEST_SOURCE_DIRECTORY variable is used. "
- "The OPTIONS argument specifies command line arguments to pass to "
- "the configuration tool. "
- "The RETURN_VALUE option specifies a variable in which to store the "
- "return value of the native build tool."
- "\n"
- CTEST_COMMAND_APPEND_OPTION_DOCS;
- }
-
cmTypeMacro(cmCTestConfigureCommand, cmCTestHandlerCommand);
protected:
diff --git a/Source/CTest/cmCTestCoverageCommand.h b/Source/CTest/cmCTestCoverageCommand.h
index 2fe762c..11bb411 100644
--- a/Source/CTest/cmCTestCoverageCommand.h
+++ b/Source/CTest/cmCTestCoverageCommand.h
@@ -41,32 +41,6 @@ public:
*/
virtual const char* GetName() const { return "ctest_coverage";}
- /**
- * Succinct documentation.
- */
- virtual const char* GetTerseDocumentation() const
- {
- return "Collect coverage tool results.";
- }
-
- /**
- * More documentation.
- */
- virtual const char* GetFullDocumentation() const
- {
- return
- " ctest_coverage([BUILD build_dir] [RETURN_VALUE res] [APPEND]\n"
- " [LABELS label1 [label2 [...]]])\n"
- "Perform the coverage of the given build directory and stores results "
- "in Coverage.xml. The second argument is a variable that will hold "
- "value."
- "\n"
- "The LABELS option filters the coverage report to include only "
- "source files labeled with at least one of the labels specified."
- "\n"
- CTEST_COMMAND_APPEND_OPTION_DOCS;
- }
-
cmTypeMacro(cmCTestCoverageCommand, cmCTestHandlerCommand);
protected:
diff --git a/Source/CTest/cmCTestCoverageHandler.cxx b/Source/CTest/cmCTestCoverageHandler.cxx
index 20aded2..33b084e 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"
@@ -141,6 +142,7 @@ void cmCTestCoverageHandler::Initialize()
this->Superclass::Initialize();
this->CustomCoverageExclude.clear();
this->SourceLabels.clear();
+ this->TargetDirs.clear();
this->LabelIdMap.clear();
this->Labels.clear();
this->LabelFilter.clear();
@@ -392,6 +394,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 +770,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/cmCTestEmptyBinaryDirectoryCommand.h b/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.h
index a763fe9..07e59a4 100644
--- a/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.h
+++ b/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.h
@@ -50,26 +50,6 @@ public:
*/
virtual const char* GetName() const { return "ctest_empty_binary_directory";}
- /**
- * Succinct documentation.
- */
- virtual const char* GetTerseDocumentation() const
- {
- return "empties the binary directory";
- }
-
- /**
- * More documentation.
- */
- virtual const char* GetFullDocumentation() const
- {
- return
- " ctest_empty_binary_directory( directory )\n"
- "Removes a binary directory. This command will perform some checks "
- "prior to deleting the directory in an attempt to avoid malicious "
- "or accidental directory deletion.";
- }
-
cmTypeMacro(cmCTestEmptyBinaryDirectoryCommand, cmCTestCommand);
};
diff --git a/Source/CTest/cmCTestMemCheckCommand.h b/Source/CTest/cmCTestMemCheckCommand.h
index 6db47ae..b50170d 100644
--- a/Source/CTest/cmCTestMemCheckCommand.h
+++ b/Source/CTest/cmCTestMemCheckCommand.h
@@ -43,40 +43,6 @@ public:
*/
virtual const char* GetName() const { return "ctest_memcheck";}
- /**
- * Succinct documentation.
- */
- virtual const char* GetTerseDocumentation() const
- {
- return "Run tests with a dynamic analysis tool.";
- }
-
- /**
- * More documentation.
- */
- virtual const char* GetFullDocumentation() const
- {
- return
- " ctest_memcheck([BUILD build_dir] [RETURN_VALUE res] [APPEND]\n"
- " [START start number] [END end number]\n"
- " [STRIDE stride number] [EXCLUDE exclude regex ]\n"
- " [INCLUDE include regex] \n"
- " [EXCLUDE_LABEL exclude regex] \n"
- " [INCLUDE_LABEL label regex] \n"
- " [PARALLEL_LEVEL level] )\n"
- "Tests the given build directory and stores results in MemCheck.xml. "
- "The second argument is a variable that will hold value. Optionally, "
- "you can specify the starting test number START, the ending test number "
- "END, the number of tests to skip between each test STRIDE, a regular "
- "expression for tests to run INCLUDE, or a regular expression for tests "
- "not to run EXCLUDE. EXCLUDE_LABEL and INCLUDE_LABEL are regular "
- "expressions for tests to be included or excluded by the test "
- "property LABEL. PARALLEL_LEVEL should be set to a positive number "
- "representing the number of tests to be run in parallel."
- "\n"
- CTEST_COMMAND_APPEND_OPTION_DOCS;
- }
-
cmTypeMacro(cmCTestMemCheckCommand, cmCTestTestCommand);
protected:
diff --git a/Source/CTest/cmCTestMultiProcessHandler.cxx b/Source/CTest/cmCTestMultiProcessHandler.cxx
index 76ddeea..4c39d10 100644
--- a/Source/CTest/cmCTestMultiProcessHandler.cxx
+++ b/Source/CTest/cmCTestMultiProcessHandler.cxx
@@ -41,6 +41,7 @@ cmCTestMultiProcessHandler::cmCTestMultiProcessHandler()
this->Completed = 0;
this->RunningCount = 0;
this->StopTimePassed = false;
+ this->HasCycles = false;
}
cmCTestMultiProcessHandler::~cmCTestMultiProcessHandler()
@@ -65,6 +66,11 @@ cmCTestMultiProcessHandler::SetTests(TestMap& tests,
if(!this->CTest->GetShowOnly())
{
this->ReadCostData();
+ this->HasCycles = !this->CheckCycles();
+ if(this->HasCycles)
+ {
+ return;
+ }
this->CreateTestCostList();
}
}
@@ -79,7 +85,7 @@ void cmCTestMultiProcessHandler::SetParallelLevel(size_t level)
void cmCTestMultiProcessHandler::RunTests()
{
this->CheckResume();
- if(!this->CheckCycles())
+ if(this->HasCycles)
{
return;
}
@@ -205,37 +211,8 @@ bool cmCTestMultiProcessHandler::StartTest(int test)
}
}
- // copy the depend tests locally because when
- // a test is finished it will be removed from the depend list
- // and we don't want to be iterating a list while removing from it
- TestSet depends = this->Tests[test];
- size_t totalDepends = depends.size();
- if(totalDepends)
- {
- for(TestSet::const_iterator i = depends.begin();
- i != depends.end(); ++i)
- {
- // if the test is not already running then start it
- if(!this->TestRunningMap[*i])
- {
- // this test might be finished, but since
- // this is a copy of the depend map we might
- // still have it
- if(!this->TestFinishMap[*i])
- {
- // only start one test in this function
- return this->StartTest(*i);
- }
- else
- {
- // the depend has been and finished
- totalDepends--;
- }
- }
- }
- }
// if there are no depends left then run this test
- if(totalDepends == 0)
+ if(this->Tests[test].empty())
{
this->StartTestProcess(test);
return true;
@@ -262,25 +239,17 @@ void cmCTestMultiProcessHandler::StartNextTests()
TestList copy = this->SortedTests;
for(TestList::iterator test = copy.begin(); test != copy.end(); ++test)
{
- //in case this test has already been started due to dependency
- if(this->TestRunningMap[*test] || this->TestFinishMap[*test])
- {
- continue;
- }
size_t processors = GetProcessorsUsed(*test);
- if(processors > numToStart)
- {
- return;
- }
- if(this->StartTest(*test))
+
+ if(processors <= numToStart && this->StartTest(*test))
{
- if(this->StopTimePassed)
- {
- return;
- }
- numToStart -= processors;
+ if(this->StopTimePassed)
+ {
+ return;
+ }
+ numToStart -= processors;
}
- if(numToStart == 0)
+ else if(numToStart == 0)
{
return;
}
@@ -472,24 +441,88 @@ int cmCTestMultiProcessHandler::SearchByName(std::string name)
//---------------------------------------------------------
void cmCTestMultiProcessHandler::CreateTestCostList()
{
- for(TestMap::iterator i = this->Tests.begin();
- i != this->Tests.end(); ++i)
- {
- SortedTests.push_back(i->first);
+ TestSet alreadySortedTests;
+
+ std::list<TestSet> priorityStack;
+ priorityStack.push_back(TestSet());
+ TestSet &topLevel = priorityStack.back();
- //If the test failed last time, it should be run first, so max the cost.
- //Only do this for parallel runs; in non-parallel runs, avoid clobbering
- //the test's explicitly set cost.
+ // In parallel test runs add previously failed tests to the front
+ // of the cost list and queue other tests for further sorting
+ for(TestMap::const_iterator i = this->Tests.begin();
+ i != this->Tests.end(); ++i)
+ {
if(this->ParallelLevel > 1 &&
std::find(this->LastTestsFailed.begin(), this->LastTestsFailed.end(),
this->Properties[i->first]->Name) != this->LastTestsFailed.end())
{
- this->Properties[i->first]->Cost = FLT_MAX;
+ //If the test failed last time, it should be run first.
+ this->SortedTests.push_back(i->first);
+ alreadySortedTests.insert(i->first);
+ }
+ else
+ {
+ topLevel.insert(i->first);
+ }
+ }
+
+ // Repeatedly move dependencies of the tests on the current dependency level
+ // to the next level until no further dependencies exist.
+ while(priorityStack.back().size())
+ {
+ TestSet &previousSet = priorityStack.back();
+ priorityStack.push_back(TestSet());
+ TestSet &currentSet = priorityStack.back();
+
+ for(TestSet::const_iterator i = previousSet.begin();
+ i != previousSet.end(); ++i)
+ {
+ TestSet const& dependencies = this->Tests[*i];
+ for(TestSet::const_iterator j = dependencies.begin();
+ j != dependencies.end(); ++j)
+ {
+ currentSet.insert(*j);
+ }
+ }
+
+ for(TestSet::const_iterator i = currentSet.begin();
+ i != currentSet.end(); ++i)
+ {
+ previousSet.erase(*i);
}
}
- TestComparator comp(this);
- std::stable_sort(SortedTests.begin(), SortedTests.end(), comp);
+ // Remove the empty dependency level
+ priorityStack.pop_back();
+
+ // Reverse iterate over the different dependency levels (deepest first).
+ // Sort tests within each level by COST and append them to the cost list.
+ for(std::list<TestSet>::reverse_iterator i = priorityStack.rbegin();
+ i != priorityStack.rend(); ++i)
+ {
+ TestSet const& currentSet = *i;
+ TestComparator comp(this);
+
+ TestList sortedCopy;
+
+ for(TestSet::const_iterator j = currentSet.begin();
+ j != currentSet.end(); ++j)
+ {
+ sortedCopy.push_back(*j);
+ }
+
+ std::stable_sort(sortedCopy.begin(), sortedCopy.end(), comp);
+
+ for(TestList::const_iterator j = sortedCopy.begin();
+ j != sortedCopy.end(); ++j)
+ {
+ if(alreadySortedTests.find(*j) == alreadySortedTests.end())
+ {
+ this->SortedTests.push_back(*j);
+ alreadySortedTests.insert(*j);
+ }
+ }
+ }
}
//---------------------------------------------------------
diff --git a/Source/CTest/cmCTestMultiProcessHandler.h b/Source/CTest/cmCTestMultiProcessHandler.h
index cd21d91..439a8f3 100644
--- a/Source/CTest/cmCTestMultiProcessHandler.h
+++ b/Source/CTest/cmCTestMultiProcessHandler.h
@@ -111,6 +111,7 @@ protected:
std::set<cmCTestRunTest*> RunningTests; // current running tests
cmCTestTestHandler * TestHandler;
cmCTest* CTest;
+ bool HasCycles;
};
#endif
diff --git a/Source/CTest/cmCTestP4.cxx b/Source/CTest/cmCTestP4.cxx
new file mode 100644
index 0000000..0058721
--- /dev/null
+++ b/Source/CTest/cmCTestP4.cxx
@@ -0,0 +1,559 @@
+/*============================================================================
+ CMake - Cross Platform Makefile Generator
+ Copyright 2000-2013 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.
+============================================================================*/
+#include "cmCTestP4.h"
+
+#include "cmCTest.h"
+#include "cmSystemTools.h"
+#include "cmXMLSafe.h"
+
+#include <cmsys/RegularExpression.hxx>
+#include <cmsys/ios/sstream>
+#include <cmsys/Process.h>
+
+#include <sys/types.h>
+#include <time.h>
+#include <ctype.h>
+
+//----------------------------------------------------------------------------
+cmCTestP4::cmCTestP4(cmCTest* ct, std::ostream& log):
+ cmCTestGlobalVC(ct, log)
+{
+ this->PriorRev = this->Unknown;
+}
+
+//----------------------------------------------------------------------------
+cmCTestP4::~cmCTestP4()
+{
+}
+
+//----------------------------------------------------------------------------
+class cmCTestP4::IdentifyParser: public cmCTestVC::LineParser
+{
+public:
+ IdentifyParser(cmCTestP4* p4, const char* prefix,
+ std::string& rev): Rev(rev)
+ {
+ this->SetLog(&p4->Log, prefix);
+ this->RegexIdentify.compile("^Change ([0-9]+) on");
+ }
+private:
+ std::string& Rev;
+ cmsys::RegularExpression RegexIdentify;
+
+ bool ProcessLine()
+ {
+ if(this->RegexIdentify.find(this->Line))
+ {
+ this->Rev = this->RegexIdentify.match(1);
+ return false;
+ }
+ return true;
+ }
+};
+
+//----------------------------------------------------------------------------
+class cmCTestP4::ChangesParser: public cmCTestVC::LineParser
+{
+public:
+ ChangesParser(cmCTestP4* p4, const char* prefix) : P4(p4)
+ {
+ this->SetLog(&P4->Log, prefix);
+ this->RegexIdentify.compile("^Change ([0-9]+) on");
+ }
+private:
+ cmsys::RegularExpression RegexIdentify;
+ cmCTestP4* P4;
+
+ bool ProcessLine()
+ {
+ if(this->RegexIdentify.find(this->Line))
+ {
+ P4->ChangeLists.push_back(this->RegexIdentify.match(1));
+ }
+ return true;
+ }
+};
+
+//----------------------------------------------------------------------------
+class cmCTestP4::UserParser: public cmCTestVC::LineParser
+{
+public:
+ UserParser(cmCTestP4* p4, const char* prefix) : P4(p4)
+ {
+ this->SetLog(&P4->Log, prefix);
+ this->RegexUser.compile("^(.+) <(.*)> \\((.*)\\) accessed (.*)$");
+ }
+private:
+ cmsys::RegularExpression RegexUser;
+ cmCTestP4* P4;
+
+ bool ProcessLine()
+ {
+ if(this->RegexUser.find(this->Line))
+ {
+ User NewUser;
+
+ NewUser.UserName = this->RegexUser.match(1);
+ NewUser.EMail = this->RegexUser.match(2);
+ NewUser.Name = this->RegexUser.match(3);
+ NewUser.AccessTime = this->RegexUser.match(4);
+ P4->Users[this->RegexUser.match(1)] = NewUser;
+
+ return false;
+ }
+ return true;
+ }
+};
+
+//----------------------------------------------------------------------------
+/* Diff format:
+==== //depot/file#rev - /absolute/path/to/file ====
+(diff data)
+==== //depot/file2#rev - /absolute/path/to/file2 ====
+(diff data)
+==== //depot/file3#rev - /absolute/path/to/file3 ====
+==== //depot/file4#rev - /absolute/path/to/file4 ====
+(diff data)
+*/
+class cmCTestP4::DiffParser: public cmCTestVC::LineParser
+{
+public:
+ DiffParser(cmCTestP4* p4, const char* prefix)
+ : P4(p4), AlreadyNotified(false)
+ {
+ this->SetLog(&P4->Log, prefix);
+ this->RegexDiff.compile("^==== (.*)#[0-9]+ - (.*)");
+ }
+private:
+ cmCTestP4* P4;
+ bool AlreadyNotified;
+ std::string CurrentPath;
+ cmsys::RegularExpression RegexDiff;
+
+ bool ProcessLine()
+ {
+ if(!this->Line.empty() && this->Line[0] == '='
+ && this->RegexDiff.find(this->Line))
+ {
+ CurrentPath = this->RegexDiff.match(1);
+ AlreadyNotified = false;
+ }
+ else
+ {
+ if(!AlreadyNotified)
+ {
+ P4->DoModification(PathModified, CurrentPath);
+ AlreadyNotified = true;
+ }
+ }
+ return true;
+ }
+};
+
+//----------------------------------------------------------------------------
+cmCTestP4::User cmCTestP4::GetUserData(const std::string& username)
+{
+ std::map<std::string, cmCTestP4::User>::const_iterator it =
+ Users.find(username);
+
+ if(it == Users.end())
+ {
+ std::vector<char const*> p4_users;
+ SetP4Options(p4_users);
+ p4_users.push_back("users");
+ p4_users.push_back("-m");
+ p4_users.push_back("1");
+ p4_users.push_back(username.c_str());
+ p4_users.push_back(0);
+
+ UserParser out(this, "users-out> ");
+ OutputLogger err(this->Log, "users-err> ");
+ RunChild(&p4_users[0], &out, &err);
+
+ // The user should now be added to the map. Search again.
+ it = Users.find(username);
+ if(it == Users.end())
+ {
+ return cmCTestP4::User();
+ }
+ }
+
+ return it->second;
+}
+
+//----------------------------------------------------------------------------
+/* Commit format:
+
+Change 1111111 by user@client on 2013/09/26 11:50:36
+
+ text
+ text
+
+Affected files ...
+
+... //path/to/file#rev edit
+... //path/to/file#rev add
+... //path/to/file#rev delete
+... //path/to/file#rev integrate
+*/
+class cmCTestP4::DescribeParser: public cmCTestVC::LineParser
+{
+public:
+ DescribeParser(cmCTestP4* p4, const char* prefix):
+ LineParser('\n', false), P4(p4), Section(SectionHeader)
+ {
+ this->SetLog(&P4->Log, prefix);
+ this->RegexHeader.compile("^Change ([0-9]+) by (.+)@(.+) on (.*)$");
+ this->RegexDiff.compile("^\\.\\.\\. (.*)#[0-9]+ ([^ ]+)$");
+ }
+private:
+ cmsys::RegularExpression RegexHeader;
+ cmsys::RegularExpression RegexDiff;
+ cmCTestP4* P4;
+
+ typedef cmCTestP4::Revision Revision;
+ typedef cmCTestP4::Change Change;
+ std::vector<Change> Changes;
+ enum SectionType { SectionHeader, SectionBody, SectionDiffHeader,
+ SectionDiff, SectionCount };
+ SectionType Section;
+ Revision Rev;
+
+ virtual bool ProcessLine()
+ {
+ if(this->Line.empty())
+ {
+ this->NextSection();
+ }
+ else
+ {
+ switch(this->Section)
+ {
+ case SectionHeader: this->DoHeaderLine(); break;
+ case SectionBody: this->DoBodyLine(); break;
+ case SectionDiffHeader: break; // nothing to do
+ case SectionDiff: this->DoDiffLine(); break;
+ case SectionCount: break; // never happens
+ }
+ }
+ return true;
+ }
+
+ void NextSection()
+ {
+ if(this->Section == SectionDiff)
+ {
+ this->P4->DoRevision(this->Rev, this->Changes);
+ this->Rev = Revision();
+ }
+
+ this->Section = SectionType((this->Section+1) % SectionCount);
+ }
+
+ void DoHeaderLine()
+ {
+ if(this->RegexHeader.find(this->Line))
+ {
+ this->Rev.Rev = this->RegexHeader.match(1);
+ this->Rev.Date = this->RegexHeader.match(4);
+
+ cmCTestP4::User user = P4->GetUserData(this->RegexHeader.match(2));
+ this->Rev.Author = user.Name;
+ this->Rev.EMail = user.EMail;
+
+ this->Rev.Committer = this->Rev.Author;
+ this->Rev.CommitterEMail = this->Rev.EMail;
+ this->Rev.CommitDate = this->Rev.Date;
+ }
+ }
+
+ void DoBodyLine()
+ {
+ if(this->Line[0] == '\t')
+ {
+ this->Rev.Log += this->Line.substr(1);
+ }
+ this->Rev.Log += "\n";
+ }
+
+ void DoDiffLine()
+ {
+ if(this->RegexDiff.find(this->Line))
+ {
+ Change change;
+ std::string Path = this->RegexDiff.match(1);
+ if(Path.length() > 2 && Path[0] == '/' && Path[1] == '/')
+ {
+ size_t found = Path.find('/', 2);
+ if(found != std::string::npos)
+ {
+ Path = Path.substr(found + 1);
+ }
+ }
+
+ change.Path = Path;
+ std::string action = this->RegexDiff.match(2);
+
+ if(action == "add")
+ {
+ change.Action = 'A';
+ }
+ else if(action == "delete")
+ {
+ change.Action = 'D';
+ }
+ else if(action == "edit" || action == "integrate")
+ {
+ change.Action = 'M';
+ }
+
+ Changes.push_back(change);
+ }
+ }
+};
+
+//----------------------------------------------------------------------------
+void cmCTestP4::SetP4Options(std::vector<char const*> &CommandOptions)
+{
+ if(P4Options.size() == 0)
+ {
+ const char* p4 = this->CommandLineTool.c_str();
+ P4Options.push_back(p4);
+
+ //The CTEST_P4_CLIENT variable sets the P4 client used when issuing
+ //Perforce commands, if it's different from the default one.
+ std::string client = this->CTest->GetCTestConfiguration("P4Client");
+ if(!client.empty())
+ {
+ P4Options.push_back("-c");
+ P4Options.push_back(client);
+ }
+
+ //Set the message language to be English, in case the P4 admin
+ //has localized them
+ P4Options.push_back("-L");
+ P4Options.push_back("en");
+
+ //The CTEST_P4_OPTIONS variable adds additional Perforce command line
+ //options before the main command
+ std::string opts = this->CTest->GetCTestConfiguration("P4Options");
+ std::vector<cmStdString> args =
+ cmSystemTools::ParseArguments(opts.c_str());
+
+ for(std::vector<cmStdString>::const_iterator ai = args.begin();
+ ai != args.end(); ++ai)
+ {
+ P4Options.push_back(ai->c_str());
+ }
+ }
+
+ CommandOptions.clear();
+ for(std::vector<std::string>::iterator i = P4Options.begin();
+ i != P4Options.end(); ++i)
+ {
+ CommandOptions.push_back(i->c_str());
+ }
+}
+
+//----------------------------------------------------------------------------
+std::string cmCTestP4::GetWorkingRevision()
+{
+ std::vector<char const*> p4_identify;
+ SetP4Options(p4_identify);
+
+ p4_identify.push_back("changes");
+ p4_identify.push_back("-m");
+ p4_identify.push_back("1");
+ p4_identify.push_back("-t");
+
+ std::string source = this->SourceDirectory + "/...#have";
+ p4_identify.push_back(source.c_str());
+ p4_identify.push_back(0);
+
+ std::string rev;
+ IdentifyParser out(this, "rev-out> ", rev);
+ OutputLogger err(this->Log, "rev-err> ");
+
+ RunChild(&p4_identify[0], &out, &err);
+
+ if(rev.empty())
+ {
+ return "0";
+ }
+ else
+ {
+ return rev;
+ }
+}
+
+//----------------------------------------------------------------------------
+void cmCTestP4::NoteOldRevision()
+{
+ this->OldRevision = this->GetWorkingRevision();
+
+ cmCTestLog(this->CTest, HANDLER_OUTPUT, " Old revision of repository is: "
+ << this->OldRevision << "\n");
+ this->PriorRev.Rev = this->OldRevision;
+}
+
+//----------------------------------------------------------------------------
+void cmCTestP4::NoteNewRevision()
+{
+ this->NewRevision = this->GetWorkingRevision();
+
+ cmCTestLog(this->CTest, HANDLER_OUTPUT, " New revision of repository is: "
+ << this->NewRevision << "\n");
+}
+
+//----------------------------------------------------------------------------
+void cmCTestP4::LoadRevisions()
+{
+ std::vector<char const*> p4_changes;
+ SetP4Options(p4_changes);
+
+ // Use 'p4 changes ...@old,new' to get a list of changelists
+ std::string range = this->SourceDirectory + "/...";
+
+ if(this->OldRevision != "0")
+ {
+ range.append("@").append(this->OldRevision);
+ }
+
+ if(this->NewRevision != "0")
+ {
+ if(this->OldRevision != "0")
+ {
+ range.append(",").append(this->NewRevision);
+ }
+ else
+ {
+ range.append("@").append(this->NewRevision);
+ }
+ }
+
+ p4_changes.push_back("changes");
+ p4_changes.push_back(range.c_str());
+ p4_changes.push_back(0);
+
+ ChangesParser out(this, "changes-out> ");
+ OutputLogger err(this->Log, "changes-err> ");
+
+ ChangeLists.clear();
+ this->RunChild(&p4_changes[0], &out, &err);
+
+ if(ChangeLists.size() == 0)
+ return;
+
+ //p4 describe -s ...@1111111,2222222
+ std::vector<char const*> p4_describe;
+ for(std::vector<std::string>::reverse_iterator i = ChangeLists.rbegin();
+ i != ChangeLists.rend(); ++i)
+ {
+ SetP4Options(p4_describe);
+ p4_describe.push_back("describe");
+ p4_describe.push_back("-s");
+ p4_describe.push_back(i->c_str());
+ p4_describe.push_back(0);
+
+ DescribeParser outDescribe(this, "describe-out> ");
+ OutputLogger errDescribe(this->Log, "describe-err> ");
+ this->RunChild(&p4_describe[0], &outDescribe, &errDescribe);
+ }
+}
+
+//----------------------------------------------------------------------------
+void cmCTestP4::LoadModifications()
+{
+ std::vector<char const*> p4_diff;
+ SetP4Options(p4_diff);
+
+ p4_diff.push_back("diff");
+
+ //Ideally we would use -Od but not all clients support it
+ p4_diff.push_back("-dn");
+ std::string source = this->SourceDirectory + "/...";
+ p4_diff.push_back(source.c_str());
+ p4_diff.push_back(0);
+
+ DiffParser out(this, "diff-out> ");
+ OutputLogger err(this->Log, "diff-err> ");
+ this->RunChild(&p4_diff[0], &out, &err);
+}
+
+//----------------------------------------------------------------------------
+bool cmCTestP4::UpdateCustom(const std::string& custom)
+{
+ std::vector<std::string> p4_custom_command;
+ cmSystemTools::ExpandListArgument(custom, p4_custom_command, true);
+
+ std::vector<char const*> p4_custom;
+ for(std::vector<std::string>::const_iterator
+ i = p4_custom_command.begin(); i != p4_custom_command.end(); ++i)
+ {
+ p4_custom.push_back(i->c_str());
+ }
+ p4_custom.push_back(0);
+
+ OutputLogger custom_out(this->Log, "custom-out> ");
+ OutputLogger custom_err(this->Log, "custom-err> ");
+
+ return this->RunUpdateCommand(&p4_custom[0], &custom_out, &custom_err);
+}
+
+//----------------------------------------------------------------------------
+bool cmCTestP4::UpdateImpl()
+{
+ std::string custom = this->CTest->GetCTestConfiguration("P4UpdateCustom");
+ if(!custom.empty())
+ {
+ return this->UpdateCustom(custom);
+ }
+
+ std::vector<char const*> p4_sync;
+ SetP4Options(p4_sync);
+
+ p4_sync.push_back("sync");
+
+ // Get user-specified update options.
+ std::string opts = this->CTest->GetCTestConfiguration("UpdateOptions");
+ if(opts.empty())
+ {
+ opts = this->CTest->GetCTestConfiguration("P4UpdateOptions");
+ }
+ std::vector<cmStdString> args = cmSystemTools::ParseArguments(opts.c_str());
+ for(std::vector<cmStdString>::const_iterator ai = args.begin();
+ ai != args.end(); ++ai)
+ {
+ p4_sync.push_back(ai->c_str());
+ }
+
+ std::string source = this->SourceDirectory + "/...";
+
+ // Specify the start time for nightly testing.
+ if(this->CTest->GetTestModel() == cmCTest::NIGHTLY)
+ {
+ std::string date = this->GetNightlyTime();
+ //CTest reports the date as YYYY-MM-DD, Perforce needs it as YYYY/MM/DD
+ std::replace(date.begin(), date.end(), '-', '/');
+
+ //Revision specification: /...@"YYYY/MM/DD HH:MM:SS"
+ source.append("@\"").append(date).append("\"");
+ }
+
+ p4_sync.push_back(source.c_str());
+ p4_sync.push_back(0);
+
+ OutputLogger out(this->Log, "sync-out> ");
+ OutputLogger err(this->Log, "sync-err> ");
+
+ return this->RunUpdateCommand(&p4_sync[0], &out, &err);
+}
diff --git a/Source/CTest/cmCTestP4.h b/Source/CTest/cmCTestP4.h
new file mode 100644
index 0000000..7a53475
--- /dev/null
+++ b/Source/CTest/cmCTestP4.h
@@ -0,0 +1,71 @@
+/*============================================================================
+ CMake - Cross Platform Makefile Generator
+ Copyright 2000-2013 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 cmCTestP4_h
+#define cmCTestP4_h
+
+#include "cmCTestGlobalVC.h"
+#include <vector>
+#include <map>
+
+/** \class cmCTestP4
+ * \brief Interaction with the Perforce command-line tool
+ *
+ */
+class cmCTestP4: public cmCTestGlobalVC
+{
+public:
+ /** Construct with a CTest instance and update log stream. */
+ cmCTestP4(cmCTest* ctest, std::ostream& log);
+
+ virtual ~cmCTestP4();
+
+private:
+ std::vector<std::string> ChangeLists;
+
+ struct User
+ {
+ std::string UserName;
+ std::string Name;
+ std::string EMail;
+ std::string AccessTime;
+
+ User(): UserName(), Name(), EMail(), AccessTime() {}
+ };
+ std::map<std::string, User> Users;
+ std::vector<std::string> P4Options;
+
+ User GetUserData(const std::string& username);
+ void SetP4Options(std::vector<char const*> &options);
+
+ std::string GetWorkingRevision();
+ virtual void NoteOldRevision();
+ virtual void NoteNewRevision();
+ virtual bool UpdateImpl();
+ bool UpdateCustom(const std::string& custom);
+
+ void LoadRevisions();
+ void LoadModifications();
+
+ // Parsing helper classes.
+ class IdentifyParser;
+ class ChangesParser;
+ class UserParser;
+ class DescribeParser;
+ class DiffParser;
+ friend class IdentifyParser;
+ friend class ChangesParser;
+ friend class UserParser;
+ friend class DescribeParser;
+ friend class DiffParser;
+};
+
+#endif
diff --git a/Source/CTest/cmCTestReadCustomFilesCommand.h b/Source/CTest/cmCTestReadCustomFilesCommand.h
index b984c84..9c0af81 100644
--- a/Source/CTest/cmCTestReadCustomFilesCommand.h
+++ b/Source/CTest/cmCTestReadCustomFilesCommand.h
@@ -48,25 +48,6 @@ public:
*/
virtual const char* GetName() const { return "ctest_read_custom_files";}
- /**
- * Succinct documentation.
- */
- virtual const char* GetTerseDocumentation() const
- {
- return "read CTestCustom files.";
- }
-
- /**
- * More documentation.
- */
- virtual const char* GetFullDocumentation() const
- {
- return
- " ctest_read_custom_files( directory ... )\n"
- "Read all the CTestCustom.ctest or CTestCustom.cmake files from "
- "the given directory.";
- }
-
cmTypeMacro(cmCTestReadCustomFilesCommand, cmCTestCommand);
};
diff --git a/Source/CTest/cmCTestRunScriptCommand.h b/Source/CTest/cmCTestRunScriptCommand.h
index 05e7899..f34bd13 100644
--- a/Source/CTest/cmCTestRunScriptCommand.h
+++ b/Source/CTest/cmCTestRunScriptCommand.h
@@ -49,30 +49,6 @@ public:
*/
virtual const char* GetName() const { return "ctest_run_script";}
- /**
- * Succinct documentation.
- */
- virtual const char* GetTerseDocumentation() const
- {
- return "runs a ctest -S script";
- }
-
- /**
- * More documentation.
- */
- virtual const char* GetFullDocumentation() const
- {
- return
- " ctest_run_script([NEW_PROCESS] script_file_name script_file_name1 \n"
- " script_file_name2 ... [RETURN_VALUE var])\n"
- "Runs a script or scripts much like if it was run from ctest -S. "
- "If no argument is provided then the current script is run using "
- "the current settings of the variables. If NEW_PROCESS is specified "
- "then each script will be run in a separate process."
- "If RETURN_VALUE is specified the return value of the last script "
- "run will be put into var.";
- }
-
cmTypeMacro(cmCTestRunScriptCommand, cmCTestCommand);
};
diff --git a/Source/CTest/cmCTestScriptHandler.cxx b/Source/CTest/cmCTestScriptHandler.cxx
index 8643cb3..64bcd59 100644
--- a/Source/CTest/cmCTestScriptHandler.cxx
+++ b/Source/CTest/cmCTestScriptHandler.cxx
@@ -361,12 +361,6 @@ void cmCTestScriptHandler::CreateCMake()
this->AddCTestCommand(new cmCTestUploadCommand);
}
-void cmCTestScriptHandler::GetCommandDocumentation(
- std::vector<cmDocumentationEntry>& v) const
-{
- this->CMake->GetCommandDocumentation(v);
-}
-
//----------------------------------------------------------------------
// this sets up some variables for the script to use, creates the required
// cmake instance and generators, and then reads in the script
diff --git a/Source/CTest/cmCTestScriptHandler.h b/Source/CTest/cmCTestScriptHandler.h
index 9d852ca..80d5831 100644
--- a/Source/CTest/cmCTestScriptHandler.h
+++ b/Source/CTest/cmCTestScriptHandler.h
@@ -110,7 +110,6 @@ public:
void Initialize();
void CreateCMake();
- void GetCommandDocumentation(std::vector<cmDocumentationEntry>& v) const;
cmake* GetCMake() { return this->CMake;}
private:
// reads in a script
diff --git a/Source/CTest/cmCTestSleepCommand.h b/Source/CTest/cmCTestSleepCommand.h
index 0f51ddf..c6baf1c 100644
--- a/Source/CTest/cmCTestSleepCommand.h
+++ b/Source/CTest/cmCTestSleepCommand.h
@@ -49,26 +49,6 @@ public:
*/
virtual const char* GetName() const { return "ctest_sleep";}
- /**
- * Succinct documentation.
- */
- virtual const char* GetTerseDocumentation() const
- {
- return "sleeps for some amount of time";
- }
-
- /**
- * More documentation.
- */
- virtual const char* GetFullDocumentation() const
- {
- return
- " ctest_sleep(<seconds>)\n"
- "Sleep for given number of seconds.\n"
- " ctest_sleep(<time1> <duration> <time2>)\n"
- "Sleep for t=(time1 + duration - time2) seconds if t > 0.";
- }
-
cmTypeMacro(cmCTestSleepCommand, cmCTestCommand);
};
diff --git a/Source/CTest/cmCTestStartCommand.h b/Source/CTest/cmCTestStartCommand.h
index 6be4770..e5535c1 100644
--- a/Source/CTest/cmCTestStartCommand.h
+++ b/Source/CTest/cmCTestStartCommand.h
@@ -57,30 +57,6 @@ public:
*/
virtual const char* GetName() const { return "ctest_start";}
- /**
- * Succinct documentation.
- */
- virtual const char* GetTerseDocumentation() const
- {
- return "Starts the testing for a given model";
- }
-
- /**
- * More documentation.
- */
- virtual const char* GetFullDocumentation() const
- {
- return
- " ctest_start(Model [TRACK <track>] [APPEND] [source [binary]])\n"
- "Starts the testing for a given model. The command should be called "
- "after the binary directory is initialized. If the 'source' and "
- "'binary' directory are not specified, it reads the "
- "CTEST_SOURCE_DIRECTORY and CTEST_BINARY_DIRECTORY. If the track is "
- "specified, the submissions will go to the specified track. "
- "If APPEND is used, the existing TAG is used rather than "
- "creating a new one based on the current time stamp.";
- }
-
cmTypeMacro(cmCTestStartCommand, cmCTestCommand);
private:
diff --git a/Source/CTest/cmCTestSubmitCommand.h b/Source/CTest/cmCTestSubmitCommand.h
index 53ee875..64c6cae 100644
--- a/Source/CTest/cmCTestSubmitCommand.h
+++ b/Source/CTest/cmCTestSubmitCommand.h
@@ -50,44 +50,6 @@ public:
*/
virtual const char* GetName() const { return "ctest_submit";}
- /**
- * Succinct documentation.
- */
- virtual const char* GetTerseDocumentation() const
- {
- return "Submit results to a dashboard server.";
- }
-
- /**
- * More documentation.
- */
- virtual const char* GetFullDocumentation() const
- {
- return
- " ctest_submit([PARTS ...] [FILES ...] [RETRY_COUNT count] "
- " [RETRY_DELAY delay][RETURN_VALUE res])\n"
- "By default all available parts are submitted if no PARTS or FILES "
- "are specified. "
- "The PARTS option lists a subset of parts to be submitted. "
- "Valid part names are:\n"
- " Start = nothing\n"
- " Update = ctest_update results, in Update.xml\n"
- " Configure = ctest_configure results, in Configure.xml\n"
- " Build = ctest_build results, in Build.xml\n"
- " Test = ctest_test results, in Test.xml\n"
- " Coverage = ctest_coverage results, in Coverage.xml\n"
- " MemCheck = ctest_memcheck results, in DynamicAnalysis.xml\n"
- " Notes = Files listed by CTEST_NOTES_FILES, in Notes.xml\n"
- " ExtraFiles = Files listed by CTEST_EXTRA_SUBMIT_FILES\n"
- " Submit = nothing\n"
- "The FILES option explicitly lists specific files to be submitted. "
- "Each individual file must exist at the time of the call.\n"
- "The RETRY_DELAY option specifies how long in seconds to wait after "
- "a timed-out submission before attempting to re-submit.\n"
- "The RETRY_COUNT option specifies how many times to retry a timed-out "
- "submission.\n";
- }
-
cmTypeMacro(cmCTestSubmitCommand, cmCTestHandlerCommand);
protected:
diff --git a/Source/CTest/cmCTestTestCommand.h b/Source/CTest/cmCTestTestCommand.h
index 130cb69..451ac99 100644
--- a/Source/CTest/cmCTestTestCommand.h
+++ b/Source/CTest/cmCTestTestCommand.h
@@ -41,45 +41,6 @@ public:
*/
virtual const char* GetName() const { return "ctest_test";}
- /**
- * Succinct documentation.
- */
- virtual const char* GetTerseDocumentation() const
- {
- return "Run tests in the project build tree.";
- }
-
- /**
- * More documentation.
- */
- virtual const char* GetFullDocumentation() const
- {
- return
- " ctest_test([BUILD build_dir] [APPEND]\n"
- " [START start number] [END end number]\n"
- " [STRIDE stride number] [EXCLUDE exclude regex ]\n"
- " [INCLUDE include regex] [RETURN_VALUE res] \n"
- " [EXCLUDE_LABEL exclude regex] \n"
- " [INCLUDE_LABEL label regex] \n"
- " [PARALLEL_LEVEL level] \n"
- " [SCHEDULE_RANDOM on] \n"
- " [STOP_TIME time of day]) \n"
- "Tests the given build directory and stores results in Test.xml. The "
- "second argument is a variable that will hold value. Optionally, "
- "you can specify the starting test number START, the ending test number "
- "END, the number of tests to skip between each test STRIDE, a regular "
- "expression for tests to run INCLUDE, or a regular expression for tests "
- "to not run EXCLUDE. EXCLUDE_LABEL and INCLUDE_LABEL are regular "
- "expression for test to be included or excluded by the test "
- "property LABEL. PARALLEL_LEVEL should be set to a positive number "
- "representing the number of tests to be run in parallel. "
- "SCHEDULE_RANDOM will launch tests in a random order, and is "
- "typically used to detect implicit test dependencies. STOP_TIME is the "
- "time of day at which the tests should all stop running."
- "\n"
- CTEST_COMMAND_APPEND_OPTION_DOCS;
- }
-
cmTypeMacro(cmCTestTestCommand, cmCTestHandlerCommand);
protected:
diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx
index 497774d..da24ae4 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"
@@ -60,10 +61,6 @@ public:
*/
virtual const char* GetName() const { return "subdirs";}
- // Unused methods
- virtual const char* GetTerseDocumentation() const { return ""; }
- virtual const char* GetFullDocumentation() const { return ""; }
-
cmTypeMacro(cmCTestSubdirCommand, cmCommand);
cmCTestTestHandler* TestHandler;
@@ -161,10 +158,6 @@ public:
*/
virtual const char* GetName() const { return "add_subdirectory";}
- // Unused methods
- virtual const char* GetTerseDocumentation() const { return ""; }
- virtual const char* GetFullDocumentation() const { return ""; }
-
cmTypeMacro(cmCTestAddSubdirectoryCommand, cmCommand);
cmCTestTestHandler* TestHandler;
@@ -249,11 +242,7 @@ public:
/**
* The name of the command as specified in CMakeList.txt.
*/
- virtual const char* GetName() const { return "ADD_TEST";}
-
- // Unused methods
- virtual const char* GetTerseDocumentation() const { return ""; }
- virtual const char* GetFullDocumentation() const { return ""; }
+ virtual const char* GetName() const { return "add_test";}
cmTypeMacro(cmCTestAddTestCommand, cmCommand);
@@ -297,11 +286,7 @@ public:
/**
* The name of the command as specified in CMakeList.txt.
*/
- virtual const char* GetName() const { return "SET_TESTS_PROPERTIES";}
-
- // Unused methods
- virtual const char* GetTerseDocumentation() const { return ""; }
- virtual const char* GetFullDocumentation() const { return ""; }
+ virtual const char* GetName() const { return "set_tests_properties";}
cmTypeMacro(cmCTestSetTestsPropertiesCommand, cmCommand);
@@ -537,6 +522,7 @@ int cmCTestTestHandler::ProcessHandler()
this->UseExcludeRegExp();
this->SetExcludeRegExp(val);
}
+ this->SetRerunFailed(cmSystemTools::IsOn(this->GetOption("RerunFailed")));
this->TestResults.clear();
@@ -819,6 +805,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 +874,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 +1739,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/cmCTestUpdateCommand.cxx b/Source/CTest/cmCTestUpdateCommand.cxx
index 2ca9f6c..5408a8a 100644
--- a/Source/CTest/cmCTestUpdateCommand.cxx
+++ b/Source/CTest/cmCTestUpdateCommand.cxx
@@ -59,6 +59,14 @@ cmCTestGenericHandler* cmCTestUpdateCommand::InitializeHandler()
"HGCommand", "CTEST_HG_COMMAND");
this->CTest->SetCTestConfigurationFromCMakeVariable(this->Makefile,
"HGUpdateOptions", "CTEST_HG_UPDATE_OPTIONS");
+ this->CTest->SetCTestConfigurationFromCMakeVariable(this->Makefile,
+ "P4Command", "CTEST_P4_COMMAND");
+ this->CTest->SetCTestConfigurationFromCMakeVariable(this->Makefile,
+ "P4UpdateOptions", "CTEST_P4_UPDATE_OPTIONS");
+ this->CTest->SetCTestConfigurationFromCMakeVariable(this->Makefile,
+ "P4Client", "CTEST_P4_CLIENT");
+ this->CTest->SetCTestConfigurationFromCMakeVariable(this->Makefile,
+ "P4Options", "CTEST_P4_OPTIONS");
cmCTestGenericHandler* handler
= this->CTest->GetInitializedHandler("update");
diff --git a/Source/CTest/cmCTestUpdateCommand.h b/Source/CTest/cmCTestUpdateCommand.h
index c578fff..a785bd8 100644
--- a/Source/CTest/cmCTestUpdateCommand.h
+++ b/Source/CTest/cmCTestUpdateCommand.h
@@ -41,28 +41,6 @@ public:
*/
virtual const char* GetName() const { return "ctest_update";}
- /**
- * Succinct documentation.
- */
- virtual const char* GetTerseDocumentation() const
- {
- return "Update the work tree from version control.";
- }
-
- /**
- * More documentation.
- */
- virtual const char* GetFullDocumentation() const
- {
- return
- " ctest_update([SOURCE source] [RETURN_VALUE res])\n"
- "Updates the given source directory and stores results in Update.xml. "
- "If no SOURCE is given, the CTEST_SOURCE_DIRECTORY variable is used. "
- "The RETURN_VALUE option specifies a variable in which to store the "
- "result, which is the number of files updated or -1 on error."
- ;
- }
-
cmTypeMacro(cmCTestUpdateCommand, cmCTestHandlerCommand);
protected:
diff --git a/Source/CTest/cmCTestUpdateHandler.cxx b/Source/CTest/cmCTestUpdateHandler.cxx
index 9eae3f3..11474ec 100644
--- a/Source/CTest/cmCTestUpdateHandler.cxx
+++ b/Source/CTest/cmCTestUpdateHandler.cxx
@@ -28,6 +28,7 @@
#include "cmCTestBZR.h"
#include "cmCTestGIT.h"
#include "cmCTestHG.h"
+#include "cmCTestP4.h"
#include <cmsys/auto_ptr.hxx>
@@ -51,7 +52,8 @@ static const char* cmCTestUpdateHandlerUpdateStrings[] =
"SVN",
"BZR",
"GIT",
- "HG"
+ "HG",
+ "P4"
};
static const char* cmCTestUpdateHandlerUpdateToString(int type)
@@ -146,6 +148,10 @@ int cmCTestUpdateHandler::DetermineType(const char* cmd, const char* type)
{
return cmCTestUpdateHandler::e_HG;
}
+ if ( stype.find("p4") != std::string::npos )
+ {
+ return cmCTestUpdateHandler::e_P4;
+ }
}
else
{
@@ -172,6 +178,10 @@ int cmCTestUpdateHandler::DetermineType(const char* cmd, const char* type)
{
return cmCTestUpdateHandler::e_HG;
}
+ if ( stype.find("p4") != std::string::npos )
+ {
+ return cmCTestUpdateHandler::e_P4;
+ }
}
return cmCTestUpdateHandler::e_UNKNOWN;
}
@@ -223,6 +233,7 @@ int cmCTestUpdateHandler::ProcessHandler()
case e_BZR: vc.reset(new cmCTestBZR(this->CTest, ofs)); break;
case e_GIT: vc.reset(new cmCTestGIT(this->CTest, ofs)); break;
case e_HG: vc.reset(new cmCTestHG(this->CTest, ofs)); break;
+ case e_P4: vc.reset(new cmCTestP4(this->CTest, ofs)); break;
default: vc.reset(new cmCTestVC(this->CTest, ofs)); break;
}
vc->SetCommandLineTool(this->UpdateCommand);
@@ -350,6 +361,18 @@ int cmCTestUpdateHandler::DetectVCS(const char* dir)
{
return cmCTestUpdateHandler::e_HG;
}
+ sourceDirectory = dir;
+ sourceDirectory += "/.p4";
+ if ( cmSystemTools::FileExists(sourceDirectory.c_str()) )
+ {
+ return cmCTestUpdateHandler::e_P4;
+ }
+ sourceDirectory = dir;
+ sourceDirectory += "/.p4config";
+ if ( cmSystemTools::FileExists(sourceDirectory.c_str()) )
+ {
+ return cmCTestUpdateHandler::e_P4;
+ }
return cmCTestUpdateHandler::e_UNKNOWN;
}
@@ -380,6 +403,7 @@ bool cmCTestUpdateHandler::SelectVCS()
case e_BZR: key = "BZRCommand"; break;
case e_GIT: key = "GITCommand"; break;
case e_HG: key = "HGCommand"; break;
+ case e_P4: key = "P4Command"; break;
default: break;
}
if (key)
diff --git a/Source/CTest/cmCTestUpdateHandler.h b/Source/CTest/cmCTestUpdateHandler.h
index 55ec974..954c024 100644
--- a/Source/CTest/cmCTestUpdateHandler.h
+++ b/Source/CTest/cmCTestUpdateHandler.h
@@ -44,6 +44,7 @@ public:
e_BZR,
e_GIT,
e_HG,
+ e_P4,
e_LAST
};
diff --git a/Source/CTest/cmCTestUploadCommand.h b/Source/CTest/cmCTestUploadCommand.h
index 62f379f..e867fb6 100644
--- a/Source/CTest/cmCTestUploadCommand.h
+++ b/Source/CTest/cmCTestUploadCommand.h
@@ -45,25 +45,6 @@ public:
*/
virtual const char* GetName() const { return "ctest_upload";}
- /**
- * Succinct documentation.
- */
- virtual const char* GetTerseDocumentation() const
- {
- return "Upload files to a dashboard server.";
- }
-
- /**
- * More documentation.
- */
- virtual const char* GetFullDocumentation() const
- {
- return
- " ctest_upload(FILES ...)\n"
- "Pass a list of files to be sent along with the build results to "
- "the dashboard server.\n";
- }
-
cmTypeMacro(cmCTestUploadCommand, cmCTestHandlerCommand);
protected:
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