diff options
Diffstat (limited to 'Source/CTest')
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 ¤tSet = 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 |