1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 #include "cmCTestRunTest.h"
7 #include <cstddef> // IWYU pragma: keep
18 #include "cmsys/RegularExpression.hxx"
21 #include "cmCTestMemCheckHandler.h"
22 #include "cmCTestMultiProcessHandler.h"
23 #include "cmProcess.h"
24 #include "cmStringAlgorithms.h"
25 #include "cmSystemTools.h"
26 #include "cmWorkingDirectory.h"
28 cmCTestRunTest::cmCTestRunTest(cmCTestMultiProcessHandler& multiHandler)
29 : MultiTestHandler(multiHandler)
31 this->CTest = multiHandler.CTest;
32 this->TestHandler = multiHandler.TestHandler;
33 this->TestResult.ExecutionTime = cmDuration::zero();
34 this->TestResult.ReturnValue = 0;
35 this->TestResult.Status = cmCTestTestHandler::NOT_RUN;
36 this->TestResult.TestCount = 0;
37 this->TestResult.Properties = nullptr;
40 void cmCTestRunTest::CheckOutput(std::string const& line)
42 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
43 this->GetIndex() << ": " << line << std::endl);
45 // Check for special CTest XML tags in this line of output.
46 // If any are found, this line is excluded from ProcessOutput.
47 if (!line.empty() && line.find("<CTest") != std::string::npos) {
48 bool ctest_tag_found = false;
49 if (this->TestHandler->CustomCompletionStatusRegex.find(line)) {
50 ctest_tag_found = true;
51 this->TestResult.CustomCompletionStatus =
52 this->TestHandler->CustomCompletionStatusRegex.match(1);
53 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
54 this->GetIndex() << ": "
55 << "Test Details changed to '"
56 << this->TestResult.CustomCompletionStatus
58 } else if (this->TestHandler->CustomLabelRegex.find(line)) {
59 ctest_tag_found = true;
60 auto label = this->TestHandler->CustomLabelRegex.match(1);
61 auto& labels = this->TestProperties->Labels;
62 if (std::find(labels.begin(), labels.end(), label) == labels.end()) {
63 labels.push_back(label);
64 std::sort(labels.begin(), labels.end());
65 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
68 << "Test Label added: '" << label << "'" << std::endl);
71 if (ctest_tag_found) {
76 this->ProcessOutput += line;
77 this->ProcessOutput += "\n";
79 // Check for TIMEOUT_AFTER_MATCH property.
80 if (!this->TestProperties->TimeoutRegularExpressions.empty()) {
81 for (auto& reg : this->TestProperties->TimeoutRegularExpressions) {
82 if (reg.first.find(this->ProcessOutput)) {
83 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
86 << "Test timeout changed to "
87 << std::chrono::duration_cast<std::chrono::seconds>(
88 this->TestProperties->AlternateTimeout)
91 this->TestProcess->ResetStartTime();
92 this->TestProcess->ChangeTimeout(
93 this->TestProperties->AlternateTimeout);
94 this->TestProperties->TimeoutRegularExpressions.clear();
101 bool cmCTestRunTest::EndTest(size_t completed, size_t total, bool started)
103 this->WriteLogOutputTop(completed, total);
106 cmProcess::State res =
107 started ? this->TestProcess->GetProcessStatus() : cmProcess::State::Error;
108 if (res != cmProcess::State::Expired) {
109 this->TimeoutIsForStopTime = false;
111 std::int64_t retVal = this->TestProcess->GetExitValue();
112 bool forceFail = false;
113 bool forceSkip = false;
114 bool skipped = false;
115 bool outputTestErrorsToConsole = false;
116 if (!this->TestProperties->RequiredRegularExpressions.empty() &&
117 this->FailedDependencies.empty()) {
119 for (auto& pass : this->TestProperties->RequiredRegularExpressions) {
120 if (pass.first.find(this->ProcessOutput)) {
122 reason = cmStrCat("Required regular expression found. Regex=[",
128 reason = "Required regular expression not found. Regex=[";
129 for (auto& pass : this->TestProperties->RequiredRegularExpressions) {
130 reason += pass.second;
137 if (!this->TestProperties->ErrorRegularExpressions.empty() &&
138 this->FailedDependencies.empty()) {
139 for (auto& fail : this->TestProperties->ErrorRegularExpressions) {
140 if (fail.first.find(this->ProcessOutput)) {
141 reason = cmStrCat("Error regular expression found in output. Regex=[",
148 if (!this->TestProperties->SkipRegularExpressions.empty() &&
149 this->FailedDependencies.empty()) {
150 for (auto& skip : this->TestProperties->SkipRegularExpressions) {
151 if (skip.first.find(this->ProcessOutput)) {
152 reason = cmStrCat("Skip regular expression found in output. Regex=[",
159 std::ostringstream outputStream;
160 if (res == cmProcess::State::Exited) {
161 bool success = !forceFail &&
163 !this->TestProperties->RequiredRegularExpressions.empty());
164 if ((this->TestProperties->SkipReturnCode >= 0 &&
165 this->TestProperties->SkipReturnCode == retVal) ||
167 this->TestResult.Status = cmCTestTestHandler::NOT_RUN;
168 std::ostringstream s;
170 s << "SKIP_REGULAR_EXPRESSION_MATCHED";
172 s << "SKIP_RETURN_CODE=" << this->TestProperties->SkipReturnCode;
174 this->TestResult.CompletionStatus = s.str();
175 outputStream << "***Skipped ";
177 } else if (success != this->TestProperties->WillFail) {
178 this->TestResult.Status = cmCTestTestHandler::COMPLETED;
179 outputStream << " Passed ";
181 this->TestResult.Status = cmCTestTestHandler::FAILED;
182 outputStream << "***Failed " << reason;
183 outputTestErrorsToConsole =
184 this->CTest->GetOutputTestOutputOnTestFailure();
186 } else if (res == cmProcess::State::Expired) {
187 outputStream << "***Timeout ";
188 this->TestResult.Status = cmCTestTestHandler::TIMEOUT;
189 outputTestErrorsToConsole =
190 this->CTest->GetOutputTestOutputOnTestFailure();
191 } else if (res == cmProcess::State::Exception) {
192 outputTestErrorsToConsole =
193 this->CTest->GetOutputTestOutputOnTestFailure();
194 outputStream << "***Exception: ";
195 this->TestResult.ExceptionStatus =
196 this->TestProcess->GetExitExceptionString();
197 switch (this->TestProcess->GetExitException()) {
198 case cmProcess::Exception::Fault:
199 outputStream << "SegFault";
200 this->TestResult.Status = cmCTestTestHandler::SEGFAULT;
202 case cmProcess::Exception::Illegal:
203 outputStream << "Illegal";
204 this->TestResult.Status = cmCTestTestHandler::ILLEGAL;
206 case cmProcess::Exception::Interrupt:
207 outputStream << "Interrupt";
208 this->TestResult.Status = cmCTestTestHandler::INTERRUPT;
210 case cmProcess::Exception::Numerical:
211 outputStream << "Numerical";
212 this->TestResult.Status = cmCTestTestHandler::NUMERICAL;
215 cmCTestLog(this->CTest, HANDLER_OUTPUT,
216 this->TestResult.ExceptionStatus);
217 this->TestResult.Status = cmCTestTestHandler::OTHER_FAULT;
219 } else if ("Disabled" == this->TestResult.CompletionStatus) {
220 outputStream << "***Not Run (Disabled) ";
221 } else // cmProcess::State::Error
223 outputStream << "***Not Run ";
226 passed = this->TestResult.Status == cmCTestTestHandler::COMPLETED;
228 snprintf(buf, sizeof(buf), "%6.2f sec",
229 this->TestProcess->GetTotalTime().count());
230 outputStream << buf << "\n";
232 bool passedOrSkipped = passed || skipped;
233 if (this->CTest->GetTestProgressOutput()) {
234 if (!passedOrSkipped) {
235 // If the test did not pass, reprint test name and error
236 std::string output = this->GetTestPrefix(completed, total);
237 std::string testName = this->TestProperties->Name;
238 const int maxTestNameWidth = this->CTest->GetMaxTestNameWidth();
239 testName.resize(maxTestNameWidth + 4, '.');
242 output += outputStream.str();
243 outputStream.str("");
244 outputStream.clear();
245 outputStream << output;
246 cmCTestLog(this->CTest, HANDLER_TEST_PROGRESS_OUTPUT, "\n"); // flush
248 if (completed == total) {
249 std::string testName = this->GetTestPrefix(completed, total) +
250 this->TestProperties->Name + "\n";
251 cmCTestLog(this->CTest, HANDLER_TEST_PROGRESS_OUTPUT, testName);
254 if (!this->CTest->GetTestProgressOutput() || !passedOrSkipped) {
255 cmCTestLog(this->CTest, HANDLER_OUTPUT, outputStream.str());
258 if (outputTestErrorsToConsole) {
259 cmCTestLog(this->CTest, HANDLER_OUTPUT, this->ProcessOutput << std::endl);
262 if (this->TestHandler->LogFile) {
263 *this->TestHandler->LogFile << "Test time = " << buf << std::endl;
266 this->ParseOutputForMeasurements();
268 // if this is doing MemCheck then all the output needs to be put into
269 // Output since that is what is parsed by cmCTestMemCheckHandler
270 if (!this->TestHandler->MemCheck && started) {
271 this->TestHandler->CleanTestOutput(
274 this->TestResult.Status == cmCTestTestHandler::COMPLETED
275 ? this->TestHandler->CustomMaximumPassedTestOutputSize
276 : this->TestHandler->CustomMaximumFailedTestOutputSize),
277 this->TestHandler->TestOutputTruncation);
279 this->TestResult.Reason = reason;
280 if (this->TestHandler->LogFile) {
282 const char* reasonType = "Test Pass Reason";
283 if (this->TestResult.Status != cmCTestTestHandler::COMPLETED &&
284 this->TestResult.Status != cmCTestTestHandler::NOT_RUN) {
285 reasonType = "Test Fail Reason";
288 auto ttime = this->TestProcess->GetTotalTime();
289 auto hours = std::chrono::duration_cast<std::chrono::hours>(ttime);
291 auto minutes = std::chrono::duration_cast<std::chrono::minutes>(ttime);
293 auto seconds = std::chrono::duration_cast<std::chrono::seconds>(ttime);
295 snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d",
296 static_cast<unsigned>(hours.count()),
297 static_cast<unsigned>(minutes.count()),
298 static_cast<unsigned>(seconds.count()));
299 *this->TestHandler->LogFile
300 << "----------------------------------------------------------"
302 if (!this->TestResult.Reason.empty()) {
303 *this->TestHandler->LogFile << reasonType << ":\n"
304 << this->TestResult.Reason << "\n";
307 *this->TestHandler->LogFile << "Test Passed.\n";
309 *this->TestHandler->LogFile << "Test Failed.\n";
312 *this->TestHandler->LogFile
313 << "\"" << this->TestProperties->Name
314 << "\" end time: " << this->CTest->CurrentTime() << std::endl
315 << "\"" << this->TestProperties->Name << "\" time elapsed: " << buffer
317 << "----------------------------------------------------------"
321 // if the test actually started and ran
322 // record the results in TestResult
324 std::string compressedOutput;
325 if (!this->TestHandler->MemCheck &&
326 this->CTest->ShouldCompressTestOutput()) {
327 std::string str = this->ProcessOutput;
328 if (this->CTest->CompressString(str)) {
329 compressedOutput = std::move(str);
332 bool compress = !compressedOutput.empty() &&
333 compressedOutput.length() < this->ProcessOutput.length();
334 this->TestResult.Output =
335 compress ? compressedOutput : this->ProcessOutput;
336 this->TestResult.CompressOutput = compress;
337 this->TestResult.ReturnValue = this->TestProcess->GetExitValue();
339 this->TestResult.CompletionStatus = "Completed";
341 this->TestResult.ExecutionTime = this->TestProcess->GetTotalTime();
342 this->MemCheckPostProcess();
343 this->ComputeWeightedCost();
345 // If the test does not need to rerun push the current TestResult onto the
346 // TestHandler vector
347 if (!this->NeedsToRepeat()) {
348 this->TestHandler->TestResults.push_back(this->TestResult);
350 this->TestProcess.reset();
351 return passed || skipped;
354 bool cmCTestRunTest::StartAgain(std::unique_ptr<cmCTestRunTest> runner,
357 auto* testRun = runner.get();
359 if (!testRun->RunAgain) {
362 testRun->RunAgain = false; // reset
363 testRun->TestProcess = cm::make_unique<cmProcess>(std::move(runner));
364 // change to tests directory
365 cmWorkingDirectory workdir(testRun->TestProperties->Directory);
366 if (workdir.Failed()) {
367 testRun->StartFailure("Failed to change working directory to " +
368 testRun->TestProperties->Directory + " : " +
369 std::strerror(workdir.GetLastResult()),
370 "Failed to change working directory");
374 testRun->StartTest(completed, testRun->TotalNumberOfTests);
378 bool cmCTestRunTest::NeedsToRepeat()
380 this->NumberOfRunsLeft--;
381 if (this->NumberOfRunsLeft == 0) {
384 // If a test is marked as NOT_RUN it will not be repeated
385 // no matter the repeat settings, so just record it as-is.
386 if (this->TestResult.Status == cmCTestTestHandler::NOT_RUN) {
389 // if number of runs left is not 0, and we are running until
390 // we find a failed (or passed) test, then return true so the test can be
392 if ((this->RepeatMode == cmCTest::Repeat::UntilFail &&
393 this->TestResult.Status == cmCTestTestHandler::COMPLETED) ||
394 (this->RepeatMode == cmCTest::Repeat::UntilPass &&
395 this->TestResult.Status != cmCTestTestHandler::COMPLETED) ||
396 (this->RepeatMode == cmCTest::Repeat::AfterTimeout &&
397 this->TestResult.Status == cmCTestTestHandler::TIMEOUT)) {
398 this->RunAgain = true;
403 void cmCTestRunTest::ComputeWeightedCost()
405 double prev = static_cast<double>(this->TestProperties->PreviousRuns);
406 double avgcost = static_cast<double>(this->TestProperties->Cost);
407 double current = this->TestResult.ExecutionTime.count();
409 if (this->TestResult.Status == cmCTestTestHandler::COMPLETED) {
410 this->TestProperties->Cost =
411 static_cast<float>(((prev * avgcost) + current) / (prev + 1.0));
412 this->TestProperties->PreviousRuns++;
416 void cmCTestRunTest::MemCheckPostProcess()
418 if (!this->TestHandler->MemCheck) {
421 cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
422 this->Index << ": process test output now: "
423 << this->TestProperties->Name << " "
424 << this->TestResult.Name << std::endl,
425 this->TestHandler->GetQuiet());
426 cmCTestMemCheckHandler* handler =
427 static_cast<cmCTestMemCheckHandler*>(this->TestHandler);
428 handler->PostProcessTest(this->TestResult, this->Index);
431 void cmCTestRunTest::StartFailure(std::unique_ptr<cmCTestRunTest> runner,
432 std::string const& output,
433 std::string const& detail)
435 auto* testRun = runner.get();
437 testRun->TestProcess = cm::make_unique<cmProcess>(std::move(runner));
438 testRun->StartFailure(output, detail);
440 testRun->FinalizeTest(false);
443 void cmCTestRunTest::StartFailure(std::string const& output,
444 std::string const& detail)
446 // Still need to log the Start message so the test summary records our
447 // attempt to start this test
448 if (!this->CTest->GetTestProgressOutput()) {
449 cmCTestLog(this->CTest, HANDLER_OUTPUT,
450 std::setw(2 * getNumWidth(this->TotalNumberOfTests) + 8)
452 << std::setw(getNumWidth(this->TestHandler->GetMaxIndex()))
453 << this->TestProperties->Index << ": "
454 << this->TestProperties->Name << std::endl);
457 this->ProcessOutput.clear();
458 if (!output.empty()) {
459 *this->TestHandler->LogFile << output << std::endl;
460 cmCTestLog(this->CTest, ERROR_MESSAGE, output << std::endl);
463 this->TestResult.Properties = this->TestProperties;
464 this->TestResult.ExecutionTime = cmDuration::zero();
465 this->TestResult.CompressOutput = false;
466 this->TestResult.ReturnValue = -1;
467 this->TestResult.CompletionStatus = detail;
468 this->TestResult.Status = cmCTestTestHandler::NOT_RUN;
469 this->TestResult.TestCount = this->TestProperties->Index;
470 this->TestResult.Name = this->TestProperties->Name;
471 this->TestResult.Path = this->TestProperties->Directory;
472 this->TestResult.Output = output;
473 this->TestResult.FullCommandLine.clear();
474 this->TestResult.Environment.clear();
477 std::string cmCTestRunTest::GetTestPrefix(size_t completed, size_t total) const
479 std::ostringstream outputStream;
480 outputStream << std::setw(getNumWidth(total)) << completed << "/";
481 outputStream << std::setw(getNumWidth(total)) << total << " ";
483 if (this->TestHandler->MemCheck) {
484 outputStream << "MemCheck";
486 outputStream << "Test";
489 std::ostringstream indexStr;
490 indexStr << " #" << this->Index << ":";
491 outputStream << std::setw(3 + getNumWidth(this->TestHandler->GetMaxIndex()))
495 return outputStream.str();
498 bool cmCTestRunTest::StartTest(std::unique_ptr<cmCTestRunTest> runner,
499 size_t completed, size_t total)
501 auto* testRun = runner.get();
503 testRun->TestProcess = cm::make_unique<cmProcess>(std::move(runner));
505 if (!testRun->StartTest(completed, total)) {
506 testRun->FinalizeTest(false);
513 // Starts the execution of a test. Returns once it has started
514 bool cmCTestRunTest::StartTest(size_t completed, size_t total)
516 this->TotalNumberOfTests = total; // save for rerun case
517 if (!this->CTest->GetTestProgressOutput()) {
518 cmCTestLog(this->CTest, HANDLER_OUTPUT,
519 std::setw(2 * getNumWidth(total) + 8)
521 << std::setw(getNumWidth(this->TestHandler->GetMaxIndex()))
522 << this->TestProperties->Index << ": "
523 << this->TestProperties->Name << std::endl);
525 std::string testName = this->GetTestPrefix(completed, total) +
526 this->TestProperties->Name + "\n";
527 cmCTestLog(this->CTest, HANDLER_TEST_PROGRESS_OUTPUT, testName);
530 this->ProcessOutput.clear();
532 this->TestResult.Properties = this->TestProperties;
533 this->TestResult.ExecutionTime = cmDuration::zero();
534 this->TestResult.CompressOutput = false;
535 this->TestResult.ReturnValue = -1;
536 this->TestResult.TestCount = this->TestProperties->Index;
537 this->TestResult.Name = this->TestProperties->Name;
538 this->TestResult.Path = this->TestProperties->Directory;
540 // Return immediately if test is disabled
541 if (this->TestProperties->Disabled) {
542 this->TestResult.CompletionStatus = "Disabled";
543 this->TestResult.Status = cmCTestTestHandler::NOT_RUN;
544 this->TestResult.Output = "Disabled";
545 this->TestResult.FullCommandLine.clear();
546 this->TestResult.Environment.clear();
550 this->TestResult.CompletionStatus = "Failed to start";
551 this->TestResult.Status = cmCTestTestHandler::BAD_COMMAND;
553 // Check for failed fixture dependencies before we even look at the command
554 // arguments because if we are not going to run the test, the command and
555 // its arguments are irrelevant. This matters for the case where a fixture
556 // dependency might be creating the executable we want to run.
557 if (!this->FailedDependencies.empty()) {
558 std::string msg = "Failed test dependencies:";
559 for (std::string const& failedDep : this->FailedDependencies) {
560 msg += " " + failedDep;
562 *this->TestHandler->LogFile << msg << std::endl;
563 cmCTestLog(this->CTest, HANDLER_OUTPUT, msg << std::endl);
564 this->TestResult.Output = msg;
565 this->TestResult.FullCommandLine.clear();
566 this->TestResult.Environment.clear();
567 this->TestResult.CompletionStatus = "Fixture dependency failed";
568 this->TestResult.Status = cmCTestTestHandler::NOT_RUN;
572 this->ComputeArguments();
573 std::vector<std::string>& args = this->TestProperties->Args;
574 if (args.size() >= 2 && args[1] == "NOT_AVAILABLE") {
576 if (this->CTest->GetConfigType().empty()) {
577 msg = "Test not available without configuration. (Missing \"-C "
580 msg = cmStrCat("Test not available in configuration \"",
581 this->CTest->GetConfigType(), "\".");
583 *this->TestHandler->LogFile << msg << std::endl;
584 cmCTestLog(this->CTest, ERROR_MESSAGE, msg << std::endl);
585 this->TestResult.Output = msg;
586 this->TestResult.FullCommandLine.clear();
587 this->TestResult.Environment.clear();
588 this->TestResult.CompletionStatus = "Missing Configuration";
589 this->TestResult.Status = cmCTestTestHandler::NOT_RUN;
593 // Check if all required files exist
594 for (std::string const& file : this->TestProperties->RequiredFiles) {
595 if (!cmSystemTools::FileExists(file)) {
596 // Required file was not found
597 *this->TestHandler->LogFile << "Unable to find required file: " << file
599 cmCTestLog(this->CTest, ERROR_MESSAGE,
600 "Unable to find required file: " << file << std::endl);
601 this->TestResult.Output = "Unable to find required file: " + file;
602 this->TestResult.FullCommandLine.clear();
603 this->TestResult.Environment.clear();
604 this->TestResult.CompletionStatus = "Required Files Missing";
605 this->TestResult.Status = cmCTestTestHandler::NOT_RUN;
609 // log and return if we did not find the executable
610 if (this->ActualCommand.empty()) {
611 // if the command was not found create a TestResult object
612 // that has that information
613 *this->TestHandler->LogFile << "Unable to find executable: " << args[1]
615 cmCTestLog(this->CTest, ERROR_MESSAGE,
616 "Unable to find executable: " << args[1] << std::endl);
617 this->TestResult.Output = "Unable to find executable: " + args[1];
618 this->TestResult.FullCommandLine.clear();
619 this->TestResult.Environment.clear();
620 this->TestResult.CompletionStatus = "Unable to find executable";
621 this->TestResult.Status = cmCTestTestHandler::NOT_RUN;
624 this->StartTime = this->CTest->CurrentTime();
626 auto timeout = this->TestProperties->Timeout;
628 this->TimeoutIsForStopTime = false;
629 std::chrono::system_clock::time_point stop_time = this->CTest->GetStopTime();
630 if (stop_time != std::chrono::system_clock::time_point()) {
631 std::chrono::duration<double> stop_timeout =
632 (stop_time - std::chrono::system_clock::now()) % std::chrono::hours(24);
634 if (stop_timeout <= std::chrono::duration<double>::zero()) {
635 stop_timeout = std::chrono::duration<double>::zero();
637 if (timeout == std::chrono::duration<double>::zero() ||
638 stop_timeout < timeout) {
639 this->TimeoutIsForStopTime = true;
640 timeout = stop_timeout;
644 return this->ForkProcess(timeout, this->TestProperties->ExplicitTimeout,
645 &this->TestProperties->Environment,
646 &this->TestProperties->EnvironmentModification,
647 &this->TestProperties->Affinity);
650 void cmCTestRunTest::ComputeArguments()
652 this->Arguments.clear(); // reset because this might be a rerun
653 auto j = this->TestProperties->Args.begin();
654 ++j; // skip test name
655 // find the test executable
656 if (this->TestHandler->MemCheck) {
657 cmCTestMemCheckHandler* handler =
658 static_cast<cmCTestMemCheckHandler*>(this->TestHandler);
659 this->ActualCommand = handler->MemoryTester;
660 this->TestProperties->Args[1] =
661 this->TestHandler->FindTheExecutable(this->TestProperties->Args[1]);
663 this->ActualCommand =
664 this->TestHandler->FindTheExecutable(this->TestProperties->Args[1]);
665 ++j; // skip the executable (it will be actualCommand)
667 std::string testCommand =
668 cmSystemTools::ConvertToOutputPath(this->ActualCommand);
670 // Prepends memcheck args to our command string
671 this->TestHandler->GenerateTestCommand(this->Arguments, this->Index);
672 for (std::string const& arg : this->Arguments) {
673 testCommand += " \"";
678 for (; j != this->TestProperties->Args.end(); ++j) {
679 testCommand += " \"";
682 this->Arguments.push_back(*j);
684 this->TestResult.FullCommandLine = testCommand;
686 // Print the test command in verbose mode
687 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
689 << this->Index << ": "
690 << (this->TestHandler->MemCheck ? "MemCheck" : "Test")
691 << " command: " << testCommand << std::endl);
693 // Print any test-specific env vars in verbose mode
694 if (!this->TestProperties->Directory.empty()) {
695 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
697 << "Working Directory: "
698 << this->TestProperties->Directory << std::endl);
701 // Print any test-specific env vars in verbose mode
702 if (!this->TestProperties->Environment.empty()) {
703 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
705 << "Environment variables: " << std::endl);
707 for (std::string const& env : this->TestProperties->Environment) {
708 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
709 this->Index << ": " << env << std::endl);
711 if (!this->TestProperties->EnvironmentModification.empty()) {
712 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
714 << "Environment variable modifications: "
717 for (std::string const& envmod :
718 this->TestProperties->EnvironmentModification) {
719 cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
720 this->Index << ": " << envmod << std::endl);
724 void cmCTestRunTest::ParseOutputForMeasurements()
726 if (!this->ProcessOutput.empty() &&
727 (this->ProcessOutput.find("<DartMeasurement") != std::string::npos ||
728 this->ProcessOutput.find("<CTestMeasurement") != std::string::npos)) {
729 if (this->TestHandler->AllTestMeasurementsRegex.find(
730 this->ProcessOutput)) {
731 this->TestResult.TestMeasurementsOutput =
732 this->TestHandler->AllTestMeasurementsRegex.match(1);
733 // keep searching and replacing until none are left
734 while (this->TestHandler->SingleTestMeasurementRegex.find(
735 this->ProcessOutput)) {
736 // replace the exact match for the string
737 cmSystemTools::ReplaceString(
739 this->TestHandler->SingleTestMeasurementRegex.match(1).c_str(), "");
745 bool cmCTestRunTest::ForkProcess(
746 cmDuration testTimeOut, bool explicitTimeout,
747 std::vector<std::string>* environment,
748 std::vector<std::string>* environment_modification,
749 std::vector<size_t>* affinity)
751 this->TestProcess->SetId(this->Index);
752 this->TestProcess->SetWorkingDirectory(this->TestProperties->Directory);
753 this->TestProcess->SetCommand(this->ActualCommand);
754 this->TestProcess->SetCommandArguments(this->Arguments);
756 // determine how much time we have
757 cmDuration timeout = this->CTest->GetRemainingTimeAllowed();
758 if (timeout != cmCTest::MaxDuration()) {
759 timeout -= std::chrono::minutes(2);
761 if (this->CTest->GetTimeOut() > cmDuration::zero() &&
762 this->CTest->GetTimeOut() < timeout) {
763 timeout = this->CTest->GetTimeOut();
765 if (testTimeOut > cmDuration::zero() &&
766 testTimeOut < this->CTest->GetRemainingTimeAllowed()) {
767 timeout = testTimeOut;
769 // always have at least 1 second if we got to here
770 if (timeout <= cmDuration::zero()) {
771 timeout = std::chrono::seconds(1);
773 // handle timeout explicitly set to 0
774 if (testTimeOut == cmDuration::zero() && explicitTimeout) {
775 timeout = cmDuration::zero();
777 cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
779 << "Test timeout computed to be: "
780 << cmDurationTo<unsigned int>(timeout)
782 this->TestHandler->GetQuiet());
784 this->TestProcess->SetTimeout(timeout);
786 cmSystemTools::SaveRestoreEnvironment sre;
787 std::ostringstream envMeasurement;
789 // We split processing ENVIRONMENT and ENVIRONMENT_MODIFICATION into two
790 // phases to ensure that MYVAR=reset: in the latter phase resets to the
791 // former phase's settings, rather than to the original environment.
792 if (environment && !environment->empty()) {
793 cmSystemTools::EnvDiff diff;
794 diff.AppendEnv(*environment);
795 diff.ApplyToCurrentEnv(&envMeasurement);
798 if (environment_modification && !environment_modification->empty()) {
799 cmSystemTools::EnvDiff diff;
802 for (auto const& envmod : *environment_modification) {
803 env_ok &= diff.ParseOperation(envmod);
810 diff.ApplyToCurrentEnv(&envMeasurement);
813 if (this->UseAllocatedResources) {
814 std::vector<std::string> envLog;
815 this->SetupResourcesEnvironment(&envLog);
816 for (auto const& var : envLog) {
817 envMeasurement << var << std::endl;
820 cmSystemTools::UnsetEnv("CTEST_RESOURCE_GROUP_COUNT");
821 // Signify that this variable is being actively unset
822 envMeasurement << "#CTEST_RESOURCE_GROUP_COUNT=" << std::endl;
825 this->TestResult.Environment = envMeasurement.str();
826 // Remove last newline
827 this->TestResult.Environment.erase(this->TestResult.Environment.length() -
830 return this->TestProcess->StartProcess(this->MultiTestHandler.Loop,
834 void cmCTestRunTest::SetupResourcesEnvironment(std::vector<std::string>* log)
836 std::string processCount = "CTEST_RESOURCE_GROUP_COUNT=";
837 processCount += std::to_string(this->AllocatedResources.size());
838 cmSystemTools::PutEnv(processCount);
840 log->push_back(processCount);
844 for (auto const& process : this->AllocatedResources) {
845 std::string prefix = "CTEST_RESOURCE_GROUP_";
846 prefix += std::to_string(i);
847 std::string resourceList = prefix + '=';
849 bool firstType = true;
850 for (auto const& it : process) {
855 auto resourceType = it.first;
856 resourceList += resourceType;
857 std::string var = prefix + cmSystemTools::UpperCase(resourceType) + '=';
858 bool firstName = true;
859 for (auto const& it2 : it.second) {
864 var += "id:" + it2.Id + ",slots:" + std::to_string(it2.Slots);
866 cmSystemTools::PutEnv(var);
871 cmSystemTools::PutEnv(resourceList);
873 log->push_back(resourceList);
879 void cmCTestRunTest::WriteLogOutputTop(size_t completed, size_t total)
881 std::ostringstream outputStream;
883 // If this is the last or only run of this test, or progress output is
884 // requested, then print out completed / total.
885 // Only issue is if a test fails and we are running until fail
886 // then it will never print out the completed / total, same would
887 // got for run until pass. Trick is when this is called we don't
888 // yet know if we are passing or failing.
889 bool const progressOnLast =
890 (this->RepeatMode != cmCTest::Repeat::UntilPass &&
891 this->RepeatMode != cmCTest::Repeat::AfterTimeout);
892 if ((progressOnLast && this->NumberOfRunsLeft == 1) ||
893 (!progressOnLast && this->NumberOfRunsLeft == this->NumberOfRunsTotal) ||
894 this->CTest->GetTestProgressOutput()) {
895 outputStream << std::setw(getNumWidth(total)) << completed << "/";
896 outputStream << std::setw(getNumWidth(total)) << total << " ";
898 // if this is one of several runs of a test just print blank space
899 // to keep things neat
901 outputStream << std::setw(getNumWidth(total)) << " ";
902 outputStream << std::setw(getNumWidth(total)) << " ";
905 if (this->TestHandler->MemCheck) {
906 outputStream << "MemCheck";
908 outputStream << "Test";
911 std::ostringstream indexStr;
912 indexStr << " #" << this->Index << ":";
913 outputStream << std::setw(3 + getNumWidth(this->TestHandler->GetMaxIndex()))
917 const int maxTestNameWidth = this->CTest->GetMaxTestNameWidth();
918 std::string outname = this->TestProperties->Name + " ";
919 outname.resize(maxTestNameWidth + 4, '.');
920 outputStream << outname;
922 *this->TestHandler->LogFile << this->TestProperties->Index << "/"
923 << this->TestHandler->TotalNumberOfTests
924 << " Testing: " << this->TestProperties->Name
926 *this->TestHandler->LogFile << this->TestProperties->Index << "/"
927 << this->TestHandler->TotalNumberOfTests
928 << " Test: " << this->TestProperties->Name
930 *this->TestHandler->LogFile << "Command: \"" << this->ActualCommand << "\"";
932 for (std::string const& arg : this->Arguments) {
933 *this->TestHandler->LogFile << " \"" << arg << "\"";
935 *this->TestHandler->LogFile
937 << "Directory: " << this->TestProperties->Directory << std::endl
938 << "\"" << this->TestProperties->Name
939 << "\" start time: " << this->StartTime << std::endl;
941 *this->TestHandler->LogFile
942 << "Output:" << std::endl
943 << "----------------------------------------------------------"
945 *this->TestHandler->LogFile << this->ProcessOutput << "<end of output>"
948 if (!this->CTest->GetTestProgressOutput()) {
949 cmCTestLog(this->CTest, HANDLER_OUTPUT, outputStream.str());
952 cmCTestLog(this->CTest, DEBUG,
953 "Testing " << this->TestProperties->Name << " ... ");
956 void cmCTestRunTest::FinalizeTest(bool started)
958 this->MultiTestHandler.FinishTestProcess(this->TestProcess->GetRunner(),