diff options
Diffstat (limited to 'Source/CTest/cmCTestTestHandler.cxx')
-rw-r--r-- | Source/CTest/cmCTestTestHandler.cxx | 438 |
1 files changed, 374 insertions, 64 deletions
diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx index fe3c3ae..2db4d6d 100644 --- a/Source/CTest/cmCTestTestHandler.cxx +++ b/Source/CTest/cmCTestTestHandler.cxx @@ -16,7 +16,7 @@ =========================================================================*/ #include "cmCTestTestHandler.h" - +#include "cmCTestMultiProcessHandler.h" #include "cmCTest.h" #include "cmake.h" #include "cmGeneratedFileStream.h" @@ -500,11 +500,14 @@ int cmCTestTestHandler::ProcessHandler() } this->TestResults.clear(); - - cmCTestLog(this->CTest, HANDLER_OUTPUT, - (this->MemCheck ? "Memory check" : "Test") - << " project " << cmSystemTools::GetCurrentWorkingDirectory() - << std::endl); + // do not output startup if this is a sub-process for parallel tests + if(!this->CTest->GetParallelSubprocess()) + { + cmCTestLog(this->CTest, HANDLER_OUTPUT, + (this->MemCheck ? "Memory check" : "Test") + << " project " << cmSystemTools::GetCurrentWorkingDirectory() + << std::endl); + } if ( ! this->PreProcessHandler() ) { return -1; @@ -517,7 +520,6 @@ int cmCTestTestHandler::ProcessHandler() std::vector<cmStdString> passed; std::vector<cmStdString> failed; int total; - this->ProcessDirectory(passed, failed); total = int(passed.size()) + int(failed.size()); @@ -550,33 +552,37 @@ int cmCTestTestHandler::ProcessHandler() { percent = 99; } - cmCTestLog(this->CTest, HANDLER_OUTPUT, std::endl - << static_cast<int>(percent + .5) << "% tests passed, " - << failed.size() << " tests failed out of " << total << std::endl); - //fprintf(stderr,"\n%.0f%% tests passed, %i tests failed out of %i\n", - // percent, int(failed.size()), total); + if(!this->CTest->GetParallelSubprocess()) + { + cmCTestLog(this->CTest, HANDLER_OUTPUT, std::endl + << static_cast<int>(percent + .5) << "% tests passed, " + << failed.size() << " tests failed out of " + << total << std::endl); + } if (failed.size()) { cmGeneratedFileStream ofs; - - cmCTestLog(this->CTest, ERROR_MESSAGE, std::endl - << "The following tests FAILED:" << std::endl); - this->StartLogFile("TestsFailed", ofs); - - std::vector<cmCTestTestHandler::cmCTestTestResult>::iterator ftit; - for(ftit = this->TestResults.begin(); - ftit != this->TestResults.end(); ++ftit) + if(!this->CTest->GetParallelSubprocess()) { - if ( ftit->Status != cmCTestTestHandler::COMPLETED ) + cmCTestLog(this->CTest, ERROR_MESSAGE, std::endl + << "The following tests FAILED:" << std::endl); + this->StartLogFile("TestsFailed", ofs); + + std::vector<cmCTestTestHandler::cmCTestTestResult>::iterator ftit; + for(ftit = this->TestResults.begin(); + ftit != this->TestResults.end(); ++ftit) { - ofs << ftit->TestCount << ":" << ftit->Name << std::endl; - cmCTestLog(this->CTest, HANDLER_OUTPUT, "\t" << std::setw(3) - << ftit->TestCount << " - " << ftit->Name.c_str() << " (" - << this->GetTestStatus(ftit->Status) << ")" << std::endl); + if ( ftit->Status != cmCTestTestHandler::COMPLETED ) + { + ofs << ftit->TestCount << ":" << ftit->Name << std::endl; + cmCTestLog(this->CTest, HANDLER_OUTPUT, "\t" << std::setw(3) + << ftit->TestCount << " - " << ftit->Name.c_str() << " (" + << this->GetTestStatus(ftit->Status) << ")" << std::endl); + } } + } - } } @@ -677,8 +683,8 @@ void cmCTestTestHandler::ProcessOneTest(cmCTestTestProperties *it, // add the arguments std::vector<std::string>::const_iterator j = args.begin(); - ++j; - ++j; + ++j; // skip test name + ++j; // skip command as it is in actualCommand std::vector<const char*> arguments; this->GenerateTestCommand(arguments); arguments.push_back(actualCommand.c_str()); @@ -917,23 +923,20 @@ void cmCTestTestHandler::ProcessOneTest(cmCTestTestProperties *it, } //---------------------------------------------------------------------- -void cmCTestTestHandler::ProcessDirectory(std::vector<cmStdString> &passed, - std::vector<cmStdString> &failed) +void cmCTestTestHandler::ComputeTestList() { - std::string current_dir = cmSystemTools::GetCurrentWorkingDirectory(); - this->TestList.clear(); - - this->GetListOfTests(); + this->TestList.clear(); // clear list of test + if(this->CTest->GetParallelSubprocess()) + { + this->LoadTestList(); + return; + } + else + { + this->GetListOfTests(); + } cmCTestTestHandler::ListOfTests::size_type tmsize = this->TestList.size(); - this->StartTest = this->CTest->CurrentTime(); - this->StartTestTime = static_cast<unsigned int>(cmSystemTools::GetTime()); - double elapsed_time_start = cmSystemTools::GetTime(); - - *this->LogFile << "Start testing: " << this->StartTest << std::endl - << "----------------------------------------------------------" - << std::endl; - // how many tests are in based on RegExp? int inREcnt = 0; cmCTestTestHandler::ListOfTests::iterator it; @@ -953,10 +956,11 @@ void cmCTestTestHandler::ProcessDirectory(std::vector<cmStdString> &passed, { this->ExpandTestsToRunInformation(inREcnt); } - + // Now create a final list of tests to run int cnt = 0; inREcnt = 0; - std::string last_directory = ""; + std::string last_directory = ""; + ListOfTests finalList; for ( it = this->TestList.begin(); it != this->TestList.end(); it ++ ) { cnt ++; @@ -965,23 +969,6 @@ void cmCTestTestHandler::ProcessDirectory(std::vector<cmStdString> &passed, inREcnt++; } - // if we are out of time then skip this test, we leave two minutes - // to submit results - if (this->CTest->GetRemainingTimeAllowed() - 120 <= 0) - { - continue; - } - - if (!(last_directory == it->Directory)) - { - cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, - "Changing directory into " << it->Directory.c_str() << "\n"); - *this->LogFile << "Changing directory into: " << it->Directory.c_str() - << std::endl; - last_directory = it->Directory; - cmSystemTools::ChangeDirectory(it->Directory.c_str()); - } - if (this->UseUnion) { // if it is not in the list and not in the regexp then skip @@ -1003,10 +990,322 @@ void cmCTestTestHandler::ProcessDirectory(std::vector<cmStdString> &passed, continue; } } - + it->Index = cnt; // save the index into the test list for this test + finalList.push_back(*it); + } + // Save the total number of tests before exclusions + this->TotalNumberOfTests = this->TestList.size(); + // Set the TestList to the final list of all test + this->TestList = finalList; +} + +bool cmCTestTestHandler::GetValue(const char* tag, + int& value, + std::ifstream& fin) +{ + std::string line; + cmSystemTools::GetLineFromStream(fin, line); + if(line == tag) + { + fin >> value; + cmSystemTools::GetLineFromStream(fin, line); // read blank line + } + else + { + cmCTestLog(this->CTest, ERROR_MESSAGE, + "parse error: missing tag: " + << tag << " found [" << line << "]" << std::endl); + return false; + } + return true; +} + +bool cmCTestTestHandler::GetValue(const char* tag, + double& value, + std::ifstream& fin) +{ + std::string line; + cmSystemTools::GetLineFromStream(fin, line); + if(line == tag) + { + fin >> value; + cmSystemTools::GetLineFromStream(fin, line); // read blank line + } + else + { + cmCTestLog(this->CTest, ERROR_MESSAGE, + "parse error: missing tag: " + << tag << " found [" << line << "]" << std::endl); + return false; + } + return true; +} + +bool cmCTestTestHandler::GetValue(const char* tag, + bool& value, + std::ifstream& fin) +{ + std::string line; + cmSystemTools::GetLineFromStream(fin, line); + if(line == tag) + { + fin >> value; + cmSystemTools::GetLineFromStream(fin, line); // read blank line + } + else + { + cmCTestLog(this->CTest, ERROR_MESSAGE, + "parse error: missing tag: " + << tag << " found [" << line << "]" << std::endl); + return false; + } + return true; +} + +bool cmCTestTestHandler::GetValue(const char* tag, + size_t& value, + std::ifstream& fin) +{ + std::string line; + cmSystemTools::GetLineFromStream(fin, line); + if(line == tag) + { + fin >> value; + cmSystemTools::GetLineFromStream(fin, line); // read blank line + } + else + { + cmCTestLog(this->CTest, ERROR_MESSAGE, + "parse error: missing tag: " + << tag << " found [" << line.c_str() << "]" << std::endl); + return false; + } + return true; +} + +bool cmCTestTestHandler::GetValue(const char* tag, + std::string& value, + std::ifstream& fin) +{ + std::string line; + cmSystemTools::GetLineFromStream(fin, line); + if(line == tag) + { + return cmSystemTools::GetLineFromStream(fin, value); + } + else + { + cmCTestLog(this->CTest, ERROR_MESSAGE, + "parse error: missing tag: " + << tag << " found [" << line << "]" << std::endl); + return false; + } + return true; +} + + +// This should load only one test and is used in -j N mode. +// it is used by the sub-process ctest runs which should have +// only one -I N test to run. +void cmCTestTestHandler::LoadTestList() +{ + this->TestList.clear(); + std::string fname = this->CTest->GetBinaryDir() + + "/Testing/Temporary/PCache.txt"; + std::ifstream fin(fname.c_str()); + std::string line; + if(!fin) + { + cmCTestLog(this->CTest, ERROR_MESSAGE, + "Could not load PCache.txt file: " + << fname.c_str() << std::endl); + return; + } + bool ok = true; + int numTestsToRun; + ok = ok && this->GetValue("TotalNumberOfTests:", + this->TotalNumberOfTests, fin); + ok = ok && this->GetValue("NumberOfTestsToRun:", numTestsToRun, fin); + this->ExpandTestsToRunInformation(this->TotalNumberOfTests); + if(this->TestsToRun.size() != 1) + { + cmCTestLog(this->CTest, ERROR_MESSAGE, + "Error when in parallel mode only one test should be run: " + << this->TestsToRun.size() << std::endl); + } + int testIndexToRun = this->TestsToRun[0]; + this->CTest->SetParallelSubprocessId(testIndexToRun); + if(!ok) + { + return; + } + for(int i =0; i < numTestsToRun; i++) + { + cmCTestTestProperties p; + int numArgs; + bool ok = this->GetValue("Name:", p.Name, fin); + ok = ok && this->GetValue("Directory:", p.Directory, fin); + ok = ok && this->GetValue("Args:", numArgs, fin); + for(int j =0; j < numArgs; ++j) + { + cmSystemTools::GetLineFromStream(fin, line); + p.Args.push_back(line); + } + int numDep = 0; + ok = ok && this->GetValue("Depends:", numDep, fin); + for(int j =0; j < numDep; ++j) + { + cmSystemTools::GetLineFromStream(fin, line); + p.Depends.push_back(line); + } + int isinre; + ok = ok && this->GetValue("IsInBasedOnREOptions:", isinre, fin); + ok = ok && this->GetValue("WillFail:", p.WillFail, fin); + ok = ok && this->GetValue("TimeOut:", p.Timeout, fin); + ok = ok && this->GetValue("Index:", p.Index, fin); + if(!ok) + { + return; + } + if(p.Index == testIndexToRun) + { + // add the one test and stop reading + this->TestList.push_back(p); + return; + } + } +} +std::string cmCTestTestHandler::SaveTestList() +{ + std::string fname = this->CTest->GetBinaryDir() + + "/Testing/Temporary/PCache.txt"; + cmGeneratedFileStream fout(fname.c_str()); + if(!fout) + { + cmCTestLog(this->CTest, ERROR_MESSAGE, std::endl + << "Could not open PCache.txt for write:" + << fname.c_str() + << std::endl); + } + fout << "TotalNumberOfTests:\n"; + fout << this->TotalNumberOfTests << "\n"; + fout << "NumberOfTestsToRun:\n"; + fout << this->TestList.size() << "\n"; + for (ListOfTests::iterator it = this->TestList.begin(); + it != this->TestList.end(); it ++ ) + { + cmCTestTestProperties& p = *it; + fout << "Name:\n" + << p.Name.c_str() + << "\nDirectory:\n" + << p.Directory.c_str() + << "\nArgs:\n" + << p.Args.size() << "\n"; + for(std::vector<std::string>::iterator i = p.Args.begin(); + i != p.Args.end(); ++i) + { + fout << i->c_str() << "\n"; + } + fout << "Depends:\n" << p.Depends.size() << "\n"; + for(std::vector<std::string>::iterator i = p.Depends.begin(); + i != p.Depends.end(); ++i) + { + fout << i->c_str() << "\n"; + } + fout << "IsInBasedOnREOptions:\n" + << p.IsInBasedOnREOptions + << "\nWillFail:\n" + << p.WillFail + << "\nTimeOut:\n" + << p.Timeout + << "\nIndex:\n" + << p.Index << "\n"; + } + fout.close(); + return fname; +} + +void cmCTestTestHandler::ProcessParallel(std::vector<cmStdString> &passed, + std::vector<cmStdString> &failed) +{ + this->ComputeTestList(); + cmCTestMultiProcessHandler parallel; + parallel.SetCTest(this->CTest); + parallel.SetParallelLevel(this->CTest->GetParallelLevel()); + std::set<int> depends; + std::map<int, std::set<int> > tests; + std::map<int, cmStdString> testnames; + for (ListOfTests::iterator it = this->TestList.begin(); + it != this->TestList.end(); it ++ ) + { + cmCTestTestProperties& p = *it; + testnames[p.Index] = p.Name; + if(p.Depends.size()) + { + for(std::vector<std::string>::iterator i = p.Depends.begin(); + i != p.Depends.end(); ++i) + { + for(ListOfTests::iterator it2 = this->TestList.begin(); + it2 != this->TestList.end(); it2 ++ ) + { + if(it2->Name == *i) + { + depends.insert(it2->Index); + break; // break out of test loop as name can only match 1 + } + } + } + } + tests[it->Index] = depends; + } + parallel.SetCTestCommand(this->CTest->GetCTestExecutable()); + parallel.SetTests(tests, testnames); + std::string fname = this->SaveTestList(); + parallel.SetTestCacheFile(fname.c_str()); + parallel.SetPassFailVectors(&passed, &failed); + this->TestResults.clear(); + parallel.SetTestResults(&this->TestResults); + parallel.RunTests(); + cmSystemTools::RemoveFile(fname.c_str()); +} + + +//---------------------------------------------------------------------- +void cmCTestTestHandler::ProcessDirectory(std::vector<cmStdString> &passed, + std::vector<cmStdString> &failed) +{ + if(this->CTest->GetParallelLevel() > 0) + { + this->ProcessParallel(passed, failed); + return; + } + // save the current working directory + std::string current_dir = cmSystemTools::GetCurrentWorkingDirectory(); + // compute the list of tests to run + this->ComputeTestList(); + this->StartTest = this->CTest->CurrentTime(); + this->StartTestTime = static_cast<unsigned int>(cmSystemTools::GetTime()); + double elapsed_time_start = cmSystemTools::GetTime(); + *this->LogFile << "Start testing: " << this->StartTest << std::endl + << "----------------------------------------------------------" + << std::endl; + std::string last_directory = ""; + // run each test + for (ListOfTests::iterator it = this->TestList.begin(); + it != this->TestList.end(); it ++ ) + { + if (!(last_directory == it->Directory)) + { + cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, + "Changing directory into " << it->Directory.c_str() << "\n"); + *this->LogFile << "Changing directory into: " << it->Directory.c_str() + << std::endl; + last_directory = it->Directory; + cmSystemTools::ChangeDirectory(it->Directory.c_str()); + } // process this one test - this->ProcessOneTest(&(*it), passed, failed, cnt, - static_cast<int>(tmsize)); + this->ProcessOneTest(&(*it), passed, failed, it->Index, + static_cast<int>(this->TotalNumberOfTests)); } this->EndTest = this->CTest->CurrentTime(); @@ -1890,6 +2189,17 @@ bool cmCTestTestHandler::SetTestsProperties( std::string(crit->c_str()))); } } + if ( key == "DEPENDS" ) + { + std::vector<std::string> lval; + cmSystemTools::ExpandListArgument(val.c_str(), lval); + std::vector<std::string>::iterator crit; + for ( crit = lval.begin(); crit != lval.end(); ++ crit ) + { + cmCTestTestProperties* tp = &(*rtit); + rtit->Depends.push_back(*crit); + } + } if ( key == "MEASUREMENT" ) { size_t pos = val.find_first_of("="); |