| /*============================================================================ |
| CMake - Cross Platform Makefile Generator |
| Copyright 2000-2009 Kitware, Inc., Insight Software Consortium |
| |
| 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 "cmCTestRunTest.h" |
| #include "cmCTestMemCheckHandler.h" |
| #include "cmCTest.h" |
| #include "cmSystemTools.h" |
| #include "cm_curl.h" |
| |
| #include <cm_zlib.h> |
| #include <cmsys/Base64.h> |
| |
| cmCTestRunTest::cmCTestRunTest(cmCTestTestHandler* handler) |
| { |
| this->CTest = handler->CTest; |
| this->TestHandler = handler; |
| this->TestProcess = 0; |
| this->TestResult.ExecutionTime =0; |
| this->TestResult.ReturnValue = 0; |
| this->TestResult.Status = cmCTestTestHandler::NOT_RUN; |
| this->TestResult.TestCount = 0; |
| this->TestResult.Properties = 0; |
| this->ProcessOutput = ""; |
| this->CompressedOutput = ""; |
| this->CompressionRatio = 2; |
| this->StopTimePassed = false; |
| this->NumberOfRunsLeft = 1; // default to 1 run of the test |
| this->RunUntilFail = false; // default to run the test once |
| this->RunAgain = false; // default to not having to run again |
| } |
| |
| cmCTestRunTest::~cmCTestRunTest() |
| { |
| } |
| |
| //---------------------------------------------------------------------------- |
| bool cmCTestRunTest::CheckOutput() |
| { |
| // Read lines for up to 0.1 seconds of total time. |
| double timeout = 0.1; |
| double timeEnd = cmSystemTools::GetTime() + timeout; |
| std::string line; |
| while((timeout = timeEnd - cmSystemTools::GetTime(), timeout > 0)) |
| { |
| int p = this->TestProcess->GetNextOutputLine(line, timeout); |
| if(p == cmsysProcess_Pipe_None) |
| { |
| // Process has terminated and all output read. |
| return false; |
| } |
| else if(p == cmsysProcess_Pipe_STDOUT) |
| { |
| // Store this line of output. |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, |
| this->GetIndex() << ": " << line << std::endl); |
| this->ProcessOutput += line; |
| this->ProcessOutput += "\n"; |
| } |
| else // if(p == cmsysProcess_Pipe_Timeout) |
| { |
| break; |
| } |
| } |
| return true; |
| } |
| |
| //--------------------------------------------------------- |
| // Streamed compression of test output. The compressed data |
| // is appended to this->CompressedOutput |
| void cmCTestRunTest::CompressOutput() |
| { |
| int ret; |
| z_stream strm; |
| |
| unsigned char* in = |
| reinterpret_cast<unsigned char*>( |
| const_cast<char*>(this->ProcessOutput.c_str())); |
| //zlib makes the guarantee that this is the maximum output size |
| int outSize = static_cast<int>( |
| static_cast<double>(this->ProcessOutput.size()) * 1.001 + 13.0); |
| unsigned char* out = new unsigned char[outSize]; |
| |
| strm.zalloc = Z_NULL; |
| strm.zfree = Z_NULL; |
| strm.opaque = Z_NULL; |
| ret = deflateInit(&strm, -1); //default compression level |
| if (ret != Z_OK) |
| { |
| delete[] out; |
| return; |
| } |
| |
| strm.avail_in = static_cast<uInt>(this->ProcessOutput.size()); |
| strm.next_in = in; |
| strm.avail_out = outSize; |
| strm.next_out = out; |
| ret = deflate(&strm, Z_FINISH); |
| |
| if(ret == Z_STREAM_ERROR || ret != Z_STREAM_END) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, "Error during output " |
| "compression. Sending uncompressed output." << std::endl); |
| delete[] out; |
| return; |
| } |
| |
| (void)deflateEnd(&strm); |
| |
| unsigned char *encoded_buffer |
| = new unsigned char[static_cast<int>(outSize * 1.5)]; |
| |
| size_t rlen |
| = cmsysBase64_Encode(out, strm.total_out, encoded_buffer, 1); |
| |
| for(size_t i = 0; i < rlen; i++) |
| { |
| this->CompressedOutput += encoded_buffer[i]; |
| } |
| |
| if(strm.total_in) |
| { |
| this->CompressionRatio = static_cast<double>(strm.total_out) / |
| static_cast<double>(strm.total_in); |
| } |
| |
| delete [] encoded_buffer; |
| delete [] out; |
| } |
| |
| //--------------------------------------------------------- |
| bool cmCTestRunTest::EndTest(size_t completed, size_t total, bool started) |
| { |
| if ((!this->TestHandler->MemCheck && |
| this->CTest->ShouldCompressTestOutput()) || |
| (this->TestHandler->MemCheck && |
| this->CTest->ShouldCompressMemCheckOutput())) |
| { |
| this->CompressOutput(); |
| } |
| |
| this->WriteLogOutputTop(completed, total); |
| std::string reason; |
| bool passed = true; |
| int res = started ? this->TestProcess->GetProcessStatus() |
| : cmsysProcess_State_Error; |
| int retVal = this->TestProcess->GetExitValue(); |
| std::vector<std::pair<cmsys::RegularExpression, |
| std::string> >::iterator passIt; |
| bool forceFail = false; |
| bool outputTestErrorsToConsole = false; |
| if (!this->TestProperties->RequiredRegularExpressions.empty()) |
| { |
| bool found = false; |
| for ( passIt = this->TestProperties->RequiredRegularExpressions.begin(); |
| passIt != this->TestProperties->RequiredRegularExpressions.end(); |
| ++ passIt ) |
| { |
| if ( passIt->first.find(this->ProcessOutput.c_str()) ) |
| { |
| found = true; |
| reason = "Required regular expression found."; |
| break; |
| } |
| } |
| if ( !found ) |
| { |
| reason = "Required regular expression not found."; |
| forceFail = true; |
| } |
| reason += "Regex=["; |
| for ( passIt = this->TestProperties->RequiredRegularExpressions.begin(); |
| passIt != this->TestProperties->RequiredRegularExpressions.end(); |
| ++ passIt ) |
| { |
| reason += passIt->second; |
| reason += "\n"; |
| } |
| reason += "]"; |
| } |
| if (!this->TestProperties->ErrorRegularExpressions.empty()) |
| { |
| for ( passIt = this->TestProperties->ErrorRegularExpressions.begin(); |
| passIt != this->TestProperties->ErrorRegularExpressions.end(); |
| ++ passIt ) |
| { |
| if ( passIt->first.find(this->ProcessOutput.c_str()) ) |
| { |
| reason = "Error regular expression found in output."; |
| reason += " Regex=["; |
| reason += passIt->second; |
| reason += "]"; |
| forceFail = true; |
| break; |
| } |
| } |
| } |
| if (res == cmsysProcess_State_Exited) |
| { |
| bool success = |
| !forceFail && (retVal == 0 || |
| this->TestProperties->RequiredRegularExpressions.size()); |
| if(this->TestProperties->SkipReturnCode >= 0 |
| && this->TestProperties->SkipReturnCode == retVal) |
| { |
| this->TestResult.Status = cmCTestTestHandler::NOT_RUN; |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Skipped "); |
| } |
| else if((success && !this->TestProperties->WillFail) |
| || (!success && this->TestProperties->WillFail)) |
| { |
| this->TestResult.Status = cmCTestTestHandler::COMPLETED; |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, " Passed " ); |
| } |
| else |
| { |
| this->TestResult.Status = cmCTestTestHandler::FAILED; |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Failed " << reason ); |
| outputTestErrorsToConsole = this->CTest->OutputTestOutputOnTestFailure; |
| } |
| } |
| else if ( res == cmsysProcess_State_Expired ) |
| { |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Timeout "); |
| this->TestResult.Status = cmCTestTestHandler::TIMEOUT; |
| outputTestErrorsToConsole = this->CTest->OutputTestOutputOnTestFailure; |
| } |
| else if ( res == cmsysProcess_State_Exception ) |
| { |
| outputTestErrorsToConsole = this->CTest->OutputTestOutputOnTestFailure; |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Exception: "); |
| switch(this->TestProcess->GetExitException()) |
| { |
| case cmsysProcess_Exception_Fault: |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, "SegFault"); |
| this->TestResult.Status = cmCTestTestHandler::SEGFAULT; |
| break; |
| case cmsysProcess_Exception_Illegal: |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, "Illegal"); |
| this->TestResult.Status = cmCTestTestHandler::ILLEGAL; |
| break; |
| case cmsysProcess_Exception_Interrupt: |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, "Interrupt"); |
| this->TestResult.Status = cmCTestTestHandler::INTERRUPT; |
| break; |
| case cmsysProcess_Exception_Numerical: |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, "Numerical"); |
| this->TestResult.Status = cmCTestTestHandler::NUMERICAL; |
| break; |
| default: |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, "Other"); |
| this->TestResult.Status = cmCTestTestHandler::OTHER_FAULT; |
| } |
| } |
| else //cmsysProcess_State_Error |
| { |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Not Run "); |
| } |
| |
| passed = this->TestResult.Status == cmCTestTestHandler::COMPLETED; |
| char buf[1024]; |
| sprintf(buf, "%6.2f sec", this->TestProcess->GetTotalTime()); |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, buf << "\n" ); |
| |
| if ( outputTestErrorsToConsole ) |
| { |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, this->ProcessOutput << std::endl ); |
| } |
| |
| if ( this->TestHandler->LogFile ) |
| { |
| *this->TestHandler->LogFile << "Test time = " << buf << std::endl; |
| } |
| |
| // Set the working directory to the tests directory |
| std::string oldpath = cmSystemTools::GetCurrentWorkingDirectory(); |
| cmSystemTools::ChangeDirectory(this->TestProperties->Directory); |
| |
| this->DartProcessing(); |
| |
| // restore working directory |
| cmSystemTools::ChangeDirectory(oldpath); |
| |
| |
| // if this is doing MemCheck then all the output needs to be put into |
| // Output since that is what is parsed by cmCTestMemCheckHandler |
| if(!this->TestHandler->MemCheck && started) |
| { |
| this->TestHandler->CleanTestOutput(this->ProcessOutput, |
| static_cast<size_t> |
| (this->TestResult.Status == cmCTestTestHandler::COMPLETED ? |
| this->TestHandler->CustomMaximumPassedTestOutputSize : |
| this->TestHandler->CustomMaximumFailedTestOutputSize)); |
| } |
| this->TestResult.Reason = reason; |
| if (this->TestHandler->LogFile) |
| { |
| bool pass = true; |
| const char* reasonType = "Test Pass Reason"; |
| if(this->TestResult.Status != cmCTestTestHandler::COMPLETED && |
| this->TestResult.Status != cmCTestTestHandler::NOT_RUN) |
| { |
| reasonType = "Test Fail Reason"; |
| pass = false; |
| } |
| double ttime = this->TestProcess->GetTotalTime(); |
| int hours = static_cast<int>(ttime / (60 * 60)); |
| int minutes = static_cast<int>(ttime / 60) % 60; |
| int seconds = static_cast<int>(ttime) % 60; |
| char buffer[100]; |
| sprintf(buffer, "%02d:%02d:%02d", hours, minutes, seconds); |
| *this->TestHandler->LogFile |
| << "----------------------------------------------------------" |
| << std::endl; |
| if(!this->TestResult.Reason.empty()) |
| { |
| *this->TestHandler->LogFile << reasonType << ":\n" |
| << this->TestResult.Reason << "\n"; |
| } |
| else |
| { |
| if(pass) |
| { |
| *this->TestHandler->LogFile << "Test Passed.\n"; |
| } |
| else |
| { |
| *this->TestHandler->LogFile << "Test Failed.\n"; |
| } |
| } |
| *this->TestHandler->LogFile << "\"" << this->TestProperties->Name |
| << "\" end time: " << this->CTest->CurrentTime() << std::endl |
| << "\"" << this->TestProperties->Name << "\" time elapsed: " |
| << buffer << std::endl |
| << "----------------------------------------------------------" |
| << std::endl << std::endl; |
| } |
| // if the test actually started and ran |
| // record the results in TestResult |
| if(started) |
| { |
| bool compress = !this->TestHandler->MemCheck && |
| this->CompressionRatio < 1 && |
| this->CTest->ShouldCompressTestOutput(); |
| this->TestResult.Output = compress ? this->CompressedOutput |
| : this->ProcessOutput; |
| this->TestResult.CompressOutput = compress; |
| this->TestResult.ReturnValue = this->TestProcess->GetExitValue(); |
| this->TestResult.CompletionStatus = "Completed"; |
| this->TestResult.ExecutionTime = this->TestProcess->GetTotalTime(); |
| this->MemCheckPostProcess(); |
| this->ComputeWeightedCost(); |
| } |
| // If the test does not need to rerun push the current TestResult onto the |
| // TestHandler vector |
| if(!this->NeedsToRerun()) |
| { |
| this->TestHandler->TestResults.push_back(this->TestResult); |
| } |
| delete this->TestProcess; |
| return passed; |
| } |
| |
| bool cmCTestRunTest::StartAgain() |
| { |
| if(!this->RunAgain) |
| { |
| return false; |
| } |
| this->RunAgain = false; // reset |
| // change to tests directory |
| std::string current_dir = cmSystemTools::GetCurrentWorkingDirectory(); |
| cmSystemTools::ChangeDirectory(this->TestProperties->Directory); |
| this->StartTest(this->TotalNumberOfTests); |
| // change back |
| cmSystemTools::ChangeDirectory(current_dir); |
| return true; |
| } |
| |
| bool cmCTestRunTest::NeedsToRerun() |
| { |
| this->NumberOfRunsLeft--; |
| if(this->NumberOfRunsLeft == 0) |
| { |
| return false; |
| } |
| // if number of runs left is not 0, and we are running until |
| // we find a failed test, then return true so the test can be |
| // restarted |
| if(this->RunUntilFail |
| && this->TestResult.Status == cmCTestTestHandler::COMPLETED) |
| { |
| this->RunAgain = true; |
| return true; |
| } |
| return false; |
| } |
| //---------------------------------------------------------------------- |
| void cmCTestRunTest::ComputeWeightedCost() |
| { |
| double prev = static_cast<double>(this->TestProperties->PreviousRuns); |
| double avgcost = static_cast<double>(this->TestProperties->Cost); |
| double current = this->TestResult.ExecutionTime; |
| |
| if(this->TestResult.Status == cmCTestTestHandler::COMPLETED) |
| { |
| this->TestProperties->Cost = |
| static_cast<float>(((prev * avgcost) + current) / (prev + 1.0)); |
| this->TestProperties->PreviousRuns++; |
| } |
| } |
| |
| //---------------------------------------------------------------------- |
| void cmCTestRunTest::MemCheckPostProcess() |
| { |
| if(!this->TestHandler->MemCheck) |
| { |
| return; |
| } |
| cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, this->Index |
| << ": process test output now: " |
| << this->TestProperties->Name << " " |
| << this->TestResult.Name << std::endl, |
| this->TestHandler->GetQuiet()); |
| cmCTestMemCheckHandler * handler = static_cast<cmCTestMemCheckHandler*> |
| (this->TestHandler); |
| handler->PostProcessTest(this->TestResult, this->Index); |
| } |
| |
| //---------------------------------------------------------------------- |
| // Starts the execution of a test. Returns once it has started |
| bool cmCTestRunTest::StartTest(size_t total) |
| { |
| this->TotalNumberOfTests = total; // save for rerun case |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, std::setw(2*getNumWidth(total) + 8) |
| << "Start " |
| << std::setw(getNumWidth(this->TestHandler->GetMaxIndex())) |
| << this->TestProperties->Index << ": " |
| << this->TestProperties->Name << std::endl); |
| this->ComputeArguments(); |
| std::vector<std::string>& args = this->TestProperties->Args; |
| this->TestResult.Properties = this->TestProperties; |
| this->TestResult.ExecutionTime = 0; |
| this->TestResult.CompressOutput = false; |
| this->TestResult.ReturnValue = -1; |
| this->TestResult.CompletionStatus = "Failed to start"; |
| this->TestResult.Status = cmCTestTestHandler::BAD_COMMAND; |
| this->TestResult.TestCount = this->TestProperties->Index; |
| this->TestResult.Name = this->TestProperties->Name; |
| this->TestResult.Path = this->TestProperties->Directory.c_str(); |
| |
| if(args.size() >= 2 && args[1] == "NOT_AVAILABLE") |
| { |
| this->TestProcess = new cmProcess; |
| std::string msg; |
| if(this->CTest->GetConfigType().empty()) |
| { |
| msg = "Test not available without configuration."; |
| msg += " (Missing \"-C <config>\"?)"; |
| } |
| else |
| { |
| msg = "Test not available in configuration \""; |
| msg += this->CTest->GetConfigType(); |
| msg += "\"."; |
| } |
| *this->TestHandler->LogFile << msg << std::endl; |
| cmCTestLog(this->CTest, ERROR_MESSAGE, msg << std::endl); |
| this->TestResult.Output = msg; |
| this->TestResult.FullCommandLine = ""; |
| this->TestResult.CompletionStatus = "Not Run"; |
| this->TestResult.Status = cmCTestTestHandler::NOT_RUN; |
| return false; |
| } |
| |
| // Check if all required files exist |
| for(std::vector<std::string>::iterator i = |
| this->TestProperties->RequiredFiles.begin(); |
| i != this->TestProperties->RequiredFiles.end(); ++i) |
| { |
| std::string file = *i; |
| |
| if(!cmSystemTools::FileExists(file.c_str())) |
| { |
| //Required file was not found |
| this->TestProcess = new cmProcess; |
| *this->TestHandler->LogFile << "Unable to find required file: " |
| << file << std::endl; |
| cmCTestLog(this->CTest, ERROR_MESSAGE, "Unable to find required file: " |
| << file << std::endl); |
| this->TestResult.Output = "Unable to find required file: " + file; |
| this->TestResult.FullCommandLine = ""; |
| this->TestResult.CompletionStatus = "Not Run"; |
| this->TestResult.Status = cmCTestTestHandler::NOT_RUN; |
| return false; |
| } |
| } |
| // log and return if we did not find the executable |
| if (this->ActualCommand == "") |
| { |
| // if the command was not found create a TestResult object |
| // that has that information |
| this->TestProcess = new cmProcess; |
| *this->TestHandler->LogFile << "Unable to find executable: " |
| << args[1] << std::endl; |
| cmCTestLog(this->CTest, ERROR_MESSAGE, "Unable to find executable: " |
| << args[1] << std::endl); |
| this->TestResult.Output = "Unable to find executable: " + args[1]; |
| this->TestResult.FullCommandLine = ""; |
| this->TestResult.CompletionStatus = "Not Run"; |
| this->TestResult.Status = cmCTestTestHandler::NOT_RUN; |
| return false; |
| } |
| this->StartTime = this->CTest->CurrentTime(); |
| |
| double timeout = this->ResolveTimeout(); |
| |
| if(this->StopTimePassed) |
| { |
| return false; |
| } |
| return this->ForkProcess(timeout, this->TestProperties->ExplicitTimeout, |
| &this->TestProperties->Environment); |
| } |
| |
| //---------------------------------------------------------------------- |
| void cmCTestRunTest::ComputeArguments() |
| { |
| this->Arguments.clear(); // reset becaue this might be a rerun |
| std::vector<std::string>::const_iterator j = |
| this->TestProperties->Args.begin(); |
| ++j; // skip test name |
| // find the test executable |
| if(this->TestHandler->MemCheck) |
| { |
| cmCTestMemCheckHandler * handler = static_cast<cmCTestMemCheckHandler*> |
| (this->TestHandler); |
| this->ActualCommand = handler->MemoryTester.c_str(); |
| this->TestProperties->Args[1] = this->TestHandler->FindTheExecutable( |
| this->TestProperties->Args[1].c_str()); |
| } |
| else |
| { |
| this->ActualCommand = |
| this->TestHandler->FindTheExecutable( |
| this->TestProperties->Args[1].c_str()); |
| ++j; //skip the executable (it will be actualCommand) |
| } |
| std::string testCommand |
| = cmSystemTools::ConvertToOutputPath(this->ActualCommand.c_str()); |
| |
| //Prepends memcheck args to our command string |
| this->TestHandler->GenerateTestCommand(this->Arguments, this->Index); |
| for(std::vector<std::string>::iterator i = this->Arguments.begin(); |
| i != this->Arguments.end(); ++i) |
| { |
| testCommand += " \""; |
| testCommand += *i; |
| testCommand += "\""; |
| } |
| |
| for(;j != this->TestProperties->Args.end(); ++j) |
| { |
| testCommand += " \""; |
| testCommand += *j; |
| testCommand += "\""; |
| this->Arguments.push_back(*j); |
| } |
| this->TestResult.FullCommandLine = testCommand; |
| |
| // Print the test command in verbose mode |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, std::endl |
| << this->Index << ": " |
| << (this->TestHandler->MemCheck?"MemCheck":"Test") |
| << " command: " << testCommand |
| << std::endl); |
| |
| // Print any test-specific env vars in verbose mode |
| if (this->TestProperties->Environment.size()) |
| { |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, this->Index << ": " |
| << "Environment variables: " << std::endl); |
| } |
| for(std::vector<std::string>::const_iterator e = |
| this->TestProperties->Environment.begin(); |
| e != this->TestProperties->Environment.end(); ++e) |
| { |
| cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, this->Index << ": " << *e |
| << std::endl); |
| } |
| } |
| |
| //---------------------------------------------------------------------- |
| void cmCTestRunTest::DartProcessing() |
| { |
| if (!this->ProcessOutput.empty() && |
| this->ProcessOutput.find("<DartMeasurement") != this->ProcessOutput.npos) |
| { |
| if (this->TestHandler->DartStuff.find(this->ProcessOutput.c_str())) |
| { |
| this->TestResult.DartString = this->TestHandler->DartStuff.match(1); |
| // keep searching and replacing until none are left |
| while (this->TestHandler->DartStuff1.find(this->ProcessOutput.c_str())) |
| { |
| // replace the exact match for the string |
| cmSystemTools::ReplaceString(this->ProcessOutput, |
| this->TestHandler->DartStuff1.match(1).c_str(), ""); |
| } |
| } |
| } |
| } |
| |
| //---------------------------------------------------------------------- |
| double cmCTestRunTest::ResolveTimeout() |
| { |
| double timeout = this->TestProperties->Timeout; |
| |
| if(this->CTest->GetStopTime() == "") |
| { |
| return timeout; |
| } |
| struct tm* lctime; |
| time_t current_time = time(0); |
| lctime = gmtime(¤t_time); |
| int gm_hour = lctime->tm_hour; |
| time_t gm_time = mktime(lctime); |
| lctime = localtime(¤t_time); |
| int local_hour = lctime->tm_hour; |
| |
| int tzone_offset = local_hour - gm_hour; |
| if(gm_time > current_time && gm_hour < local_hour) |
| { |
| // this means gm_time is on the next day |
| tzone_offset -= 24; |
| } |
| else if(gm_time < current_time && gm_hour > local_hour) |
| { |
| // this means gm_time is on the previous day |
| tzone_offset += 24; |
| } |
| |
| tzone_offset *= 100; |
| char buf[1024]; |
| // add todays year day and month to the time in str because |
| // curl_getdate no longer assumes the day is today |
| sprintf(buf, "%d%02d%02d %s %+05i", |
| lctime->tm_year + 1900, |
| lctime->tm_mon + 1, |
| lctime->tm_mday, |
| this->CTest->GetStopTime().c_str(), |
| tzone_offset); |
| |
| time_t stop_time = curl_getdate(buf, ¤t_time); |
| if(stop_time == -1) |
| { |
| return timeout; |
| } |
| |
| //the stop time refers to the next day |
| if(this->CTest->NextDayStopTime) |
| { |
| stop_time += 24*60*60; |
| } |
| int stop_timeout = static_cast<int>(stop_time - current_time) % (24*60*60); |
| this->CTest->LastStopTimeout = stop_timeout; |
| |
| if(stop_timeout <= 0 || stop_timeout > this->CTest->LastStopTimeout) |
| { |
| cmCTestLog(this->CTest, ERROR_MESSAGE, "The stop time has been passed. " |
| "Stopping all tests." << std::endl); |
| this->StopTimePassed = true; |
| return 0; |
| } |
| return timeout == 0 ? stop_timeout : |
| (timeout < stop_timeout ? timeout : stop_timeout); |
| } |
| |
| //---------------------------------------------------------------------- |
| bool cmCTestRunTest::ForkProcess(double testTimeOut, bool explicitTimeout, |
| std::vector<std::string>* environment) |
| { |
| this->TestProcess = new cmProcess; |
| this->TestProcess->SetId(this->Index); |
| this->TestProcess->SetWorkingDirectory( |
| this->TestProperties->Directory.c_str()); |
| this->TestProcess->SetCommand(this->ActualCommand.c_str()); |
| this->TestProcess->SetCommandArguments(this->Arguments); |
| |
| // determine how much time we have |
| double timeout = this->CTest->GetRemainingTimeAllowed() - 120; |
| if (this->CTest->GetTimeOut() > 0 && this->CTest->GetTimeOut() < timeout) |
| { |
| timeout = this->CTest->GetTimeOut(); |
| } |
| if (testTimeOut > 0 |
| && testTimeOut < this->CTest->GetRemainingTimeAllowed()) |
| { |
| timeout = testTimeOut; |
| } |
| // always have at least 1 second if we got to here |
| if (timeout <= 0) |
| { |
| timeout = 1; |
| } |
| // handle timeout explicitly set to 0 |
| if (testTimeOut == 0 && explicitTimeout) |
| { |
| timeout = 0; |
| } |
| cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, this->Index << ": " |
| << "Test timeout computed to be: " << timeout << "\n", |
| this->TestHandler->GetQuiet()); |
| |
| this->TestProcess->SetTimeout(timeout); |
| |
| #ifdef CMAKE_BUILD_WITH_CMAKE |
| cmSystemTools::SaveRestoreEnvironment sre; |
| #endif |
| |
| if (environment && !environment->empty()) |
| { |
| cmSystemTools::AppendEnv(*environment); |
| } |
| |
| return this->TestProcess->StartProcess(); |
| } |
| |
| void cmCTestRunTest::WriteLogOutputTop(size_t completed, size_t total) |
| { |
| // if this is the last or only run of this test |
| // then print out completed / total |
| // Only issue is if a test fails and we are running until fail |
| // then it will never print out the completed / total, same would |
| // got for run until pass. Trick is when this is called we don't |
| // yet know if we are passing or failing. |
| if(this->NumberOfRunsLeft == 1) |
| { |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, std::setw(getNumWidth(total)) |
| << completed << "/"); |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, std::setw(getNumWidth(total)) |
| << total << " "); |
| } |
| // if this is one of several runs of a test just print blank space |
| // to keep things neat |
| else |
| { |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, std::setw(getNumWidth(total)) |
| << " " << " "); |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, std::setw(getNumWidth(total)) |
| << " " << " "); |
| } |
| |
| if ( this->TestHandler->MemCheck ) |
| { |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, "MemCheck"); |
| } |
| else |
| { |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, "Test"); |
| } |
| |
| std::ostringstream indexStr; |
| indexStr << " #" << this->Index << ":"; |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, |
| std::setw(3 + getNumWidth(this->TestHandler->GetMaxIndex())) |
| << indexStr.str()); |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, " "); |
| const int maxTestNameWidth = this->CTest->GetMaxTestNameWidth(); |
| std::string outname = this->TestProperties->Name + " "; |
| outname.resize(maxTestNameWidth + 4, '.'); |
| |
| *this->TestHandler->LogFile << this->TestProperties->Index << "/" |
| << this->TestHandler->TotalNumberOfTests << " Testing: " |
| << this->TestProperties->Name << std::endl; |
| *this->TestHandler->LogFile << this->TestProperties->Index << "/" |
| << this->TestHandler->TotalNumberOfTests |
| << " Test: " << this->TestProperties->Name << std::endl; |
| *this->TestHandler->LogFile << "Command: \"" << this->ActualCommand << "\""; |
| |
| for (std::vector<std::string>::iterator i = this->Arguments.begin(); |
| i != this->Arguments.end(); ++i) |
| { |
| *this->TestHandler->LogFile |
| << " \"" << *i << "\""; |
| } |
| *this->TestHandler->LogFile << std::endl |
| << "Directory: " << this->TestProperties->Directory << std::endl |
| << "\"" << this->TestProperties->Name << "\" start time: " |
| << this->StartTime << std::endl; |
| |
| *this->TestHandler->LogFile |
| << "Output:" << std::endl |
| << "----------------------------------------------------------" |
| << std::endl; |
| *this->TestHandler->LogFile |
| << this->ProcessOutput << "<end of output>" << std::endl; |
| |
| cmCTestLog(this->CTest, HANDLER_OUTPUT, outname.c_str()); |
| cmCTestLog(this->CTest, DEBUG, "Testing " |
| << this->TestProperties->Name << " ... "); |
| } |