cmCTestRunTest.cxx 27.8 KB
Newer Older
1 2
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
3
#include "cmCTestRunTest.h"
Brad King's avatar
Brad King committed
4

5
#include "cmCTest.h"
6
#include "cmCTestMemCheckHandler.h"
7
#include "cmCTestMultiProcessHandler.h"
8
#include "cmProcess.h"
9
#include "cmSystemTools.h"
10
#include "cmWorkingDirectory.h"
11

12
#include "cmsys/RegularExpression.hxx"
13
#include <chrono>
14
#include <cmAlgorithms.h>
15
#include <cstdint>
16
#include <cstring>
17
#include <iomanip>
18
#include <ratio>
19 20 21
#include <sstream>
#include <stdio.h>
#include <utility>
22

23 24
cmCTestRunTest::cmCTestRunTest(cmCTestMultiProcessHandler& multiHandler)
  : MultiTestHandler(multiHandler)
25
{
26 27
  this->CTest = multiHandler.CTest;
  this->TestHandler = multiHandler.TestHandler;
Wouter Klouwen's avatar
Wouter Klouwen committed
28
  this->TestResult.ExecutionTime = cmDuration::zero();
Bill Hoffman's avatar
Bill Hoffman committed
29
  this->TestResult.ReturnValue = 0;
30
  this->TestResult.Status = cmCTestTestHandler::NOT_RUN;
Bill Hoffman's avatar
Bill Hoffman committed
31
  this->TestResult.TestCount = 0;
Daniel Pfeifer's avatar
Daniel Pfeifer committed
32
  this->TestResult.Properties = nullptr;
33 34
  this->NumberOfRunsLeft = 1; // default to 1 run of the test
  this->RunUntilFail = false; // default to run the test once
35
  this->RunAgain = false;     // default to not having to run again
36 37
}

38
void cmCTestRunTest::CheckOutput(std::string const& line)
39
{
40 41
  cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
             this->GetIndex() << ": " << line << std::endl);
42 43 44 45 46 47
  this->ProcessOutput += line;
  this->ProcessOutput += "\n";

  // Check for TIMEOUT_AFTER_MATCH property.
  if (!this->TestProperties->TimeoutRegularExpressions.empty()) {
    for (auto& reg : this->TestProperties->TimeoutRegularExpressions) {
48
      if (reg.first.find(this->ProcessOutput)) {
49 50
        cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                   this->GetIndex()
51 52 53 54 55 56 57 58 59 60 61
                     << ": "
                     << "Test timeout changed to "
                     << std::chrono::duration_cast<std::chrono::seconds>(
                          this->TestProperties->AlternateTimeout)
                          .count()
                     << std::endl);
        this->TestProcess->ResetStartTime();
        this->TestProcess->ChangeTimeout(
          this->TestProperties->AlternateTimeout);
        this->TestProperties->TimeoutRegularExpressions.clear();
        break;
62
      }
63
    }
64
  }
65
}
66

67
bool cmCTestRunTest::EndTest(size_t completed, size_t total, bool started)
68
{
69
  this->WriteLogOutputTop(completed, total);
70 71
  std::string reason;
  bool passed = true;
72 73
  cmProcess::State res =
    started ? this->TestProcess->GetProcessStatus() : cmProcess::State::Error;
74 75 76
  if (res != cmProcess::State::Expired) {
    this->TimeoutIsForStopTime = false;
  }
77
  std::int64_t retVal = this->TestProcess->GetExitValue();
78
  bool forceFail = false;
79
  bool skipped = false;
80
  bool outputTestErrorsToConsole = false;
81 82
  if (!this->TestProperties->RequiredRegularExpressions.empty() &&
      this->FailedDependencies.empty()) {
83
    bool found = false;
84
    for (auto& pass : this->TestProperties->RequiredRegularExpressions) {
85
      if (pass.first.find(this->ProcessOutput)) {
86 87
        found = true;
        reason = "Required regular expression found.";
88
        break;
89
      }
90 91
    }
    if (!found) {
92 93
      reason = "Required regular expression not found.";
      forceFail = true;
94 95
    }
    reason += "Regex=[";
96 97
    for (auto& pass : this->TestProperties->RequiredRegularExpressions) {
      reason += pass.second;
98 99
      reason += "\n";
    }
100 101
    reason += "]";
  }
102 103
  if (!this->TestProperties->ErrorRegularExpressions.empty() &&
      this->FailedDependencies.empty()) {
104
    for (auto& pass : this->TestProperties->ErrorRegularExpressions) {
105
      if (pass.first.find(this->ProcessOutput)) {
106 107
        reason = "Error regular expression found in output.";
        reason += " Regex=[";
108
        reason += pass.second;
109 110
        reason += "]";
        forceFail = true;
111
        break;
112
      }
113
    }
114
  }
115
  std::ostringstream outputStream;
116
  if (res == cmProcess::State::Exited) {
117
    bool success = !forceFail &&
118 119
      (retVal == 0 ||
       !this->TestProperties->RequiredRegularExpressions.empty());
120 121
    if (this->TestProperties->SkipReturnCode >= 0 &&
        this->TestProperties->SkipReturnCode == retVal) {
122
      this->TestResult.Status = cmCTestTestHandler::NOT_RUN;
123 124 125
      std::ostringstream s;
      s << "SKIP_RETURN_CODE=" << this->TestProperties->SkipReturnCode;
      this->TestResult.CompletionStatus = s.str();
126
      cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Skipped ");
127
      skipped = true;
128 129
    } else if ((success && !this->TestProperties->WillFail) ||
               (!success && this->TestProperties->WillFail)) {
130
      this->TestResult.Status = cmCTestTestHandler::COMPLETED;
131
      outputStream << "   Passed  ";
132
    } else {
133
      this->TestResult.Status = cmCTestTestHandler::FAILED;
134
      outputStream << "***Failed  " << reason;
135
      outputTestErrorsToConsole = this->CTest->OutputTestOutputOnTestFailure;
136
    }
137
  } else if (res == cmProcess::State::Expired) {
138
    outputStream << "***Timeout ";
139
    this->TestResult.Status = cmCTestTestHandler::TIMEOUT;
140
    outputTestErrorsToConsole = this->CTest->OutputTestOutputOnTestFailure;
141
  } else if (res == cmProcess::State::Exception) {
142
    outputTestErrorsToConsole = this->CTest->OutputTestOutputOnTestFailure;
143
    outputStream << "***Exception: ";
144 145
    this->TestResult.ExceptionStatus =
      this->TestProcess->GetExitExceptionString();
146
    switch (this->TestProcess->GetExitException()) {
147
      case cmProcess::Exception::Fault:
148
        outputStream << "SegFault";
149 150
        this->TestResult.Status = cmCTestTestHandler::SEGFAULT;
        break;
151
      case cmProcess::Exception::Illegal:
152
        outputStream << "Illegal";
153 154
        this->TestResult.Status = cmCTestTestHandler::ILLEGAL;
        break;
155
      case cmProcess::Exception::Interrupt:
156
        outputStream << "Interrupt";
157 158
        this->TestResult.Status = cmCTestTestHandler::INTERRUPT;
        break;
159
      case cmProcess::Exception::Numerical:
160
        outputStream << "Numerical";
161 162 163
        this->TestResult.Status = cmCTestTestHandler::NUMERICAL;
        break;
      default:
164 165
        cmCTestLog(this->CTest, HANDLER_OUTPUT,
                   this->TestResult.ExceptionStatus);
166 167
        this->TestResult.Status = cmCTestTestHandler::OTHER_FAULT;
    }
Betsy McPhail's avatar
Betsy McPhail committed
168
  } else if ("Disabled" == this->TestResult.CompletionStatus) {
169
    outputStream << "***Not Run (Disabled) ";
170
  } else // cmProcess::State::Error
171
  {
172
    outputStream << "***Not Run ";
173
  }
174

175 176
  passed = this->TestResult.Status == cmCTestTestHandler::COMPLETED;
  char buf[1024];
177
  sprintf(buf, "%6.2f sec", this->TestProcess->GetTotalTime().count());
178 179
  outputStream << buf << "\n";

180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
  if (this->CTest->GetTestProgressOutput()) {
    if (!passed) {
      // If the test did not pass, reprint test name and error
      std::string output = GetTestPrefix(completed, total);
      std::string testName = this->TestProperties->Name;
      const int maxTestNameWidth = this->CTest->GetMaxTestNameWidth();
      testName.resize(maxTestNameWidth + 4, '.');

      output += testName;
      output += outputStream.str();
      outputStream.str("");
      outputStream.clear();
      outputStream << output;
      cmCTestLog(this->CTest, HANDLER_TEST_PROGRESS_OUTPUT, "\n"); // flush
    }
    if (completed == total) {
      std::string testName =
        GetTestPrefix(completed, total) + this->TestProperties->Name + "\n";
      cmCTestLog(this->CTest, HANDLER_TEST_PROGRESS_OUTPUT, testName);
    }
  }
  if (!this->CTest->GetTestProgressOutput() || !passed) {
    cmCTestLog(this->CTest, HANDLER_OUTPUT, outputStream.str());
  }
204

205 206 207
  if (outputTestErrorsToConsole) {
    cmCTestLog(this->CTest, HANDLER_OUTPUT, this->ProcessOutput << std::endl);
  }
208

209
  if (this->TestHandler->LogFile) {
210
    *this->TestHandler->LogFile << "Test time = " << buf << std::endl;
211
  }
212

213
  this->DartProcessing();
214

215 216
  // if this is doing MemCheck then all the output needs to be put into
  // Output since that is what is parsed by cmCTestMemCheckHandler
217 218 219 220 221 222 223 224
  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));
  }
225
  this->TestResult.Reason = reason;
226
  if (this->TestHandler->LogFile) {
227 228
    bool pass = true;
    const char* reasonType = "Test Pass Reason";
229 230
    if (this->TestResult.Status != cmCTestTestHandler::COMPLETED &&
        this->TestResult.Status != cmCTestTestHandler::NOT_RUN) {
231 232
      reasonType = "Test Fail Reason";
      pass = false;
233
    }
234 235 236 237 238 239
    auto ttime = this->TestProcess->GetTotalTime();
    auto hours = std::chrono::duration_cast<std::chrono::hours>(ttime);
    ttime -= hours;
    auto minutes = std::chrono::duration_cast<std::chrono::minutes>(ttime);
    ttime -= minutes;
    auto seconds = std::chrono::duration_cast<std::chrono::seconds>(ttime);
240
    char buffer[100];
241 242 243
    sprintf(buffer, "%02d:%02d:%02d", static_cast<unsigned>(hours.count()),
            static_cast<unsigned>(minutes.count()),
            static_cast<unsigned>(seconds.count()));
244 245 246
    *this->TestHandler->LogFile
      << "----------------------------------------------------------"
      << std::endl;
247
    if (!this->TestResult.Reason.empty()) {
248
      *this->TestHandler->LogFile << reasonType << ":\n"
249 250 251
                                  << this->TestResult.Reason << "\n";
    } else {
      if (pass) {
252
        *this->TestHandler->LogFile << "Test Passed.\n";
253
      } else {
254 255
        *this->TestHandler->LogFile << "Test Failed.\n";
      }
256 257 258
    }
    *this->TestHandler->LogFile
      << "\"" << this->TestProperties->Name
259
      << "\" end time: " << this->CTest->CurrentTime() << std::endl
260 261
      << "\"" << this->TestProperties->Name << "\" time elapsed: " << buffer
      << std::endl
262
      << "----------------------------------------------------------"
263 264 265
      << std::endl
      << std::endl;
  }
266 267
  // if the test actually started and ran
  // record the results in TestResult
268
  if (started) {
269 270 271 272 273 274 275 276 277 278
    std::string compressedOutput;
    if (!this->TestHandler->MemCheck &&
        this->CTest->ShouldCompressTestOutput()) {
      std::string str = this->ProcessOutput;
      if (this->CTest->CompressString(str)) {
        compressedOutput = std::move(str);
      }
    }
    bool compress = !compressedOutput.empty() &&
      compressedOutput.length() < this->ProcessOutput.length();
279
    this->TestResult.Output =
280
      compress ? compressedOutput : this->ProcessOutput;
281
    this->TestResult.CompressOutput = compress;
282
    this->TestResult.ReturnValue = this->TestProcess->GetExitValue();
283 284 285
    if (!skipped) {
      this->TestResult.CompletionStatus = "Completed";
    }
286
    this->TestResult.ExecutionTime = this->TestProcess->GetTotalTime();
287
    this->MemCheckPostProcess();
288
    this->ComputeWeightedCost();
289
  }
290
  // If the test does not need to rerun push the current TestResult onto the
291
  // TestHandler vector
292
  if (!this->NeedsToRerun()) {
293
    this->TestHandler->TestResults.push_back(this->TestResult);
294
  }
295
  this->TestProcess.reset();
296
  return passed || skipped;
297 298
}

299
bool cmCTestRunTest::StartAgain(size_t completed)
300
{
301
  if (!this->RunAgain) {
302
    return false;
303
  }
304 305
  this->RunAgain = false; // reset
  // change to tests directory
306
  cmWorkingDirectory workdir(this->TestProperties->Directory);
307 308 309 310 311 312 313
  if (workdir.Failed()) {
    this->StartFailure("Failed to change working directory to " +
                       this->TestProperties->Directory + " : " +
                       std::strerror(workdir.GetLastResult()));
    return true;
  }

314
  this->StartTest(completed, this->TotalNumberOfTests);
315 316 317 318 319 320
  return true;
}

bool cmCTestRunTest::NeedsToRerun()
{
  this->NumberOfRunsLeft--;
321
  if (this->NumberOfRunsLeft == 0) {
322
    return false;
323
  }
324 325 326
  // 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
327 328
  if (this->RunUntilFail &&
      this->TestResult.Status == cmCTestTestHandler::COMPLETED) {
329 330
    this->RunAgain = true;
    return true;
331
  }
332 333
  return false;
}
334 335
void cmCTestRunTest::ComputeWeightedCost()
{
David Cole's avatar
David Cole committed
336 337
  double prev = static_cast<double>(this->TestProperties->PreviousRuns);
  double avgcost = static_cast<double>(this->TestProperties->Cost);
338
  double current = this->TestResult.ExecutionTime.count();
339

340
  if (this->TestResult.Status == cmCTestTestHandler::COMPLETED) {
David Cole's avatar
David Cole committed
341 342
    this->TestProperties->Cost =
      static_cast<float>(((prev * avgcost) + current) / (prev + 1.0));
343
    this->TestProperties->PreviousRuns++;
344
  }
345 346
}

Zach's avatar
Zach committed
347 348
void cmCTestRunTest::MemCheckPostProcess()
{
349
  if (!this->TestHandler->MemCheck) {
Zach's avatar
Zach committed
350
    return;
351
  }
352 353 354 355
  cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                     this->Index << ": process test output now: "
                                 << this->TestProperties->Name << " "
                                 << this->TestResult.Name << std::endl,
356 357 358
                     this->TestHandler->GetQuiet());
  cmCTestMemCheckHandler* handler =
    static_cast<cmCTestMemCheckHandler*>(this->TestHandler);
359
  handler->PostProcessTest(this->TestResult, this->Index);
Zach's avatar
Zach committed
360 361
}

362 363 364 365
void cmCTestRunTest::StartFailure(std::string const& output)
{
  // Still need to log the Start message so the test summary records our
  // attempt to start this test
366 367 368 369 370 371 372 373
  if (!this->CTest->GetTestProgressOutput()) {
    cmCTestLog(this->CTest, HANDLER_OUTPUT,
               std::setw(2 * getNumWidth(this->TotalNumberOfTests) + 8)
                 << "Start "
                 << std::setw(getNumWidth(this->TestHandler->GetMaxIndex()))
                 << this->TestProperties->Index << ": "
                 << this->TestProperties->Name << std::endl);
  }
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394

  this->ProcessOutput.clear();
  if (!output.empty()) {
    *this->TestHandler->LogFile << output << std::endl;
    cmCTestLog(this->CTest, ERROR_MESSAGE, output << std::endl);
  }

  this->TestResult.Properties = this->TestProperties;
  this->TestResult.ExecutionTime = cmDuration::zero();
  this->TestResult.CompressOutput = false;
  this->TestResult.ReturnValue = -1;
  this->TestResult.CompletionStatus = "Failed to start";
  this->TestResult.Status = cmCTestTestHandler::NOT_RUN;
  this->TestResult.TestCount = this->TestProperties->Index;
  this->TestResult.Name = this->TestProperties->Name;
  this->TestResult.Path = this->TestProperties->Directory;
  this->TestResult.Output = output;
  this->TestResult.FullCommandLine.clear();
  this->TestProcess = cm::make_unique<cmProcess>(*this);
}

395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415
std::string cmCTestRunTest::GetTestPrefix(size_t completed, size_t total) const
{
  std::ostringstream outputStream;
  outputStream << std::setw(getNumWidth(total)) << completed << "/";
  outputStream << std::setw(getNumWidth(total)) << total << " ";

  if (this->TestHandler->MemCheck) {
    outputStream << "MemCheck";
  } else {
    outputStream << "Test";
  }

  std::ostringstream indexStr;
  indexStr << " #" << this->Index << ":";
  outputStream << std::setw(3 + getNumWidth(this->TestHandler->GetMaxIndex()))
               << indexStr.str();
  outputStream << " ";

  return outputStream.str();
}

416
// Starts the execution of a test.  Returns once it has started
417
bool cmCTestRunTest::StartTest(size_t completed, size_t total)
418
{
419
  this->TotalNumberOfTests = total; // save for rerun case
420 421 422 423 424 425 426 427 428 429 430 431 432
  if (!this->CTest->GetTestProgressOutput()) {
    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);
  } else {
    std::string testName =
      GetTestPrefix(completed, total) + this->TestProperties->Name + "\n";
    cmCTestLog(this->CTest, HANDLER_TEST_PROGRESS_OUTPUT, testName);
  }

433
  this->ProcessOutput.clear();
Betsy McPhail's avatar
Betsy McPhail committed
434

Gregor Jasny's avatar
Gregor Jasny committed
435 436 437 438 439 440 441 442
  this->TestResult.Properties = this->TestProperties;
  this->TestResult.ExecutionTime = cmDuration::zero();
  this->TestResult.CompressOutput = false;
  this->TestResult.ReturnValue = -1;
  this->TestResult.TestCount = this->TestProperties->Index;
  this->TestResult.Name = this->TestProperties->Name;
  this->TestResult.Path = this->TestProperties->Directory;

Betsy McPhail's avatar
Betsy McPhail committed
443 444 445 446
  // Return immediately if test is disabled
  if (this->TestProperties->Disabled) {
    this->TestResult.CompletionStatus = "Disabled";
    this->TestResult.Status = cmCTestTestHandler::NOT_RUN;
447
    this->TestProcess = cm::make_unique<cmProcess>(*this);
Betsy McPhail's avatar
Betsy McPhail committed
448
    this->TestResult.Output = "Disabled";
449
    this->TestResult.FullCommandLine.clear();
Betsy McPhail's avatar
Betsy McPhail committed
450 451 452
    return false;
  }

453 454
  this->TestResult.CompletionStatus = "Failed to start";
  this->TestResult.Status = cmCTestTestHandler::BAD_COMMAND;
455

456 457 458 459
  // Check for failed fixture dependencies before we even look at the command
  // arguments because if we are not going to run the test, the command and
  // its arguments are irrelevant. This matters for the case where a fixture
  // dependency might be creating the executable we want to run.
460
  if (!this->FailedDependencies.empty()) {
461
    this->TestProcess = cm::make_unique<cmProcess>(*this);
462
    std::string msg = "Failed test dependencies:";
463 464
    for (std::string const& failedDep : this->FailedDependencies) {
      msg += " " + failedDep;
465 466 467 468
    }
    *this->TestHandler->LogFile << msg << std::endl;
    cmCTestLog(this->CTest, HANDLER_OUTPUT, msg << std::endl);
    this->TestResult.Output = msg;
469
    this->TestResult.FullCommandLine.clear();
470
    this->TestResult.CompletionStatus = "Fixture dependency failed";
471 472 473 474
    this->TestResult.Status = cmCTestTestHandler::NOT_RUN;
    return false;
  }

475 476
  this->ComputeArguments();
  std::vector<std::string>& args = this->TestProperties->Args;
477
  if (args.size() >= 2 && args[1] == "NOT_AVAILABLE") {
478
    this->TestProcess = cm::make_unique<cmProcess>(*this);
479
    std::string msg;
480
    if (this->CTest->GetConfigType().empty()) {
481 482
      msg = "Test not available without configuration.";
      msg += "  (Missing \"-C <config>\"?)";
483
    } else {
484 485 486
      msg = "Test not available in configuration \"";
      msg += this->CTest->GetConfigType();
      msg += "\".";
487
    }
488 489 490
    *this->TestHandler->LogFile << msg << std::endl;
    cmCTestLog(this->CTest, ERROR_MESSAGE, msg << std::endl);
    this->TestResult.Output = msg;
491
    this->TestResult.FullCommandLine.clear();
492
    this->TestResult.CompletionStatus = "Missing Configuration";
493 494
    this->TestResult.Status = cmCTestTestHandler::NOT_RUN;
    return false;
495
  }
496

497
  // Check if all required files exist
498
  for (std::string const& file : this->TestProperties->RequiredFiles) {
499
    if (!cmSystemTools::FileExists(file)) {
500
      // Required file was not found
501
      this->TestProcess = cm::make_unique<cmProcess>(*this);
502 503 504 505
      *this->TestHandler->LogFile << "Unable to find required file: " << file
                                  << std::endl;
      cmCTestLog(this->CTest, ERROR_MESSAGE,
                 "Unable to find required file: " << file << std::endl);
506
      this->TestResult.Output = "Unable to find required file: " + file;
507
      this->TestResult.FullCommandLine.clear();
508
      this->TestResult.CompletionStatus = "Required Files Missing";
509 510 511
      this->TestResult.Status = cmCTestTestHandler::NOT_RUN;
      return false;
    }
512
  }
513
  // log and return if we did not find the executable
514
  if (this->ActualCommand.empty()) {
515
    // if the command was not found create a TestResult object
516
    // that has that information
517
    this->TestProcess = cm::make_unique<cmProcess>(*this);
518 519 520 521
    *this->TestHandler->LogFile << "Unable to find executable: " << args[1]
                                << std::endl;
    cmCTestLog(this->CTest, ERROR_MESSAGE,
               "Unable to find executable: " << args[1] << std::endl);
522
    this->TestResult.Output = "Unable to find executable: " + args[1];
523
    this->TestResult.FullCommandLine.clear();
524
    this->TestResult.CompletionStatus = "Unable to find executable";
525
    this->TestResult.Status = cmCTestTestHandler::NOT_RUN;
526
    return false;
527
  }
528 529
  this->StartTime = this->CTest->CurrentTime();

530
  auto timeout = this->TestProperties->Timeout;
Zach's avatar
Zach committed
531

532
  this->TimeoutIsForStopTime = false;
533 534
  std::chrono::system_clock::time_point stop_time = this->CTest->GetStopTime();
  if (stop_time != std::chrono::system_clock::time_point()) {
535
    std::chrono::duration<double> stop_timeout =
536 537 538
      (stop_time - std::chrono::system_clock::now()) % std::chrono::hours(24);

    if (stop_timeout <= std::chrono::duration<double>::zero()) {
539
      stop_timeout = std::chrono::duration<double>::zero();
540 541 542
    }
    if (timeout == std::chrono::duration<double>::zero() ||
        stop_timeout < timeout) {
543
      this->TimeoutIsForStopTime = true;
544 545
      timeout = stop_timeout;
    }
546
  }
547

548
  return this->ForkProcess(timeout, this->TestProperties->ExplicitTimeout,
549 550
                           &this->TestProperties->Environment,
                           &this->TestProperties->Affinity);
551 552 553 554
}

void cmCTestRunTest::ComputeArguments()
{
luz.paz's avatar
luz.paz committed
555
  this->Arguments.clear(); // reset because this might be a rerun
Zach's avatar
Zach committed
556
  std::vector<std::string>::const_iterator j =
557 558
    this->TestProperties->Args.begin();
  ++j; // skip test name
Zach's avatar
Zach committed
559
  // find the test executable
560 561 562
  if (this->TestHandler->MemCheck) {
    cmCTestMemCheckHandler* handler =
      static_cast<cmCTestMemCheckHandler*>(this->TestHandler);
563
    this->ActualCommand = handler->MemoryTester;
564 565
    this->TestProperties->Args[1] = this->TestHandler->FindTheExecutable(
      this->TestProperties->Args[1].c_str());
566 567
  } else {
    this->ActualCommand = this->TestHandler->FindTheExecutable(
Zach's avatar
Zach committed
568
      this->TestProperties->Args[1].c_str());
569 570 571
    ++j; // skip the executable (it will be actualCommand)
  }
  std::string testCommand =
572
    cmSystemTools::ConvertToOutputPath(this->ActualCommand);
Zach's avatar
Zach committed
573

574
  // Prepends memcheck args to our command string
575
  this->TestHandler->GenerateTestCommand(this->Arguments, this->Index);
576
  for (std::string const& arg : this->Arguments) {
577
    testCommand += " \"";
578
    testCommand += arg;
579
    testCommand += "\"";
580
  }
Zach's avatar
Zach committed
581

582
  for (; j != this->TestProperties->Args.end(); ++j) {
583 584 585
    testCommand += " \"";
    testCommand += *j;
    testCommand += "\"";
586
    this->Arguments.push_back(*j);
587
  }
588
  this->TestResult.FullCommandLine = testCommand;
589

590
  // Print the test command in verbose mode
591 592
  cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
             std::endl
593 594 595
               << this->Index << ": "
               << (this->TestHandler->MemCheck ? "MemCheck" : "Test")
               << " command: " << testCommand << std::endl);
596 597

  // Print any test-specific env vars in verbose mode
598
  if (!this->TestProperties->Environment.empty()) {
599 600 601
    cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
               this->Index << ": "
                           << "Environment variables: " << std::endl);
602
  }
603
  for (std::string const& env : this->TestProperties->Environment) {
604 605
    cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
               this->Index << ":  " << env << std::endl);
606
  }
607 608 609
}

void cmCTestRunTest::DartProcessing()
610
{
611
  if (!this->ProcessOutput.empty() &&
612
      this->ProcessOutput.find("<DartMeasurement") != std::string::npos) {
613
    if (this->TestHandler->DartStuff.find(this->ProcessOutput)) {
614
      this->TestResult.DartString = this->TestHandler->DartStuff.match(1);
615
      // keep searching and replacing until none are left
616
      while (this->TestHandler->DartStuff1.find(this->ProcessOutput)) {
617
        // replace the exact match for the string
618 619 620
        cmSystemTools::ReplaceString(
          this->ProcessOutput, this->TestHandler->DartStuff1.match(1).c_str(),
          "");
621 622
      }
    }
623
  }
624 625
}

Wouter Klouwen's avatar
Wouter Klouwen committed
626
bool cmCTestRunTest::ForkProcess(cmDuration testTimeOut, bool explicitTimeout,
627 628
                                 std::vector<std::string>* environment,
                                 std::vector<size_t>* affinity)
629
{
630
  this->TestProcess = cm::make_unique<cmProcess>(*this);
631
  this->TestProcess->SetId(this->Index);
Gregor Jasny's avatar
Gregor Jasny committed
632 633
  this->TestProcess->SetWorkingDirectory(this->TestProperties->Directory);
  this->TestProcess->SetCommand(this->ActualCommand);
634
  this->TestProcess->SetCommandArguments(this->Arguments);
635

636
  // determine how much time we have
Wouter Klouwen's avatar
Wouter Klouwen committed
637
  cmDuration timeout = this->CTest->GetRemainingTimeAllowed();
638
  if (timeout != cmCTest::MaxDuration()) {
639 640
    timeout -= std::chrono::minutes(2);
  }
Wouter Klouwen's avatar
Wouter Klouwen committed
641
  if (this->CTest->GetTimeOut() > cmDuration::zero() &&
642
      this->CTest->GetTimeOut() < timeout) {
643
    timeout = this->CTest->GetTimeOut();
644
  }
Wouter Klouwen's avatar
Wouter Klouwen committed
645
  if (testTimeOut > cmDuration::zero() &&
646
      testTimeOut < this->CTest->GetRemainingTimeAllowed()) {
647
    timeout = testTimeOut;
648
  }
649
  // always have at least 1 second if we got to here
Wouter Klouwen's avatar
Wouter Klouwen committed
650
  if (timeout <= cmDuration::zero()) {
651
    timeout = std::chrono::seconds(1);
652
  }
653
  // handle timeout explicitly set to 0
Wouter Klouwen's avatar
Wouter Klouwen committed
654 655
  if (testTimeOut == cmDuration::zero() && explicitTimeout) {
    timeout = cmDuration::zero();
656
  }
657 658 659 660 661
  cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                     this->Index << ": "
                                 << "Test timeout computed to be: "
                                 << cmDurationTo<unsigned int>(timeout)
                                 << "\n",
662
                     this->TestHandler->GetQuiet());
663

Zach's avatar
Zach committed
664 665
  this->TestProcess->SetTimeout(timeout);

666 667 668 669
#ifdef CMAKE_BUILD_WITH_CMAKE
  cmSystemTools::SaveRestoreEnvironment sre;
#endif

670
  if (environment && !environment->empty()) {
671
    cmSystemTools::AppendEnv(*environment);
672
  }
673

674 675
  return this->TestProcess->StartProcess(this->MultiTestHandler.Loop,
                                         affinity);
676 677
}

678
void cmCTestRunTest::WriteLogOutputTop(size_t completed, size_t total)
679
{
680 681
  std::ostringstream outputStream;

682 683
  // If this is the last or only run of this test, or progress output is
  // requested, then print out completed / total.
684 685 686 687
  // 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.
688
  if (this->NumberOfRunsLeft == 1 || this->CTest->GetTestProgressOutput()) {
689 690
    outputStream << std::setw(getNumWidth(total)) << completed << "/";
    outputStream << std::setw(getNumWidth(total)) << total << " ";
691
  }
692 693
  // if this is one of several runs of a test just print blank space
  // to keep things neat
694
  else {
695 696
    outputStream << std::setw(getNumWidth(total)) << "  ";
    outputStream << std::setw(getNumWidth(total)) << "  ";
697
  }
698

699
  if (this->TestHandler->MemCheck) {
700
    outputStream << "MemCheck";
701
  } else {
702
    outputStream << "Test";
703
  }
704

705
  std::ostringstream indexStr;
Zach's avatar
Zach committed
706
  indexStr << " #" << this->Index << ":";
707 708 709 710
  outputStream << std::setw(3 + getNumWidth(this->TestHandler->GetMaxIndex()))
               << indexStr.str();
  outputStream << " ";

711 712
  const int maxTestNameWidth = this->CTest->GetMaxTestNameWidth();
  std::string outname = this->TestProperties->Name + " ";