cmCTestRunTest.cxx 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027
  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"
  4. #include <algorithm>
  5. #include <chrono>
  6. #include <cstddef> // IWYU pragma: keep
  7. #include <cstdint>
  8. #include <cstdio>
  9. #include <iomanip>
  10. #include <ratio>
  11. #include <sstream>
  12. #include <utility>
  13. #include <cm/memory>
  14. #include <cm/optional>
  15. #include "cmsys/RegularExpression.hxx"
  16. #include "cmCTest.h"
  17. #include "cmCTestMemCheckHandler.h"
  18. #include "cmCTestMultiProcessHandler.h"
  19. #include "cmDuration.h"
  20. #include "cmProcess.h"
  21. #include "cmStringAlgorithms.h"
  22. #include "cmSystemTools.h"
  23. #include "cmUVHandlePtr.h"
  24. #include "cmWorkingDirectory.h"
  25. cmCTestRunTest::cmCTestRunTest(cmCTestMultiProcessHandler& multiHandler,
  26. int index)
  27. : MultiTestHandler(multiHandler)
  28. , Index(index)
  29. , CTest(MultiTestHandler.CTest)
  30. , TestHandler(MultiTestHandler.TestHandler)
  31. , TestProperties(MultiTestHandler.Properties[Index])
  32. , Instrumentation(cmSystemTools::GetLogicalWorkingDirectory())
  33. {
  34. }
  35. void cmCTestRunTest::CheckOutput(std::string const& line)
  36. {
  37. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  38. this->GetIndex() << ": " << line << std::endl);
  39. // Check for special CTest XML tags in this line of output.
  40. // If any are found, this line is excluded from ProcessOutput.
  41. if (!line.empty() && line.find("<CTest") != std::string::npos) {
  42. bool ctest_tag_found = false;
  43. if (this->TestHandler->CustomCompletionStatusRegex.find(line)) {
  44. ctest_tag_found = true;
  45. this->TestResult.CustomCompletionStatus =
  46. this->TestHandler->CustomCompletionStatusRegex.match(1);
  47. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  48. this->GetIndex() << ": "
  49. << "Test Details changed to '"
  50. << this->TestResult.CustomCompletionStatus
  51. << "'" << std::endl);
  52. } else if (this->TestHandler->CustomLabelRegex.find(line)) {
  53. ctest_tag_found = true;
  54. auto label = this->TestHandler->CustomLabelRegex.match(1);
  55. auto& labels = this->TestProperties->Labels;
  56. if (std::find(labels.begin(), labels.end(), label) == labels.end()) {
  57. labels.push_back(label);
  58. std::sort(labels.begin(), labels.end());
  59. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  60. this->GetIndex()
  61. << ": "
  62. << "Test Label added: '" << label << "'" << std::endl);
  63. }
  64. }
  65. if (ctest_tag_found) {
  66. return;
  67. }
  68. }
  69. this->ProcessOutput += line;
  70. this->ProcessOutput += "\n";
  71. // Check for TIMEOUT_AFTER_MATCH property.
  72. if (!this->TestProperties->TimeoutRegularExpressions.empty()) {
  73. for (auto& reg : this->TestProperties->TimeoutRegularExpressions) {
  74. if (reg.first.find(this->ProcessOutput)) {
  75. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  76. this->GetIndex()
  77. << ": "
  78. << "Test timeout changed to "
  79. << std::chrono::duration_cast<std::chrono::seconds>(
  80. this->TestProperties->AlternateTimeout)
  81. .count()
  82. << std::endl);
  83. this->TestProcess->ResetStartTime();
  84. this->TestProcess->ChangeTimeout(
  85. this->TestProperties->AlternateTimeout);
  86. this->TestProperties->TimeoutRegularExpressions.clear();
  87. break;
  88. }
  89. }
  90. }
  91. }
  92. cmCTestRunTest::EndTestResult cmCTestRunTest::EndTest(size_t completed,
  93. size_t total,
  94. bool started)
  95. {
  96. this->WriteLogOutputTop(completed, total);
  97. std::string reason;
  98. bool passed = true;
  99. cmProcess::State res =
  100. started ? this->TestProcess->GetProcessStatus() : cmProcess::State::Error;
  101. std::int64_t retVal = this->TestProcess->GetExitValue();
  102. bool forceFail = false;
  103. bool forceSkip = false;
  104. bool skipped = false;
  105. bool outputTestErrorsToConsole = false;
  106. if (!this->TestProperties->RequiredRegularExpressions.empty() &&
  107. this->FailedDependencies.empty()) {
  108. bool found = false;
  109. for (auto& pass : this->TestProperties->RequiredRegularExpressions) {
  110. if (pass.first.find(this->ProcessOutput)) {
  111. found = true;
  112. reason = cmStrCat("Required regular expression found. Regex=[",
  113. pass.second, ']');
  114. break;
  115. }
  116. }
  117. if (!found) {
  118. reason = "Required regular expression not found. Regex=[";
  119. for (auto& pass : this->TestProperties->RequiredRegularExpressions) {
  120. reason += pass.second;
  121. reason += "\n";
  122. }
  123. reason += "]";
  124. forceFail = true;
  125. }
  126. }
  127. if (!this->TestProperties->ErrorRegularExpressions.empty() &&
  128. this->FailedDependencies.empty()) {
  129. for (auto& fail : this->TestProperties->ErrorRegularExpressions) {
  130. if (fail.first.find(this->ProcessOutput)) {
  131. reason = cmStrCat("Error regular expression found in output. Regex=[",
  132. fail.second, ']');
  133. forceFail = true;
  134. break;
  135. }
  136. }
  137. }
  138. if (!this->TestProperties->SkipRegularExpressions.empty() &&
  139. this->FailedDependencies.empty()) {
  140. for (auto& skip : this->TestProperties->SkipRegularExpressions) {
  141. if (skip.first.find(this->ProcessOutput)) {
  142. reason = cmStrCat("Skip regular expression found in output. Regex=[",
  143. skip.second, ']');
  144. forceSkip = true;
  145. break;
  146. }
  147. }
  148. }
  149. std::string resourceSpecParseError;
  150. if (!this->TestProperties->GeneratedResourceSpecFile.empty()) {
  151. this->MultiTestHandler.ResourceSpecFile =
  152. this->TestProperties->GeneratedResourceSpecFile;
  153. if (!this->MultiTestHandler.InitResourceAllocator(
  154. resourceSpecParseError)) {
  155. reason = "Invalid resource spec file";
  156. forceFail = true;
  157. } else {
  158. this->MultiTestHandler.CheckResourceAvailability();
  159. }
  160. }
  161. std::ostringstream outputStream;
  162. if (res == cmProcess::State::Exited) {
  163. bool success = !forceFail &&
  164. (retVal == 0 ||
  165. !this->TestProperties->RequiredRegularExpressions.empty());
  166. if ((this->TestProperties->SkipReturnCode >= 0 &&
  167. this->TestProperties->SkipReturnCode == retVal) ||
  168. forceSkip) {
  169. this->TestResult.Status = cmCTestTestHandler::NOT_RUN;
  170. std::ostringstream s;
  171. if (forceSkip) {
  172. s << "SKIP_REGULAR_EXPRESSION_MATCHED";
  173. } else {
  174. s << "SKIP_RETURN_CODE=" << this->TestProperties->SkipReturnCode;
  175. }
  176. this->TestResult.CompletionStatus = s.str();
  177. outputStream << "***Skipped ";
  178. skipped = true;
  179. } else if (success != this->TestProperties->WillFail) {
  180. this->TestResult.Status = cmCTestTestHandler::COMPLETED;
  181. outputStream << " Passed ";
  182. } else {
  183. this->TestResult.Status = cmCTestTestHandler::FAILED;
  184. outputStream << "***Failed " << reason;
  185. outputTestErrorsToConsole =
  186. this->CTest->GetOutputTestOutputOnTestFailure();
  187. }
  188. } else if (res == cmProcess::State::Expired) {
  189. outputStream << "***Timeout ";
  190. if (this->TestProperties->TimeoutSignal &&
  191. this->TestProcess->GetTerminationStyle() ==
  192. cmProcess::Termination::Custom) {
  193. outputStream << "(" << this->TestProperties->TimeoutSignal->Name << ") ";
  194. }
  195. this->TestResult.Status = cmCTestTestHandler::TIMEOUT;
  196. outputTestErrorsToConsole =
  197. this->CTest->GetOutputTestOutputOnTestFailure();
  198. } else if (res == cmProcess::State::Exception) {
  199. outputTestErrorsToConsole =
  200. this->CTest->GetOutputTestOutputOnTestFailure();
  201. outputStream << "***Exception: ";
  202. this->TestResult.ExceptionStatus =
  203. this->TestProcess->GetExitExceptionString();
  204. switch (this->TestProcess->GetExitException()) {
  205. case cmProcess::Exception::Fault:
  206. outputStream << "SegFault";
  207. this->TestResult.Status = cmCTestTestHandler::SEGFAULT;
  208. break;
  209. case cmProcess::Exception::Illegal:
  210. outputStream << "Illegal";
  211. this->TestResult.Status = cmCTestTestHandler::ILLEGAL;
  212. break;
  213. case cmProcess::Exception::Interrupt:
  214. outputStream << "Interrupt";
  215. this->TestResult.Status = cmCTestTestHandler::INTERRUPT;
  216. break;
  217. case cmProcess::Exception::Numerical:
  218. outputStream << "Numerical";
  219. this->TestResult.Status = cmCTestTestHandler::NUMERICAL;
  220. break;
  221. default:
  222. cmCTestLog(this->CTest, HANDLER_OUTPUT,
  223. this->TestResult.ExceptionStatus);
  224. this->TestResult.Status = cmCTestTestHandler::OTHER_FAULT;
  225. }
  226. } else if ("Disabled" == this->TestResult.CompletionStatus) {
  227. outputStream << "***Not Run (Disabled) ";
  228. } else // cmProcess::State::Error
  229. {
  230. outputStream << "***Not Run ";
  231. }
  232. passed = this->TestResult.Status == cmCTestTestHandler::COMPLETED;
  233. char buf[1024];
  234. snprintf(buf, sizeof(buf), "%6.2f sec",
  235. this->TestProcess->GetTotalTime().count());
  236. outputStream << buf << "\n";
  237. bool passedOrSkipped = passed || skipped;
  238. if (this->CTest->GetTestProgressOutput()) {
  239. if (!passedOrSkipped) {
  240. // If the test did not pass, reprint test name and error
  241. std::string output = this->GetTestPrefix(completed, total);
  242. std::string testName = this->TestProperties->Name;
  243. const int maxTestNameWidth = this->CTest->GetMaxTestNameWidth();
  244. testName.resize(maxTestNameWidth + 4, '.');
  245. output += testName;
  246. output += outputStream.str();
  247. outputStream.str("");
  248. outputStream.clear();
  249. outputStream << output;
  250. cmCTestLog(this->CTest, HANDLER_TEST_PROGRESS_OUTPUT, "\n"); // flush
  251. }
  252. if (completed == total) {
  253. std::string testName = this->GetTestPrefix(completed, total) +
  254. this->TestProperties->Name + "\n";
  255. cmCTestLog(this->CTest, HANDLER_TEST_PROGRESS_OUTPUT, testName);
  256. }
  257. }
  258. if (!this->CTest->GetTestProgressOutput() || !passedOrSkipped) {
  259. cmCTestLog(this->CTest, HANDLER_OUTPUT, outputStream.str());
  260. }
  261. if (outputTestErrorsToConsole) {
  262. cmCTestLog(this->CTest, HANDLER_OUTPUT, this->ProcessOutput << std::endl);
  263. }
  264. if (!resourceSpecParseError.empty()) {
  265. cmCTestLog(this->CTest, ERROR_MESSAGE,
  266. resourceSpecParseError << std::endl);
  267. } else if (!this->TestProperties->GeneratedResourceSpecFile.empty()) {
  268. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  269. "Using generated resource spec file "
  270. << this->TestProperties->GeneratedResourceSpecFile
  271. << std::endl);
  272. }
  273. if (this->TestHandler->LogFile) {
  274. *this->TestHandler->LogFile << "Test time = " << buf << std::endl;
  275. }
  276. this->ParseOutputForMeasurements();
  277. // if this is doing MemCheck then all the output needs to be put into
  278. // Output since that is what is parsed by cmCTestMemCheckHandler
  279. if (!this->TestHandler->MemCheck && started) {
  280. this->TestHandler->CleanTestOutput(
  281. this->ProcessOutput,
  282. static_cast<size_t>(this->TestResult.Status ==
  283. cmCTestTestHandler::COMPLETED
  284. ? this->TestHandler->TestOptions.OutputSizePassed
  285. : this->TestHandler->TestOptions.OutputSizeFailed),
  286. this->TestHandler->TestOptions.OutputTruncation);
  287. }
  288. this->TestResult.Reason = reason;
  289. if (this->TestHandler->LogFile) {
  290. bool pass = true;
  291. const char* reasonType = "Test Pass Reason";
  292. if (this->TestResult.Status != cmCTestTestHandler::COMPLETED &&
  293. this->TestResult.Status != cmCTestTestHandler::NOT_RUN) {
  294. reasonType = "Test Fail Reason";
  295. pass = false;
  296. }
  297. auto ttime = this->TestProcess->GetTotalTime();
  298. auto hours = std::chrono::duration_cast<std::chrono::hours>(ttime);
  299. ttime -= hours;
  300. auto minutes = std::chrono::duration_cast<std::chrono::minutes>(ttime);
  301. ttime -= minutes;
  302. auto seconds = std::chrono::duration_cast<std::chrono::seconds>(ttime);
  303. char buffer[100];
  304. snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d",
  305. static_cast<unsigned>(hours.count()),
  306. static_cast<unsigned>(minutes.count()),
  307. static_cast<unsigned>(seconds.count()));
  308. *this->TestHandler->LogFile
  309. << "----------------------------------------------------------"
  310. << std::endl;
  311. if (!this->TestResult.Reason.empty()) {
  312. *this->TestHandler->LogFile << reasonType << ":\n"
  313. << this->TestResult.Reason << "\n";
  314. } else {
  315. if (pass) {
  316. *this->TestHandler->LogFile << "Test Passed.\n";
  317. } else {
  318. *this->TestHandler->LogFile << "Test Failed.\n";
  319. }
  320. }
  321. *this->TestHandler->LogFile
  322. << "\"" << this->TestProperties->Name
  323. << "\" end time: " << this->CTest->CurrentTime() << std::endl
  324. << "\"" << this->TestProperties->Name << "\" time elapsed: " << buffer
  325. << std::endl
  326. << "----------------------------------------------------------"
  327. << std::endl
  328. << std::endl;
  329. }
  330. // if the test actually started and ran
  331. // record the results in TestResult
  332. if (started) {
  333. std::string compressedOutput;
  334. if (!this->TestHandler->MemCheck &&
  335. this->CTest->ShouldCompressTestOutput()) {
  336. std::string str = this->ProcessOutput;
  337. if (this->CTest->CompressString(str)) {
  338. compressedOutput = std::move(str);
  339. }
  340. }
  341. bool compress = !compressedOutput.empty() &&
  342. compressedOutput.length() < this->ProcessOutput.length();
  343. this->TestResult.Output =
  344. compress ? compressedOutput : this->ProcessOutput;
  345. this->TestResult.CompressOutput = compress;
  346. this->TestResult.ReturnValue = this->TestProcess->GetExitValue();
  347. if (!skipped) {
  348. this->TestResult.CompletionStatus = "Completed";
  349. }
  350. this->TestResult.ExecutionTime = this->TestProcess->GetTotalTime();
  351. this->MemCheckPostProcess();
  352. this->ComputeWeightedCost();
  353. }
  354. // If the test does not need to rerun push the current TestResult onto the
  355. // TestHandler vector
  356. if (!this->NeedsToRepeat()) {
  357. this->TestHandler->TestResults.push_back(this->TestResult);
  358. }
  359. cmCTestRunTest::EndTestResult testResult;
  360. testResult.Passed = passed || skipped;
  361. if (res == cmProcess::State::Expired &&
  362. this->TestProcess->GetTimeoutReason() ==
  363. cmProcess::TimeoutReason::StopTime) {
  364. testResult.StopTimePassed = true;
  365. }
  366. this->TestProcess.reset();
  367. return testResult;
  368. }
  369. bool cmCTestRunTest::StartAgain(std::unique_ptr<cmCTestRunTest> runner,
  370. size_t completed)
  371. {
  372. auto* testRun = runner.get();
  373. if (!testRun->RunAgain) {
  374. return false;
  375. }
  376. testRun->RunAgain = false; // reset
  377. testRun->TestProcess = cm::make_unique<cmProcess>(std::move(runner));
  378. // change to tests directory
  379. cmWorkingDirectory workdir(testRun->TestProperties->Directory);
  380. if (workdir.Failed()) {
  381. testRun->StartFailure(testRun->TotalNumberOfTests, workdir.GetError(),
  382. "Failed to change working directory");
  383. return true;
  384. }
  385. testRun->StartTest(completed, testRun->TotalNumberOfTests);
  386. return true;
  387. }
  388. bool cmCTestRunTest::NeedsToRepeat()
  389. {
  390. this->NumberOfRunsLeft--;
  391. if (this->NumberOfRunsLeft == 0) {
  392. return false;
  393. }
  394. // If a test is marked as NOT_RUN it will not be repeated
  395. // no matter the repeat settings, so just record it as-is.
  396. if (this->TestResult.Status == cmCTestTestHandler::NOT_RUN) {
  397. return false;
  398. }
  399. // if number of runs left is not 0, and we are running until
  400. // we find a failed (or passed) test, then return true so the test can be
  401. // restarted
  402. if ((this->RepeatMode == cmCTest::Repeat::UntilFail &&
  403. this->TestResult.Status == cmCTestTestHandler::COMPLETED) ||
  404. (this->RepeatMode == cmCTest::Repeat::UntilPass &&
  405. this->TestResult.Status != cmCTestTestHandler::COMPLETED) ||
  406. (this->RepeatMode == cmCTest::Repeat::AfterTimeout &&
  407. this->TestResult.Status == cmCTestTestHandler::TIMEOUT)) {
  408. this->RunAgain = true;
  409. return true;
  410. }
  411. return false;
  412. }
  413. void cmCTestRunTest::ComputeWeightedCost()
  414. {
  415. double prev = static_cast<double>(this->TestProperties->PreviousRuns);
  416. double avgcost = static_cast<double>(this->TestProperties->Cost);
  417. double current = this->TestResult.ExecutionTime.count();
  418. if (this->TestResult.Status == cmCTestTestHandler::COMPLETED) {
  419. this->TestProperties->Cost =
  420. static_cast<float>(((prev * avgcost) + current) / (prev + 1.0));
  421. this->TestProperties->PreviousRuns++;
  422. }
  423. }
  424. void cmCTestRunTest::MemCheckPostProcess()
  425. {
  426. if (!this->TestHandler->MemCheck) {
  427. return;
  428. }
  429. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  430. this->Index << ": process test output now: "
  431. << this->TestProperties->Name << " "
  432. << this->TestResult.Name << std::endl,
  433. this->TestHandler->GetQuiet());
  434. cmCTestMemCheckHandler* handler =
  435. static_cast<cmCTestMemCheckHandler*>(this->TestHandler);
  436. handler->PostProcessTest(this->TestResult, this->Index);
  437. }
  438. void cmCTestRunTest::StartFailure(std::unique_ptr<cmCTestRunTest> runner,
  439. size_t total, std::string const& output,
  440. std::string const& detail)
  441. {
  442. auto* testRun = runner.get();
  443. testRun->TestProcess = cm::make_unique<cmProcess>(std::move(runner));
  444. testRun->StartFailure(total, output, detail);
  445. testRun->FinalizeTest(false);
  446. }
  447. void cmCTestRunTest::StartFailure(size_t total, std::string const& output,
  448. std::string const& detail)
  449. {
  450. // Still need to log the Start message so the test summary records our
  451. // attempt to start this test
  452. if (!this->CTest->GetTestProgressOutput()) {
  453. cmCTestLog(this->CTest, HANDLER_OUTPUT,
  454. std::setw(2 * getNumWidth(total) + 8)
  455. << "Start "
  456. << std::setw(getNumWidth(this->TestHandler->GetMaxIndex()))
  457. << this->TestProperties->Index << ": "
  458. << this->TestProperties->Name << std::endl);
  459. }
  460. this->ProcessOutput.clear();
  461. if (!output.empty()) {
  462. *this->TestHandler->LogFile << output << std::endl;
  463. cmCTestLog(this->CTest, ERROR_MESSAGE, output << std::endl);
  464. }
  465. this->TestResult.Properties = this->TestProperties;
  466. this->TestResult.ExecutionTime = cmDuration::zero();
  467. this->TestResult.CompressOutput = false;
  468. this->TestResult.ReturnValue = -1;
  469. this->TestResult.CompletionStatus = detail;
  470. this->TestResult.Status = cmCTestTestHandler::NOT_RUN;
  471. this->TestResult.TestCount = this->TestProperties->Index;
  472. this->TestResult.Name = this->TestProperties->Name;
  473. this->TestResult.Path = this->TestProperties->Directory;
  474. this->TestResult.Output = output;
  475. this->TestResult.FullCommandLine.clear();
  476. this->TestResult.Environment.clear();
  477. }
  478. std::string cmCTestRunTest::GetTestPrefix(size_t completed, size_t total) const
  479. {
  480. std::ostringstream outputStream;
  481. outputStream << std::setw(getNumWidth(total)) << completed << "/";
  482. outputStream << std::setw(getNumWidth(total)) << total << " ";
  483. if (this->TestHandler->MemCheck) {
  484. outputStream << "MemCheck";
  485. } else {
  486. outputStream << "Test";
  487. }
  488. std::ostringstream indexStr;
  489. indexStr << " #" << this->Index << ":";
  490. outputStream << std::setw(3 + getNumWidth(this->TestHandler->GetMaxIndex()))
  491. << indexStr.str();
  492. outputStream << " ";
  493. return outputStream.str();
  494. }
  495. void cmCTestRunTest::StartTest(std::unique_ptr<cmCTestRunTest> runner,
  496. size_t completed, size_t total)
  497. {
  498. auto* testRun = runner.get();
  499. testRun->TestProcess = cm::make_unique<cmProcess>(std::move(runner));
  500. if (!testRun->StartTest(completed, total)) {
  501. testRun->FinalizeTest(false);
  502. }
  503. }
  504. // Starts the execution of a test. Returns once it has started
  505. bool cmCTestRunTest::StartTest(size_t completed, size_t total)
  506. {
  507. this->TotalNumberOfTests = total; // save for rerun case
  508. if (!this->CTest->GetTestProgressOutput()) {
  509. cmCTestLog(this->CTest, HANDLER_OUTPUT,
  510. std::setw(2 * getNumWidth(total) + 8)
  511. << "Start "
  512. << std::setw(getNumWidth(this->TestHandler->GetMaxIndex()))
  513. << this->TestProperties->Index << ": "
  514. << this->TestProperties->Name << std::endl);
  515. } else {
  516. std::string testName = this->GetTestPrefix(completed, total) +
  517. this->TestProperties->Name + "\n";
  518. cmCTestLog(this->CTest, HANDLER_TEST_PROGRESS_OUTPUT, testName);
  519. }
  520. this->ProcessOutput.clear();
  521. this->TestResult.Properties = this->TestProperties;
  522. this->TestResult.ExecutionTime = cmDuration::zero();
  523. this->TestResult.CompressOutput = false;
  524. this->TestResult.ReturnValue = -1;
  525. this->TestResult.TestCount = this->TestProperties->Index;
  526. this->TestResult.Name = this->TestProperties->Name;
  527. this->TestResult.Path = this->TestProperties->Directory;
  528. // Reject invalid test properties.
  529. if (this->TestProperties->Error) {
  530. std::string const& msg = *this->TestProperties->Error;
  531. *this->TestHandler->LogFile << msg << std::endl;
  532. cmCTestLog(this->CTest, HANDLER_OUTPUT, msg << std::endl);
  533. this->TestResult.CompletionStatus = "Invalid Test Properties";
  534. this->TestResult.Status = cmCTestTestHandler::NOT_RUN;
  535. this->TestResult.Output = msg;
  536. this->TestResult.FullCommandLine.clear();
  537. this->TestResult.Environment.clear();
  538. return false;
  539. }
  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();
  547. return false;
  548. }
  549. this->TestResult.CompletionStatus = "Failed to start";
  550. this->TestResult.Status = cmCTestTestHandler::BAD_COMMAND;
  551. // Check for failed fixture dependencies before we even look at the command
  552. // arguments because if we are not going to run the test, the command and
  553. // its arguments are irrelevant. This matters for the case where a fixture
  554. // dependency might be creating the executable we want to run.
  555. if (!this->FailedDependencies.empty()) {
  556. std::string msg = "Failed test dependencies:";
  557. for (std::string const& failedDep : this->FailedDependencies) {
  558. msg += " " + failedDep;
  559. }
  560. *this->TestHandler->LogFile << msg << std::endl;
  561. cmCTestLog(this->CTest, HANDLER_OUTPUT, msg << std::endl);
  562. this->TestResult.Output = msg;
  563. this->TestResult.FullCommandLine.clear();
  564. this->TestResult.Environment.clear();
  565. this->TestResult.CompletionStatus = "Fixture dependency failed";
  566. this->TestResult.Status = cmCTestTestHandler::NOT_RUN;
  567. return false;
  568. }
  569. this->ComputeArguments();
  570. std::vector<std::string>& args = this->TestProperties->Args;
  571. if (args.size() >= 2 && args[1] == "NOT_AVAILABLE") {
  572. std::string msg;
  573. if (this->CTest->GetConfigType().empty()) {
  574. msg = "Test not available without configuration. (Missing \"-C "
  575. "<config>\"?)";
  576. } else {
  577. msg = cmStrCat("Test not available in configuration \"",
  578. this->CTest->GetConfigType(), "\".");
  579. }
  580. *this->TestHandler->LogFile << msg << std::endl;
  581. cmCTestLog(this->CTest, ERROR_MESSAGE, msg << std::endl);
  582. this->TestResult.Output = msg;
  583. this->TestResult.FullCommandLine.clear();
  584. this->TestResult.Environment.clear();
  585. this->TestResult.CompletionStatus = "Missing Configuration";
  586. this->TestResult.Status = cmCTestTestHandler::NOT_RUN;
  587. return false;
  588. }
  589. // Check if all required files exist
  590. for (std::string const& file : this->TestProperties->RequiredFiles) {
  591. if (!cmSystemTools::FileExists(file)) {
  592. // Required file was not found
  593. *this->TestHandler->LogFile << "Unable to find required file: " << file
  594. << std::endl;
  595. cmCTestLog(this->CTest, ERROR_MESSAGE,
  596. "Unable to find required file: " << file << std::endl);
  597. this->TestResult.Output = "Unable to find required file: " + file;
  598. this->TestResult.FullCommandLine.clear();
  599. this->TestResult.Environment.clear();
  600. this->TestResult.CompletionStatus = "Required Files Missing";
  601. this->TestResult.Status = cmCTestTestHandler::NOT_RUN;
  602. return false;
  603. }
  604. }
  605. // log and return if we did not find the executable
  606. if (this->ActualCommand.empty()) {
  607. // if the command was not found create a TestResult object
  608. // that has that information
  609. *this->TestHandler->LogFile << "Unable to find executable: " << args[1]
  610. << std::endl;
  611. cmCTestLog(this->CTest, ERROR_MESSAGE,
  612. "Unable to find executable: " << args[1] << std::endl);
  613. this->TestResult.Output = "Unable to find executable: " + args[1];
  614. this->TestResult.FullCommandLine.clear();
  615. this->TestResult.Environment.clear();
  616. this->TestResult.CompletionStatus = "Unable to find executable";
  617. this->TestResult.Status = cmCTestTestHandler::NOT_RUN;
  618. return false;
  619. }
  620. this->StartTime = this->CTest->CurrentTime();
  621. if (this->Instrumentation.HasQuery()) {
  622. this->Instrumentation.GetPreTestStats();
  623. }
  624. return this->ForkProcess();
  625. }
  626. void cmCTestRunTest::ComputeArguments()
  627. {
  628. this->Arguments.clear(); // reset because this might be a rerun
  629. auto j = this->TestProperties->Args.begin();
  630. ++j; // skip test name
  631. // find the test executable
  632. if (this->TestHandler->MemCheck) {
  633. cmCTestMemCheckHandler* handler =
  634. static_cast<cmCTestMemCheckHandler*>(this->TestHandler);
  635. this->ActualCommand = handler->MemoryTester;
  636. this->TestProperties->Args[1] =
  637. this->TestHandler->FindTheExecutable(this->TestProperties->Args[1]);
  638. } else {
  639. this->ActualCommand =
  640. this->TestHandler->FindTheExecutable(this->TestProperties->Args[1]);
  641. ++j; // skip the executable (it will be actualCommand)
  642. }
  643. std::string testCommand =
  644. cmSystemTools::ConvertToOutputPath(this->ActualCommand);
  645. // Prepends memcheck args to our command string
  646. this->TestHandler->GenerateTestCommand(this->Arguments, this->Index);
  647. for (std::string const& arg : this->Arguments) {
  648. testCommand += " \"";
  649. testCommand += arg;
  650. testCommand += "\"";
  651. }
  652. for (; j != this->TestProperties->Args.end(); ++j) {
  653. testCommand += " \"";
  654. testCommand += *j;
  655. testCommand += "\"";
  656. this->Arguments.push_back(*j);
  657. }
  658. this->TestResult.FullCommandLine = testCommand;
  659. // Print the test command in verbose mode
  660. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  661. std::endl
  662. << this->Index << ": "
  663. << (this->TestHandler->MemCheck ? "MemCheck" : "Test")
  664. << " command: " << testCommand << std::endl);
  665. // Print any test-specific env vars in verbose mode
  666. if (!this->TestProperties->Directory.empty()) {
  667. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  668. this->Index << ": "
  669. << "Working Directory: "
  670. << this->TestProperties->Directory << std::endl);
  671. }
  672. // Print any test-specific env vars in verbose mode
  673. if (!this->TestProperties->Environment.empty()) {
  674. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  675. this->Index << ": "
  676. << "Environment variables: " << std::endl);
  677. }
  678. for (std::string const& env : this->TestProperties->Environment) {
  679. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  680. this->Index << ": " << env << std::endl);
  681. }
  682. if (!this->TestProperties->EnvironmentModification.empty()) {
  683. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  684. this->Index << ": "
  685. << "Environment variable modifications: "
  686. << std::endl);
  687. }
  688. for (std::string const& envmod :
  689. this->TestProperties->EnvironmentModification) {
  690. cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  691. this->Index << ": " << envmod << std::endl);
  692. }
  693. }
  694. void cmCTestRunTest::ParseOutputForMeasurements()
  695. {
  696. if (!this->ProcessOutput.empty() &&
  697. (this->ProcessOutput.find("<DartMeasurement") != std::string::npos ||
  698. this->ProcessOutput.find("<CTestMeasurement") != std::string::npos)) {
  699. if (this->TestHandler->AllTestMeasurementsRegex.find(
  700. this->ProcessOutput)) {
  701. this->TestResult.TestMeasurementsOutput =
  702. this->TestHandler->AllTestMeasurementsRegex.match(1);
  703. // keep searching and replacing until none are left
  704. while (this->TestHandler->SingleTestMeasurementRegex.find(
  705. this->ProcessOutput)) {
  706. // replace the exact match for the string
  707. cmSystemTools::ReplaceString(
  708. this->ProcessOutput,
  709. this->TestHandler->SingleTestMeasurementRegex.match(1).c_str(), "");
  710. }
  711. }
  712. }
  713. }
  714. bool cmCTestRunTest::ForkProcess()
  715. {
  716. this->TestProcess->SetId(this->Index);
  717. this->TestProcess->SetWorkingDirectory(this->TestProperties->Directory);
  718. this->TestProcess->SetCommand(this->ActualCommand);
  719. this->TestProcess->SetCommandArguments(this->Arguments);
  720. cm::optional<cmDuration> timeout;
  721. // Check TIMEOUT test property.
  722. if (this->TestProperties->Timeout &&
  723. *this->TestProperties->Timeout >= cmDuration::zero()) {
  724. timeout = this->TestProperties->Timeout;
  725. }
  726. // An explicit TIMEOUT=0 test property means "no timeout".
  727. if (timeout) {
  728. if (*timeout == std::chrono::duration<double>::zero()) {
  729. timeout = cm::nullopt;
  730. }
  731. } else {
  732. // Check --timeout.
  733. if (this->CTest->GetGlobalTimeout() > cmDuration::zero()) {
  734. timeout = this->CTest->GetGlobalTimeout();
  735. }
  736. if (!timeout) {
  737. // Check CTEST_TEST_TIMEOUT.
  738. cmDuration ctestTestTimeout = this->CTest->GetTimeOut();
  739. if (ctestTestTimeout > cmDuration::zero()) {
  740. timeout = ctestTestTimeout;
  741. }
  742. }
  743. }
  744. // Check CTEST_TIME_LIMIT.
  745. cmDuration timeRemaining = this->CTest->GetRemainingTimeAllowed();
  746. if (timeRemaining != cmCTest::MaxDuration()) {
  747. // This two minute buffer is historical.
  748. timeRemaining -= std::chrono::minutes(2);
  749. }
  750. // Check --stop-time.
  751. std::chrono::system_clock::time_point stop_time = this->CTest->GetStopTime();
  752. if (stop_time != std::chrono::system_clock::time_point()) {
  753. cmDuration timeUntilStop =
  754. (stop_time - std::chrono::system_clock::now()) % std::chrono::hours(24);
  755. if (timeUntilStop < timeRemaining) {
  756. timeRemaining = timeUntilStop;
  757. }
  758. }
  759. // Enforce remaining time even over explicit TIMEOUT=0.
  760. if (timeRemaining <= cmDuration::zero()) {
  761. timeRemaining = cmDuration::zero();
  762. }
  763. if (!timeout || timeRemaining < *timeout) {
  764. timeout = timeRemaining;
  765. this->TestProcess->SetTimeoutReason(cmProcess::TimeoutReason::StopTime);
  766. }
  767. if (timeout) {
  768. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  769. this->Index << ": "
  770. << "Test timeout computed to be: "
  771. << cmDurationTo<unsigned int>(*timeout)
  772. << "\n",
  773. this->TestHandler->GetQuiet());
  774. this->TestProcess->SetTimeout(*timeout);
  775. } else {
  776. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  777. this->Index
  778. << ": "
  779. << "Test timeout suppressed by TIMEOUT property.\n",
  780. this->TestHandler->GetQuiet());
  781. }
  782. cmSystemTools::SaveRestoreEnvironment sre;
  783. std::ostringstream envMeasurement;
  784. // We split processing ENVIRONMENT and ENVIRONMENT_MODIFICATION into two
  785. // phases to ensure that MYVAR=reset: in the latter phase resets to the
  786. // former phase's settings, rather than to the original environment.
  787. if (!this->TestProperties->Environment.empty()) {
  788. cmSystemTools::EnvDiff diff;
  789. diff.AppendEnv(this->TestProperties->Environment);
  790. diff.ApplyToCurrentEnv(&envMeasurement);
  791. }
  792. if (!this->TestProperties->EnvironmentModification.empty()) {
  793. cmSystemTools::EnvDiff diff;
  794. bool env_ok = true;
  795. for (auto const& envmod : this->TestProperties->EnvironmentModification) {
  796. env_ok &= diff.ParseOperation(envmod);
  797. }
  798. if (!env_ok) {
  799. return false;
  800. }
  801. diff.ApplyToCurrentEnv(&envMeasurement);
  802. }
  803. if (this->UseAllocatedResources) {
  804. std::vector<std::string> envLog;
  805. this->SetupResourcesEnvironment(&envLog);
  806. for (auto const& var : envLog) {
  807. envMeasurement << var << std::endl;
  808. }
  809. } else {
  810. cmSystemTools::UnsetEnv("CTEST_RESOURCE_GROUP_COUNT");
  811. // Signify that this variable is being actively unset
  812. envMeasurement << "#CTEST_RESOURCE_GROUP_COUNT=" << std::endl;
  813. }
  814. this->TestResult.Environment = envMeasurement.str();
  815. // Remove last newline
  816. this->TestResult.Environment.erase(this->TestResult.Environment.length() -
  817. 1);
  818. return this->TestProcess->StartProcess(*this->MultiTestHandler.Loop,
  819. &this->TestProperties->Affinity);
  820. }
  821. void cmCTestRunTest::SetupResourcesEnvironment(std::vector<std::string>* log)
  822. {
  823. std::string processCount = "CTEST_RESOURCE_GROUP_COUNT=";
  824. processCount += std::to_string(this->AllocatedResources.size());
  825. cmSystemTools::PutEnv(processCount);
  826. if (log) {
  827. log->push_back(processCount);
  828. }
  829. std::size_t i = 0;
  830. for (auto const& process : this->AllocatedResources) {
  831. std::string prefix = "CTEST_RESOURCE_GROUP_";
  832. prefix += std::to_string(i);
  833. std::string resourceList = prefix + '=';
  834. prefix += '_';
  835. bool firstType = true;
  836. for (auto const& it : process) {
  837. if (!firstType) {
  838. resourceList += ',';
  839. }
  840. firstType = false;
  841. auto resourceType = it.first;
  842. resourceList += resourceType;
  843. std::string var = prefix + cmSystemTools::UpperCase(resourceType) + '=';
  844. bool firstName = true;
  845. for (auto const& it2 : it.second) {
  846. if (!firstName) {
  847. var += ';';
  848. }
  849. firstName = false;
  850. var += "id:" + it2.Id + ",slots:" + std::to_string(it2.Slots);
  851. }
  852. cmSystemTools::PutEnv(var);
  853. if (log) {
  854. log->push_back(var);
  855. }
  856. }
  857. cmSystemTools::PutEnv(resourceList);
  858. if (log) {
  859. log->push_back(resourceList);
  860. }
  861. ++i;
  862. }
  863. }
  864. void cmCTestRunTest::WriteLogOutputTop(size_t completed, size_t total)
  865. {
  866. std::ostringstream outputStream;
  867. // If this is the last or only run of this test, or progress output is
  868. // requested, then print out completed / total.
  869. // Only issue is if a test fails and we are running until fail
  870. // then it will never print out the completed / total, same would
  871. // got for run until pass. Trick is when this is called we don't
  872. // yet know if we are passing or failing.
  873. bool const progressOnLast =
  874. (this->RepeatMode != cmCTest::Repeat::UntilPass &&
  875. this->RepeatMode != cmCTest::Repeat::AfterTimeout);
  876. if ((progressOnLast && this->NumberOfRunsLeft == 1) ||
  877. (!progressOnLast && this->NumberOfRunsLeft == this->NumberOfRunsTotal) ||
  878. this->CTest->GetTestProgressOutput()) {
  879. outputStream << std::setw(getNumWidth(total)) << completed << "/";
  880. outputStream << std::setw(getNumWidth(total)) << total << " ";
  881. }
  882. // if this is one of several runs of a test just print blank space
  883. // to keep things neat
  884. else {
  885. outputStream << std::setw(getNumWidth(total)) << " ";
  886. outputStream << std::setw(getNumWidth(total)) << " ";
  887. }
  888. if (this->TestHandler->MemCheck) {
  889. outputStream << "MemCheck";
  890. } else {
  891. outputStream << "Test";
  892. }
  893. std::ostringstream indexStr;
  894. indexStr << " #" << this->Index << ":";
  895. outputStream << std::setw(3 + getNumWidth(this->TestHandler->GetMaxIndex()))
  896. << indexStr.str();
  897. outputStream << " ";
  898. const int maxTestNameWidth = this->CTest->GetMaxTestNameWidth();
  899. std::string outname = this->TestProperties->Name + " ";
  900. outname.resize(maxTestNameWidth + 4, '.');
  901. outputStream << outname;
  902. *this->TestHandler->LogFile << this->TestProperties->Index << "/"
  903. << this->TestHandler->TotalNumberOfTests
  904. << " Testing: " << this->TestProperties->Name
  905. << std::endl;
  906. *this->TestHandler->LogFile << this->TestProperties->Index << "/"
  907. << this->TestHandler->TotalNumberOfTests
  908. << " Test: " << this->TestProperties->Name
  909. << std::endl;
  910. *this->TestHandler->LogFile << "Command: \"" << this->ActualCommand << "\"";
  911. for (std::string const& arg : this->Arguments) {
  912. *this->TestHandler->LogFile << " \"" << arg << "\"";
  913. }
  914. *this->TestHandler->LogFile
  915. << std::endl
  916. << "Directory: " << this->TestProperties->Directory << std::endl
  917. << "\"" << this->TestProperties->Name
  918. << "\" start time: " << this->StartTime << std::endl;
  919. *this->TestHandler->LogFile
  920. << "Output:" << std::endl
  921. << "----------------------------------------------------------"
  922. << std::endl;
  923. *this->TestHandler->LogFile << this->ProcessOutput << "<end of output>"
  924. << std::endl;
  925. if (!this->CTest->GetTestProgressOutput()) {
  926. cmCTestLog(this->CTest, HANDLER_OUTPUT, outputStream.str());
  927. }
  928. cmCTestLog(this->CTest, DEBUG,
  929. "Testing " << this->TestProperties->Name << " ... ");
  930. }
  931. void cmCTestRunTest::FinalizeTest(bool started)
  932. {
  933. if (this->Instrumentation.HasQuery()) {
  934. this->Instrumentation.InstrumentTest(
  935. this->TestProperties->Name, this->ActualCommand, this->Arguments,
  936. this->TestProcess->GetExitValue(), this->TestProcess->GetStartTime(),
  937. this->TestProcess->GetSystemStartTime());
  938. }
  939. this->MultiTestHandler.FinishTestProcess(this->TestProcess->GetRunner(),
  940. started);
  941. }