resolve cyclic dependency with zstd
[platform/upstream/cmake.git] / Source / cmCTest.cxx
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 "cmCTest.h"
4
5 #include <algorithm>
6 #include <cctype>
7 #include <chrono>
8 #include <cstdio>
9 #include <cstdlib>
10 #include <cstring>
11 #include <ctime>
12 #include <iostream>
13 #include <map>
14 #include <sstream>
15 #include <string>
16 #include <utility>
17 #include <vector>
18
19 #include <cm/memory>
20 #include <cm/optional>
21 #include <cm/string_view>
22 #include <cmext/algorithm>
23 #include <cmext/string_view>
24
25 #include <cm3p/curl/curl.h>
26 #include <cm3p/zlib.h>
27
28 #include "cmsys/Base64.h"
29 #include "cmsys/Directory.hxx"
30 #include "cmsys/FStream.hxx"
31 #include "cmsys/Glob.hxx"
32 #include "cmsys/Process.h"
33 #include "cmsys/RegularExpression.hxx"
34 #include "cmsys/SystemInformation.hxx"
35 #if defined(_WIN32)
36 #  include <windows.h> // IWYU pragma: keep
37 #else
38 #  include <unistd.h> // IWYU pragma: keep
39 #endif
40
41 #include "cmCMakePresetsGraph.h"
42 #include "cmCTestBuildAndTestHandler.h"
43 #include "cmCTestBuildHandler.h"
44 #include "cmCTestConfigureHandler.h"
45 #include "cmCTestCoverageHandler.h"
46 #include "cmCTestGenericHandler.h"
47 #include "cmCTestMemCheckHandler.h"
48 #include "cmCTestScriptHandler.h"
49 #include "cmCTestStartCommand.h"
50 #include "cmCTestSubmitHandler.h"
51 #include "cmCTestTestHandler.h"
52 #include "cmCTestUpdateHandler.h"
53 #include "cmCTestUploadHandler.h"
54 #include "cmDynamicLoader.h"
55 #include "cmGeneratedFileStream.h"
56 #include "cmGlobalGenerator.h"
57 #include "cmMakefile.h"
58 #include "cmProcessOutput.h"
59 #include "cmState.h"
60 #include "cmStateSnapshot.h"
61 #include "cmStateTypes.h"
62 #include "cmStringAlgorithms.h"
63 #include "cmSystemTools.h"
64 #include "cmValue.h"
65 #include "cmVersion.h"
66 #include "cmVersionConfig.h"
67 #include "cmXMLWriter.h"
68 #include "cmake.h"
69
70 #if defined(__BEOS__) || defined(__HAIKU__)
71 #  include <be/kernel/OS.h> /* disable_debugger() API. */
72 #endif
73
74 struct cmCTest::Private
75 {
76   /** Representation of one part.  */
77   struct PartInfo
78   {
79     void SetName(const std::string& name) { this->Name = name; }
80     const std::string& GetName() const { return this->Name; }
81
82     void Enable() { this->Enabled = true; }
83     explicit operator bool() const { return this->Enabled; }
84
85     std::vector<std::string> SubmitFiles;
86
87   private:
88     bool Enabled = false;
89     std::string Name;
90   };
91
92   int RepeatCount = 1; // default to run each test once
93   cmCTest::Repeat RepeatMode = cmCTest::Repeat::Never;
94   std::string ConfigType;
95   std::string ScheduleType;
96   std::chrono::system_clock::time_point StopTime;
97   bool StopOnFailure = false;
98   bool TestProgressOutput = false;
99   bool Verbose = false;
100   bool ExtraVerbose = false;
101   bool ProduceXML = false;
102   bool LabelSummary = true;
103   bool SubprojectSummary = true;
104   bool UseHTTP10 = false;
105   bool PrintLabels = false;
106   bool Failover = false;
107
108   bool FlushTestProgressLine = false;
109
110   bool ForceNewCTestProcess = false;
111
112   bool RunConfigurationScript = false;
113
114   // these are helper classes
115   cmCTestBuildHandler BuildHandler;
116   cmCTestBuildAndTestHandler BuildAndTestHandler;
117   cmCTestCoverageHandler CoverageHandler;
118   cmCTestScriptHandler ScriptHandler;
119   cmCTestTestHandler TestHandler;
120   cmCTestUpdateHandler UpdateHandler;
121   cmCTestConfigureHandler ConfigureHandler;
122   cmCTestMemCheckHandler MemCheckHandler;
123   cmCTestSubmitHandler SubmitHandler;
124   cmCTestUploadHandler UploadHandler;
125
126   std::vector<cmCTestGenericHandler*> GetTestingHandlers()
127   {
128     return { &this->BuildHandler,     &this->BuildAndTestHandler,
129              &this->CoverageHandler,  &this->ScriptHandler,
130              &this->TestHandler,      &this->UpdateHandler,
131              &this->ConfigureHandler, &this->MemCheckHandler,
132              &this->SubmitHandler,    &this->UploadHandler };
133   }
134
135   std::map<std::string, cmCTestGenericHandler*> GetNamedTestingHandlers()
136   {
137     return { { "build", &this->BuildHandler },
138              { "buildtest", &this->BuildAndTestHandler },
139              { "coverage", &this->CoverageHandler },
140              { "script", &this->ScriptHandler },
141              { "test", &this->TestHandler },
142              { "update", &this->UpdateHandler },
143              { "configure", &this->ConfigureHandler },
144              { "memcheck", &this->MemCheckHandler },
145              { "submit", &this->SubmitHandler },
146              { "upload", &this->UploadHandler } };
147   }
148
149   bool ShowOnly = false;
150   bool OutputAsJson = false;
151   int OutputAsJsonVersion = 1;
152
153   // TODO: The ctest configuration should be a hierarchy of
154   // configuration option sources: command-line, script, ini file.
155   // Then the ini file can get re-loaded whenever it changes without
156   // affecting any higher-precedence settings.
157   std::map<std::string, std::string> CTestConfiguration;
158   std::map<std::string, std::string> CTestConfigurationOverwrites;
159
160   PartInfo Parts[PartCount];
161   std::map<std::string, Part> PartMap;
162
163   std::string CurrentTag;
164   bool TomorrowTag = false;
165
166   int TestModel = cmCTest::EXPERIMENTAL;
167   std::string SpecificGroup;
168
169   cmDuration TimeOut = cmDuration::zero();
170
171   cmDuration GlobalTimeout = cmDuration::zero();
172
173   int MaxTestNameWidth = 30;
174
175   int ParallelLevel = 1;
176   bool ParallelLevelSetInCli = false;
177
178   unsigned long TestLoad = 0;
179
180   int CompatibilityMode;
181
182   // information for the --build-and-test options
183   std::string BinaryDir;
184   std::string TestDir;
185
186   std::string NotesFiles;
187
188   bool InteractiveDebugMode = true;
189
190   bool ShortDateFormat = true;
191
192   bool CompressXMLFiles = false;
193   bool CompressTestOutput = true;
194
195   // By default we write output to the process output streams.
196   std::ostream* StreamOut = &std::cout;
197   std::ostream* StreamErr = &std::cerr;
198
199   bool SuppressUpdatingCTestConfiguration = false;
200
201   bool Debug = false;
202   bool ShowLineNumbers = false;
203   bool Quiet = false;
204
205   std::string BuildID;
206
207   std::vector<std::string> InitialCommandLineArguments;
208
209   int SubmitIndex = 0;
210
211   std::unique_ptr<cmGeneratedFileStream> OutputLogFile;
212   int OutputLogFileLastTag = -1;
213
214   bool OutputTestOutputOnTestFailure = false;
215   bool OutputColorCode = cmCTest::ColoredOutputSupportedByConsole();
216
217   std::map<std::string, std::string> Definitions;
218
219   cmCTest::NoTests NoTestsMode = cmCTest::NoTests::Legacy;
220 };
221
222 struct tm* cmCTest::GetNightlyTime(std::string const& str, bool tomorrowtag)
223 {
224   struct tm* lctime;
225   time_t tctime = time(nullptr);
226   lctime = gmtime(&tctime);
227   char buf[1024];
228   // add todays year day and month to the time in str because
229   // curl_getdate no longer assumes the day is today
230   snprintf(buf, sizeof(buf), "%d%02d%02d %s", lctime->tm_year + 1900,
231            lctime->tm_mon + 1, lctime->tm_mday, str.c_str());
232   cmCTestLog(this, OUTPUT,
233              "Determine Nightly Start Time" << std::endl
234                                             << "   Specified time: " << str
235                                             << std::endl);
236   // Convert the nightly start time to seconds. Since we are
237   // providing only a time and a timezone, the current date of
238   // the local machine is assumed. Consequently, nightlySeconds
239   // is the time at which the nightly dashboard was opened or
240   // will be opened on the date of the current client machine.
241   // As such, this time may be in the past or in the future.
242   time_t ntime = curl_getdate(buf, &tctime);
243   cmCTestLog(this, DEBUG, "   Get curl time: " << ntime << std::endl);
244   tctime = time(nullptr);
245   cmCTestLog(this, DEBUG, "   Get the current time: " << tctime << std::endl);
246
247   const int dayLength = 24 * 60 * 60;
248   cmCTestLog(this, DEBUG, "Seconds: " << tctime << std::endl);
249   while (ntime > tctime) {
250     // If nightlySeconds is in the past, this is the current
251     // open dashboard, then return nightlySeconds.  If
252     // nightlySeconds is in the future, this is the next
253     // dashboard to be opened, so subtract 24 hours to get the
254     // time of the current open dashboard
255     ntime -= dayLength;
256     cmCTestLog(this, DEBUG, "Pick yesterday" << std::endl);
257     cmCTestLog(this, DEBUG,
258                "   Future time, subtract day: " << ntime << std::endl);
259   }
260   while (tctime > (ntime + dayLength)) {
261     ntime += dayLength;
262     cmCTestLog(this, DEBUG, "   Past time, add day: " << ntime << std::endl);
263   }
264   cmCTestLog(this, DEBUG, "nightlySeconds: " << ntime << std::endl);
265   cmCTestLog(this, DEBUG,
266              "   Current time: " << tctime << " Nightly time: " << ntime
267                                  << std::endl);
268   if (tomorrowtag) {
269     cmCTestLog(this, OUTPUT, "   Use future tag, Add a day" << std::endl);
270     ntime += dayLength;
271   }
272   lctime = gmtime(&ntime);
273   return lctime;
274 }
275
276 bool cmCTest::GetTomorrowTag() const
277 {
278   return this->Impl->TomorrowTag;
279 }
280
281 std::string cmCTest::CleanString(const std::string& str,
282                                  std::string::size_type spos)
283 {
284   spos = str.find_first_not_of(" \n\t\r\f\v", spos);
285   std::string::size_type epos = str.find_last_not_of(" \n\t\r\f\v");
286   if (spos == std::string::npos) {
287     return std::string();
288   }
289   if (epos != std::string::npos) {
290     epos = epos - spos + 1;
291   }
292   return str.substr(spos, epos);
293 }
294
295 std::string cmCTest::CurrentTime()
296 {
297   time_t currenttime = time(nullptr);
298   struct tm* t = localtime(&currenttime);
299   // return ::CleanString(ctime(&currenttime));
300   char current_time[1024];
301   if (this->Impl->ShortDateFormat) {
302     strftime(current_time, 1000, "%b %d %H:%M %Z", t);
303   } else {
304     strftime(current_time, 1000, "%a %b %d %H:%M:%S %Z %Y", t);
305   }
306   cmCTestLog(this, DEBUG, "   Current_Time: " << current_time << std::endl);
307   return cmCTest::CleanString(current_time);
308 }
309
310 std::string cmCTest::GetCostDataFile()
311 {
312   std::string fname = this->GetCTestConfiguration("CostDataFile");
313   if (fname.empty()) {
314     fname = this->GetBinaryDir() + "/Testing/Temporary/CTestCostData.txt";
315   }
316   return fname;
317 }
318
319 std::string cmCTest::DecodeURL(const std::string& in)
320 {
321   std::string out;
322   for (const char* c = in.c_str(); *c; ++c) {
323     if (*c == '%' && isxdigit(*(c + 1)) && isxdigit(*(c + 2))) {
324       char buf[3] = { *(c + 1), *(c + 2), 0 };
325       out.append(1, static_cast<char>(strtoul(buf, nullptr, 16)));
326       c += 2;
327     } else {
328       out.append(1, *c);
329     }
330   }
331   return out;
332 }
333
334 cmCTest::cmCTest()
335   : Impl(new Private)
336 {
337   std::string envValue;
338   if (cmSystemTools::GetEnv("CTEST_OUTPUT_ON_FAILURE", envValue)) {
339     this->Impl->OutputTestOutputOnTestFailure = !cmIsOff(envValue);
340   }
341   envValue.clear();
342   if (cmSystemTools::GetEnv("CTEST_PROGRESS_OUTPUT", envValue)) {
343     this->Impl->TestProgressOutput = !cmIsOff(envValue);
344   }
345
346   this->Impl->Parts[PartStart].SetName("Start");
347   this->Impl->Parts[PartUpdate].SetName("Update");
348   this->Impl->Parts[PartConfigure].SetName("Configure");
349   this->Impl->Parts[PartBuild].SetName("Build");
350   this->Impl->Parts[PartTest].SetName("Test");
351   this->Impl->Parts[PartCoverage].SetName("Coverage");
352   this->Impl->Parts[PartMemCheck].SetName("MemCheck");
353   this->Impl->Parts[PartSubmit].SetName("Submit");
354   this->Impl->Parts[PartNotes].SetName("Notes");
355   this->Impl->Parts[PartExtraFiles].SetName("ExtraFiles");
356   this->Impl->Parts[PartUpload].SetName("Upload");
357   this->Impl->Parts[PartDone].SetName("Done");
358
359   // Fill the part name-to-id map.
360   for (Part p = PartStart; p != PartCount; p = static_cast<Part>(p + 1)) {
361     this->Impl
362       ->PartMap[cmSystemTools::LowerCase(this->Impl->Parts[p].GetName())] = p;
363   }
364
365   for (auto& handler : this->Impl->GetTestingHandlers()) {
366     handler->SetCTestInstance(this);
367   }
368
369   // Make sure we can capture the build tool output.
370   cmSystemTools::EnableVSConsoleOutput();
371 }
372
373 cmCTest::~cmCTest() = default;
374
375 int cmCTest::GetParallelLevel() const
376 {
377   return this->Impl->ParallelLevel;
378 }
379
380 void cmCTest::SetParallelLevel(int level)
381 {
382   this->Impl->ParallelLevel = level < 1 ? 1 : level;
383 }
384
385 unsigned long cmCTest::GetTestLoad() const
386 {
387   return this->Impl->TestLoad;
388 }
389
390 void cmCTest::SetTestLoad(unsigned long load)
391 {
392   this->Impl->TestLoad = load;
393 }
394
395 bool cmCTest::ShouldCompressTestOutput()
396 {
397   return this->Impl->CompressTestOutput;
398 }
399
400 cmCTest::Part cmCTest::GetPartFromName(const std::string& name)
401 {
402   // Look up by lower-case to make names case-insensitive.
403   std::string lower_name = cmSystemTools::LowerCase(name);
404   auto const i = this->Impl->PartMap.find(lower_name);
405   if (i != this->Impl->PartMap.end()) {
406     return i->second;
407   }
408
409   // The string does not name a valid part.
410   return PartCount;
411 }
412
413 int cmCTest::Initialize(const char* binary_dir, cmCTestStartCommand* command)
414 {
415   bool quiet = false;
416   if (command && command->ShouldBeQuiet()) {
417     quiet = true;
418   }
419
420   cmCTestOptionalLog(this, DEBUG, "Here: " << __LINE__ << std::endl, quiet);
421   if (!this->Impl->InteractiveDebugMode) {
422     this->BlockTestErrorDiagnostics();
423   } else {
424     cmSystemTools::PutEnv("CTEST_INTERACTIVE_DEBUG_MODE=1");
425   }
426
427   this->Impl->BinaryDir = binary_dir;
428   cmSystemTools::ConvertToUnixSlashes(this->Impl->BinaryDir);
429
430   this->UpdateCTestConfiguration();
431
432   cmCTestOptionalLog(this, DEBUG, "Here: " << __LINE__ << std::endl, quiet);
433   if (this->Impl->ProduceXML) {
434     cmCTestOptionalLog(this, DEBUG, "Here: " << __LINE__ << std::endl, quiet);
435     cmCTestOptionalLog(this, OUTPUT,
436                        "   Site: "
437                          << this->GetCTestConfiguration("Site") << std::endl
438                          << "   Build name: "
439                          << cmCTest::SafeBuildIdField(
440                               this->GetCTestConfiguration("BuildName"))
441                          << std::endl,
442                        quiet);
443     cmCTestOptionalLog(this, DEBUG, "Produce XML is on" << std::endl, quiet);
444     if (this->Impl->TestModel == cmCTest::NIGHTLY &&
445         this->GetCTestConfiguration("NightlyStartTime").empty()) {
446       cmCTestOptionalLog(
447         this, WARNING,
448         "WARNING: No nightly start time found please set in CTestConfig.cmake"
449         " or DartConfig.cmake"
450           << std::endl,
451         quiet);
452       cmCTestOptionalLog(this, DEBUG, "Here: " << __LINE__ << std::endl,
453                          quiet);
454       return 0;
455     }
456   }
457
458   cmake cm(cmake::RoleScript, cmState::CTest);
459   cm.SetHomeDirectory("");
460   cm.SetHomeOutputDirectory("");
461   cm.GetCurrentSnapshot().SetDefaultDefinitions();
462   cmGlobalGenerator gg(&cm);
463   cmMakefile mf(&gg, cm.GetCurrentSnapshot());
464   if (!this->ReadCustomConfigurationFileTree(this->Impl->BinaryDir, &mf)) {
465     cmCTestOptionalLog(
466       this, DEBUG, "Cannot find custom configuration file tree" << std::endl,
467       quiet);
468     return 0;
469   }
470
471   if (this->Impl->ProduceXML) {
472     // Verify "Testing" directory exists:
473     //
474     std::string testingDir = this->Impl->BinaryDir + "/Testing";
475     if (cmSystemTools::FileExists(testingDir)) {
476       if (!cmSystemTools::FileIsDirectory(testingDir)) {
477         cmCTestLog(this, ERROR_MESSAGE,
478                    "File " << testingDir
479                            << " is in the place of the testing directory"
480                            << std::endl);
481         return 0;
482       }
483     } else {
484       if (!cmSystemTools::MakeDirectory(testingDir)) {
485         cmCTestLog(this, ERROR_MESSAGE,
486                    "Cannot create directory " << testingDir << std::endl);
487         return 0;
488       }
489     }
490
491     // Create new "TAG" file or read existing one:
492     //
493     bool createNewTag = true;
494     if (command) {
495       createNewTag = command->ShouldCreateNewTag();
496     }
497
498     std::string tagfile = testingDir + "/TAG";
499     cmsys::ifstream tfin(tagfile.c_str());
500     std::string tag;
501
502     if (createNewTag) {
503       time_t tctime = time(nullptr);
504       if (this->Impl->TomorrowTag) {
505         tctime += (24 * 60 * 60);
506       }
507       struct tm* lctime = gmtime(&tctime);
508       if (tfin && cmSystemTools::GetLineFromStream(tfin, tag)) {
509         int year = 0;
510         int mon = 0;
511         int day = 0;
512         int hour = 0;
513         int min = 0;
514         sscanf(tag.c_str(), "%04d%02d%02d-%02d%02d", &year, &mon, &day, &hour,
515                &min);
516         if (year != lctime->tm_year + 1900 || mon != lctime->tm_mon + 1 ||
517             day != lctime->tm_mday) {
518           tag.clear();
519         }
520         std::string group;
521         if (cmSystemTools::GetLineFromStream(tfin, group) &&
522             !this->Impl->Parts[PartStart] && !command) {
523           this->Impl->SpecificGroup = group;
524         }
525         std::string model;
526         if (cmSystemTools::GetLineFromStream(tfin, model) &&
527             !this->Impl->Parts[PartStart] && !command) {
528           this->Impl->TestModel = GetTestModelFromString(model);
529         }
530         tfin.close();
531       }
532       if (tag.empty() || (nullptr != command) ||
533           this->Impl->Parts[PartStart]) {
534         cmCTestOptionalLog(
535           this, DEBUG,
536           "TestModel: " << this->GetTestModelString() << std::endl, quiet);
537         cmCTestOptionalLog(this, DEBUG,
538                            "TestModel: " << this->Impl->TestModel << std::endl,
539                            quiet);
540         if (this->Impl->TestModel == cmCTest::NIGHTLY) {
541           lctime = this->GetNightlyTime(
542             this->GetCTestConfiguration("NightlyStartTime"),
543             this->Impl->TomorrowTag);
544         }
545         char datestring[100];
546         snprintf(datestring, sizeof(datestring), "%04d%02d%02d-%02d%02d",
547                  lctime->tm_year + 1900, lctime->tm_mon + 1, lctime->tm_mday,
548                  lctime->tm_hour, lctime->tm_min);
549         tag = datestring;
550         cmsys::ofstream ofs(tagfile.c_str());
551         if (ofs) {
552           ofs << tag << std::endl;
553           ofs << this->GetTestModelString() << std::endl;
554           switch (this->Impl->TestModel) {
555             case cmCTest::EXPERIMENTAL:
556               ofs << "Experimental" << std::endl;
557               break;
558             case cmCTest::NIGHTLY:
559               ofs << "Nightly" << std::endl;
560               break;
561             case cmCTest::CONTINUOUS:
562               ofs << "Continuous" << std::endl;
563               break;
564           }
565         }
566         ofs.close();
567         if (nullptr == command) {
568           cmCTestOptionalLog(this, OUTPUT,
569                              "Create new tag: " << tag << " - "
570                                                 << this->GetTestModelString()
571                                                 << std::endl,
572                              quiet);
573         }
574       }
575     } else {
576       std::string group;
577       std::string modelStr;
578       int model = cmCTest::UNKNOWN;
579
580       if (tfin) {
581         cmSystemTools::GetLineFromStream(tfin, tag);
582         cmSystemTools::GetLineFromStream(tfin, group);
583         if (cmSystemTools::GetLineFromStream(tfin, modelStr)) {
584           model = GetTestModelFromString(modelStr);
585         }
586         tfin.close();
587       }
588
589       if (tag.empty()) {
590         cmCTestLog(this, ERROR_MESSAGE,
591                    "Cannot read existing TAG file in " << testingDir
592                                                        << std::endl);
593         return 0;
594       }
595
596       if (this->Impl->TestModel == cmCTest::UNKNOWN) {
597         if (model == cmCTest::UNKNOWN) {
598           cmCTestLog(this, ERROR_MESSAGE,
599                      "TAG file does not contain model and "
600                      "no model specified in start command"
601                        << std::endl);
602           return 0;
603         }
604
605         this->SetTestModel(model);
606       }
607
608       if (model != this->Impl->TestModel && model != cmCTest::UNKNOWN &&
609           this->Impl->TestModel != cmCTest::UNKNOWN) {
610         cmCTestOptionalLog(this, WARNING,
611                            "Model given in TAG does not match "
612                            "model given in ctest_start()"
613                              << std::endl,
614                            quiet);
615       }
616
617       if (!this->Impl->SpecificGroup.empty() &&
618           group != this->Impl->SpecificGroup) {
619         cmCTestOptionalLog(this, WARNING,
620                            "Group given in TAG does not match "
621                            "group given in ctest_start()"
622                              << std::endl,
623                            quiet);
624       } else {
625         this->Impl->SpecificGroup = group;
626       }
627
628       cmCTestOptionalLog(this, OUTPUT,
629                          "  Use existing tag: " << tag << " - "
630                                                 << this->GetTestModelString()
631                                                 << std::endl,
632                          quiet);
633     }
634
635     this->Impl->CurrentTag = tag;
636   }
637
638   return 1;
639 }
640
641 bool cmCTest::InitializeFromCommand(cmCTestStartCommand* command)
642 {
643   std::string src_dir = this->GetCTestConfiguration("SourceDirectory");
644   std::string bld_dir = this->GetCTestConfiguration("BuildDirectory");
645   this->Impl->BuildID = "";
646   for (Part p = PartStart; p != PartCount; p = static_cast<Part>(p + 1)) {
647     this->Impl->Parts[p].SubmitFiles.clear();
648   }
649
650   cmMakefile* mf = command->GetMakefile();
651   std::string fname;
652
653   std::string src_dir_fname = cmStrCat(src_dir, "/CTestConfig.cmake");
654   cmSystemTools::ConvertToUnixSlashes(src_dir_fname);
655
656   std::string bld_dir_fname = cmStrCat(bld_dir, "/CTestConfig.cmake");
657   cmSystemTools::ConvertToUnixSlashes(bld_dir_fname);
658
659   if (cmSystemTools::FileExists(bld_dir_fname)) {
660     fname = bld_dir_fname;
661   } else if (cmSystemTools::FileExists(src_dir_fname)) {
662     fname = src_dir_fname;
663   }
664
665   if (!fname.empty()) {
666     cmCTestOptionalLog(this, OUTPUT,
667                        "   Reading ctest configuration file: " << fname
668                                                                << std::endl,
669                        command->ShouldBeQuiet());
670     bool readit = mf->ReadDependentFile(fname);
671     if (!readit) {
672       std::string m = cmStrCat("Could not find include file: ", fname);
673       command->SetError(m);
674       return false;
675     }
676   }
677
678   this->SetCTestConfigurationFromCMakeVariable(mf, "NightlyStartTime",
679                                                "CTEST_NIGHTLY_START_TIME",
680                                                command->ShouldBeQuiet());
681   this->SetCTestConfigurationFromCMakeVariable(mf, "Site", "CTEST_SITE",
682                                                command->ShouldBeQuiet());
683   this->SetCTestConfigurationFromCMakeVariable(
684     mf, "BuildName", "CTEST_BUILD_NAME", command->ShouldBeQuiet());
685
686   if (!this->Initialize(bld_dir.c_str(), command)) {
687     return false;
688   }
689   cmCTestOptionalLog(this, OUTPUT,
690                      "   Use " << this->GetTestModelString() << " tag: "
691                                << this->GetCurrentTag() << std::endl,
692                      command->ShouldBeQuiet());
693   return true;
694 }
695
696 bool cmCTest::UpdateCTestConfiguration()
697 {
698   if (this->Impl->SuppressUpdatingCTestConfiguration) {
699     return true;
700   }
701   std::string fileName = this->Impl->BinaryDir + "/CTestConfiguration.ini";
702   if (!cmSystemTools::FileExists(fileName)) {
703     fileName = this->Impl->BinaryDir + "/DartConfiguration.tcl";
704   }
705   cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
706              "UpdateCTestConfiguration  from :" << fileName << "\n");
707   if (!cmSystemTools::FileExists(fileName)) {
708     // No need to exit if we are not producing XML
709     if (this->Impl->ProduceXML) {
710       cmCTestLog(this, WARNING, "Cannot find file: " << fileName << std::endl);
711       return false;
712     }
713   } else {
714     cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
715                "Parse Config file:" << fileName << "\n");
716     // parse the dart test file
717     cmsys::ifstream fin(fileName.c_str());
718
719     if (!fin) {
720       return false;
721     }
722
723     char buffer[1024];
724     while (fin) {
725       buffer[0] = 0;
726       fin.getline(buffer, 1023);
727       buffer[1023] = 0;
728       std::string line = cmCTest::CleanString(buffer);
729       if (line.empty()) {
730         continue;
731       }
732       while (fin && (line.back() == '\\')) {
733         line.resize(line.size() - 1);
734         buffer[0] = 0;
735         fin.getline(buffer, 1023);
736         buffer[1023] = 0;
737         line += cmCTest::CleanString(buffer);
738       }
739       if (line[0] == '#') {
740         continue;
741       }
742       std::string::size_type cpos = line.find_first_of(':');
743       if (cpos == std::string::npos) {
744         continue;
745       }
746       std::string key = line.substr(0, cpos);
747       std::string value = cmCTest::CleanString(line, cpos + 1);
748       this->Impl->CTestConfiguration[key] = value;
749     }
750     fin.close();
751   }
752   if (!this->GetCTestConfiguration("BuildDirectory").empty()) {
753     this->Impl->BinaryDir = this->GetCTestConfiguration("BuildDirectory");
754     cmSystemTools::ChangeDirectory(this->Impl->BinaryDir);
755   }
756   this->Impl->TimeOut =
757     std::chrono::seconds(atoi(this->GetCTestConfiguration("TimeOut").c_str()));
758   std::string const& testLoad = this->GetCTestConfiguration("TestLoad");
759   if (!testLoad.empty()) {
760     unsigned long load;
761     if (cmStrToULong(testLoad, &load)) {
762       this->SetTestLoad(load);
763     } else {
764       cmCTestLog(this, WARNING,
765                  "Invalid value for 'Test Load' : " << testLoad << std::endl);
766     }
767   }
768   if (this->Impl->ProduceXML) {
769     this->Impl->CompressXMLFiles =
770       cmIsOn(this->GetCTestConfiguration("CompressSubmission"));
771   }
772   return true;
773 }
774
775 void cmCTest::BlockTestErrorDiagnostics()
776 {
777   cmSystemTools::PutEnv("DART_TEST_FROM_DART=1");
778   cmSystemTools::PutEnv("DASHBOARD_TEST_FROM_CTEST=" CMake_VERSION);
779 #if defined(_WIN32)
780   SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
781 #elif defined(__BEOS__) || defined(__HAIKU__)
782   disable_debugger(1);
783 #endif
784 }
785
786 void cmCTest::SetTestModel(int mode)
787 {
788   this->Impl->InteractiveDebugMode = false;
789   this->Impl->TestModel = mode;
790 }
791
792 int cmCTest::GetTestModel() const
793 {
794   return this->Impl->TestModel;
795 }
796
797 bool cmCTest::SetTest(const std::string& ttype, bool report)
798 {
799   if (cmSystemTools::LowerCase(ttype) == "all") {
800     for (Part p = PartStart; p != PartCount; p = static_cast<Part>(p + 1)) {
801       this->Impl->Parts[p].Enable();
802     }
803     return true;
804   }
805   Part p = this->GetPartFromName(ttype);
806   if (p != PartCount) {
807     this->Impl->Parts[p].Enable();
808     return true;
809   }
810   if (report) {
811     cmCTestLog(this, ERROR_MESSAGE,
812                "Don't know about test \"" << ttype << "\" yet..."
813                                           << std::endl);
814   }
815   return false;
816 }
817
818 void cmCTest::Finalize()
819 {
820 }
821
822 bool cmCTest::OpenOutputFile(const std::string& path, const std::string& name,
823                              cmGeneratedFileStream& stream, bool compress)
824 {
825   std::string testingDir = this->Impl->BinaryDir + "/Testing";
826   if (!path.empty()) {
827     testingDir += "/" + path;
828   }
829   if (cmSystemTools::FileExists(testingDir)) {
830     if (!cmSystemTools::FileIsDirectory(testingDir)) {
831       cmCTestLog(this, ERROR_MESSAGE,
832                  "File " << testingDir
833                          << " is in the place of the testing directory"
834                          << std::endl);
835       return false;
836     }
837   } else {
838     if (!cmSystemTools::MakeDirectory(testingDir)) {
839       cmCTestLog(this, ERROR_MESSAGE,
840                  "Cannot create directory " << testingDir << std::endl);
841       return false;
842     }
843   }
844   std::string filename = testingDir + "/" + name;
845   stream.SetTempExt("tmp");
846   stream.Open(filename);
847   if (!stream) {
848     cmCTestLog(this, ERROR_MESSAGE,
849                "Problem opening file: " << filename << std::endl);
850     return false;
851   }
852   if (compress) {
853     if (this->Impl->CompressXMLFiles) {
854       stream.SetCompression(true);
855     }
856   }
857   return true;
858 }
859
860 bool cmCTest::AddIfExists(Part part, const std::string& file)
861 {
862   if (this->CTestFileExists(file)) {
863     this->AddSubmitFile(part, file);
864   } else {
865     std::string name = cmStrCat(file, ".gz");
866     if (this->CTestFileExists(name)) {
867       this->AddSubmitFile(part, file);
868     } else {
869       return false;
870     }
871   }
872   return true;
873 }
874
875 bool cmCTest::CTestFileExists(const std::string& filename)
876 {
877   std::string testingDir = this->Impl->BinaryDir + "/Testing/" +
878     this->Impl->CurrentTag + "/" + filename;
879   return cmSystemTools::FileExists(testingDir);
880 }
881
882 cmCTestBuildHandler* cmCTest::GetBuildHandler()
883 {
884   return &this->Impl->BuildHandler;
885 }
886
887 cmCTestBuildAndTestHandler* cmCTest::GetBuildAndTestHandler()
888 {
889   return &this->Impl->BuildAndTestHandler;
890 }
891
892 cmCTestCoverageHandler* cmCTest::GetCoverageHandler()
893 {
894   return &this->Impl->CoverageHandler;
895 }
896
897 cmCTestScriptHandler* cmCTest::GetScriptHandler()
898 {
899   return &this->Impl->ScriptHandler;
900 }
901
902 cmCTestTestHandler* cmCTest::GetTestHandler()
903 {
904   return &this->Impl->TestHandler;
905 }
906
907 cmCTestUpdateHandler* cmCTest::GetUpdateHandler()
908 {
909   return &this->Impl->UpdateHandler;
910 }
911
912 cmCTestConfigureHandler* cmCTest::GetConfigureHandler()
913 {
914   return &this->Impl->ConfigureHandler;
915 }
916
917 cmCTestMemCheckHandler* cmCTest::GetMemCheckHandler()
918 {
919   return &this->Impl->MemCheckHandler;
920 }
921
922 cmCTestSubmitHandler* cmCTest::GetSubmitHandler()
923 {
924   return &this->Impl->SubmitHandler;
925 }
926
927 cmCTestUploadHandler* cmCTest::GetUploadHandler()
928 {
929   return &this->Impl->UploadHandler;
930 }
931
932 int cmCTest::ProcessSteps()
933 {
934   int res = 0;
935   bool notest = true;
936   int update_count = 0;
937
938   for (Part p = PartStart; notest && p != PartCount;
939        p = static_cast<Part>(p + 1)) {
940     notest = !this->Impl->Parts[p];
941   }
942   if (this->Impl->Parts[PartUpdate] &&
943       (this->GetRemainingTimeAllowed() > std::chrono::minutes(2))) {
944     cmCTestUpdateHandler* uphandler = this->GetUpdateHandler();
945     uphandler->SetPersistentOption(
946       "SourceDirectory",
947       this->GetCTestConfiguration("SourceDirectory").c_str());
948     update_count = uphandler->ProcessHandler();
949     if (update_count < 0) {
950       res |= cmCTest::UPDATE_ERRORS;
951     }
952   }
953   if (this->Impl->TestModel == cmCTest::CONTINUOUS && !update_count) {
954     return 0;
955   }
956   if (this->Impl->Parts[PartConfigure] &&
957       (this->GetRemainingTimeAllowed() > std::chrono::minutes(2))) {
958     if (this->GetConfigureHandler()->ProcessHandler() < 0) {
959       res |= cmCTest::CONFIGURE_ERRORS;
960     }
961   }
962   if (this->Impl->Parts[PartBuild] &&
963       (this->GetRemainingTimeAllowed() > std::chrono::minutes(2))) {
964     this->UpdateCTestConfiguration();
965     if (this->GetBuildHandler()->ProcessHandler() < 0) {
966       res |= cmCTest::BUILD_ERRORS;
967     }
968   }
969   if ((this->Impl->Parts[PartTest] || notest) &&
970       (this->GetRemainingTimeAllowed() > std::chrono::minutes(2))) {
971     this->UpdateCTestConfiguration();
972     if (this->GetTestHandler()->ProcessHandler() < 0) {
973       res |= cmCTest::TEST_ERRORS;
974     }
975   }
976   if (this->Impl->Parts[PartCoverage] &&
977       (this->GetRemainingTimeAllowed() > std::chrono::minutes(2))) {
978     this->UpdateCTestConfiguration();
979     if (this->GetCoverageHandler()->ProcessHandler() < 0) {
980       res |= cmCTest::COVERAGE_ERRORS;
981     }
982   }
983   if (this->Impl->Parts[PartMemCheck] &&
984       (this->GetRemainingTimeAllowed() > std::chrono::minutes(2))) {
985     this->UpdateCTestConfiguration();
986     if (this->GetMemCheckHandler()->ProcessHandler() < 0) {
987       res |= cmCTest::MEMORY_ERRORS;
988     }
989   }
990   if (!notest) {
991     std::string notes_dir = this->Impl->BinaryDir + "/Testing/Notes";
992     if (cmSystemTools::FileIsDirectory(notes_dir)) {
993       cmsys::Directory d;
994       d.Load(notes_dir);
995       unsigned long kk;
996       for (kk = 0; kk < d.GetNumberOfFiles(); kk++) {
997         const char* file = d.GetFile(kk);
998         std::string fullname = notes_dir + "/" + file;
999         if (cmSystemTools::FileExists(fullname) &&
1000             !cmSystemTools::FileIsDirectory(fullname)) {
1001           if (!this->Impl->NotesFiles.empty()) {
1002             this->Impl->NotesFiles += ";";
1003           }
1004           this->Impl->NotesFiles += fullname;
1005           this->Impl->Parts[PartNotes].Enable();
1006         }
1007       }
1008     }
1009   }
1010   if (this->Impl->Parts[PartNotes]) {
1011     this->UpdateCTestConfiguration();
1012     if (!this->Impl->NotesFiles.empty()) {
1013       this->GenerateNotesFile(this->Impl->NotesFiles);
1014     }
1015   }
1016   if (this->Impl->Parts[PartSubmit]) {
1017     this->UpdateCTestConfiguration();
1018     if (this->GetSubmitHandler()->ProcessHandler() < 0) {
1019       res |= cmCTest::SUBMIT_ERRORS;
1020     }
1021   }
1022   if (res != 0) {
1023     cmCTestLog(this, ERROR_MESSAGE, "Errors while running CTest" << std::endl);
1024     if (!this->Impl->OutputTestOutputOnTestFailure) {
1025       const std::string lastTestLog =
1026         this->GetBinaryDir() + "/Testing/Temporary/LastTest.log";
1027       cmCTestLog(this, ERROR_MESSAGE,
1028                  "Output from these tests are in: " << lastTestLog
1029                                                     << std::endl);
1030       cmCTestLog(this, ERROR_MESSAGE,
1031                  "Use \"--rerun-failed --output-on-failure\" to re-run the "
1032                  "failed cases verbosely."
1033                    << std::endl);
1034     }
1035   }
1036   return res;
1037 }
1038
1039 std::string cmCTest::GetTestModelString()
1040 {
1041   if (!this->Impl->SpecificGroup.empty()) {
1042     return this->Impl->SpecificGroup;
1043   }
1044   switch (this->Impl->TestModel) {
1045     case cmCTest::NIGHTLY:
1046       return "Nightly";
1047     case cmCTest::CONTINUOUS:
1048       return "Continuous";
1049   }
1050   return "Experimental";
1051 }
1052
1053 int cmCTest::GetTestModelFromString(const std::string& str)
1054 {
1055   if (str.empty()) {
1056     return cmCTest::EXPERIMENTAL;
1057   }
1058   std::string rstr = cmSystemTools::LowerCase(str);
1059   if (cmHasLiteralPrefix(rstr, "cont")) {
1060     return cmCTest::CONTINUOUS;
1061   }
1062   if (cmHasLiteralPrefix(rstr, "nigh")) {
1063     return cmCTest::NIGHTLY;
1064   }
1065   return cmCTest::EXPERIMENTAL;
1066 }
1067
1068 //######################################################################
1069 //######################################################################
1070 //######################################################################
1071 //######################################################################
1072
1073 int cmCTest::RunMakeCommand(const std::string& command, std::string& output,
1074                             int* retVal, const char* dir, cmDuration timeout,
1075                             std::ostream& ofs, Encoding encoding)
1076 {
1077   // First generate the command and arguments
1078   std::vector<std::string> args = cmSystemTools::ParseArguments(command);
1079
1080   if (args.empty()) {
1081     return false;
1082   }
1083
1084   std::vector<const char*> argv;
1085   argv.reserve(args.size() + 1);
1086   for (std::string const& a : args) {
1087     argv.push_back(a.c_str());
1088   }
1089   argv.push_back(nullptr);
1090
1091   output.clear();
1092   cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, "Run command:");
1093   for (char const* arg : argv) {
1094     if (!arg) {
1095       break;
1096     }
1097     cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, " \"" << arg << "\"");
1098   }
1099   cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, std::endl);
1100
1101   // Now create process object
1102   cmsysProcess* cp = cmsysProcess_New();
1103   cmsysProcess_SetCommand(cp, argv.data());
1104   cmsysProcess_SetWorkingDirectory(cp, dir);
1105   cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
1106   cmsysProcess_SetTimeout(cp, timeout.count());
1107   cmsysProcess_Execute(cp);
1108
1109   // Initialize tick's
1110   std::string::size_type tick = 0;
1111   std::string::size_type tick_len = 1024;
1112   std::string::size_type tick_line_len = 50;
1113
1114   char* data;
1115   int length;
1116   cmProcessOutput processOutput(encoding);
1117   std::string strdata;
1118   cmCTestLog(this, HANDLER_PROGRESS_OUTPUT,
1119              "   Each . represents " << tick_len
1120                                      << " bytes of output\n"
1121                                         "    "
1122                                      << std::flush);
1123   while (cmsysProcess_WaitForData(cp, &data, &length, nullptr)) {
1124     processOutput.DecodeText(data, length, strdata);
1125     for (char& cc : strdata) {
1126       if (cc == 0) {
1127         cc = '\n';
1128       }
1129     }
1130     output.append(strdata);
1131     while (output.size() > (tick * tick_len)) {
1132       tick++;
1133       cmCTestLog(this, HANDLER_PROGRESS_OUTPUT, "." << std::flush);
1134       if (tick % tick_line_len == 0 && tick > 0) {
1135         cmCTestLog(this, HANDLER_PROGRESS_OUTPUT,
1136                    "  Size: " << int((double(output.size()) / 1024.0) + 1)
1137                               << "K\n    " << std::flush);
1138       }
1139     }
1140     cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
1141                cmCTestLogWrite(strdata.c_str(), strdata.size()));
1142     if (ofs) {
1143       ofs << cmCTestLogWrite(strdata.c_str(), strdata.size());
1144     }
1145   }
1146   processOutput.DecodeText(std::string(), strdata);
1147   if (!strdata.empty()) {
1148     output.append(strdata);
1149     cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
1150                cmCTestLogWrite(strdata.c_str(), strdata.size()));
1151     if (ofs) {
1152       ofs << cmCTestLogWrite(strdata.c_str(), strdata.size());
1153     }
1154   }
1155   cmCTestLog(this, HANDLER_PROGRESS_OUTPUT,
1156              " Size of output: " << int(double(output.size()) / 1024.0) << "K"
1157                                  << std::endl);
1158
1159   cmsysProcess_WaitForExit(cp, nullptr);
1160
1161   int result = cmsysProcess_GetState(cp);
1162
1163   if (result == cmsysProcess_State_Exited) {
1164     *retVal = cmsysProcess_GetExitValue(cp);
1165     cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
1166                "Command exited with the value: " << *retVal << std::endl);
1167   } else if (result == cmsysProcess_State_Exception) {
1168     *retVal = cmsysProcess_GetExitException(cp);
1169     cmCTestLog(this, WARNING,
1170                "There was an exception: " << *retVal << std::endl);
1171   } else if (result == cmsysProcess_State_Expired) {
1172     cmCTestLog(this, WARNING, "There was a timeout" << std::endl);
1173   } else if (result == cmsysProcess_State_Error) {
1174     output += "\n*** ERROR executing: ";
1175     output += cmsysProcess_GetErrorString(cp);
1176     output += "\n***The build process failed.";
1177     cmCTestLog(this, ERROR_MESSAGE,
1178                "There was an error: " << cmsysProcess_GetErrorString(cp)
1179                                       << std::endl);
1180   }
1181
1182   cmsysProcess_Delete(cp);
1183
1184   return result;
1185 }
1186
1187 //######################################################################
1188 //######################################################################
1189 //######################################################################
1190 //######################################################################
1191
1192 int cmCTest::RunTest(std::vector<const char*> argv, std::string* output,
1193                      int* retVal, std::ostream* log, cmDuration testTimeOut,
1194                      std::vector<std::string>* environment, Encoding encoding)
1195 {
1196   bool modifyEnv = (environment && !environment->empty());
1197
1198   // determine how much time we have
1199   cmDuration timeout = this->GetRemainingTimeAllowed();
1200   if (timeout != cmCTest::MaxDuration()) {
1201     timeout -= std::chrono::minutes(2);
1202   }
1203   if (this->Impl->TimeOut > cmDuration::zero() &&
1204       this->Impl->TimeOut < timeout) {
1205     timeout = this->Impl->TimeOut;
1206   }
1207   if (testTimeOut > cmDuration::zero() &&
1208       testTimeOut < this->GetRemainingTimeAllowed()) {
1209     timeout = testTimeOut;
1210   }
1211
1212   // always have at least 1 second if we got to here
1213   if (timeout <= cmDuration::zero()) {
1214     timeout = std::chrono::seconds(1);
1215   }
1216   cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
1217              "Test timeout computed to be: "
1218                << (timeout == cmCTest::MaxDuration()
1219                      ? std::string("infinite")
1220                      : std::to_string(cmDurationTo<unsigned int>(timeout)))
1221                << "\n");
1222   if (cmSystemTools::SameFile(argv[0], cmSystemTools::GetCTestCommand()) &&
1223       !this->Impl->ForceNewCTestProcess) {
1224     cmCTest inst;
1225     inst.Impl->ConfigType = this->Impl->ConfigType;
1226     inst.Impl->TimeOut = timeout;
1227
1228     // Capture output of the child ctest.
1229     std::ostringstream oss;
1230     inst.SetStreams(&oss, &oss);
1231
1232     std::vector<std::string> args;
1233     for (char const* i : argv) {
1234       if (i) {
1235         // make sure we pass the timeout in for any build and test
1236         // invocations. Since --build-generator is required this is a
1237         // good place to check for it, and to add the arguments in
1238         if (strcmp(i, "--build-generator") == 0 &&
1239             timeout != cmCTest::MaxDuration() &&
1240             timeout > cmDuration::zero()) {
1241           args.emplace_back("--test-timeout");
1242           args.push_back(std::to_string(cmDurationTo<unsigned int>(timeout)));
1243         }
1244         args.emplace_back(i);
1245       }
1246     }
1247     if (log) {
1248       *log << "* Run internal CTest" << std::endl;
1249     }
1250
1251     std::unique_ptr<cmSystemTools::SaveRestoreEnvironment> saveEnv;
1252     if (modifyEnv) {
1253       saveEnv = cm::make_unique<cmSystemTools::SaveRestoreEnvironment>();
1254       cmSystemTools::AppendEnv(*environment);
1255     }
1256
1257     *retVal = inst.Run(args, output);
1258     if (output) {
1259       *output += oss.str();
1260     }
1261     if (log && output) {
1262       *log << *output;
1263     }
1264     if (output) {
1265       cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
1266                  "Internal cmCTest object used to run test." << std::endl
1267                                                              << *output
1268                                                              << std::endl);
1269     }
1270
1271     return cmsysProcess_State_Exited;
1272   }
1273   std::vector<char> tempOutput;
1274   if (output) {
1275     output->clear();
1276   }
1277
1278   std::unique_ptr<cmSystemTools::SaveRestoreEnvironment> saveEnv;
1279   if (modifyEnv) {
1280     saveEnv = cm::make_unique<cmSystemTools::SaveRestoreEnvironment>();
1281     cmSystemTools::AppendEnv(*environment);
1282   }
1283
1284   cmsysProcess* cp = cmsysProcess_New();
1285   cmsysProcess_SetCommand(cp, argv.data());
1286   cmCTestLog(this, DEBUG, "Command is: " << argv[0] << std::endl);
1287   if (cmSystemTools::GetRunCommandHideConsole()) {
1288     cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
1289   }
1290
1291   cmsysProcess_SetTimeout(cp, timeout.count());
1292   cmsysProcess_Execute(cp);
1293
1294   char* data;
1295   int length;
1296   cmProcessOutput processOutput(encoding);
1297   std::string strdata;
1298   while (cmsysProcess_WaitForData(cp, &data, &length, nullptr)) {
1299     processOutput.DecodeText(data, length, strdata);
1300     if (output) {
1301       cm::append(tempOutput, data, data + length);
1302     }
1303     cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
1304                cmCTestLogWrite(strdata.c_str(), strdata.size()));
1305     if (log) {
1306       log->write(strdata.c_str(), strdata.size());
1307     }
1308   }
1309   processOutput.DecodeText(std::string(), strdata);
1310   if (!strdata.empty()) {
1311     cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
1312                cmCTestLogWrite(strdata.c_str(), strdata.size()));
1313     if (log) {
1314       log->write(strdata.c_str(), strdata.size());
1315     }
1316   }
1317
1318   cmsysProcess_WaitForExit(cp, nullptr);
1319   processOutput.DecodeText(tempOutput, tempOutput);
1320   if (output && tempOutput.begin() != tempOutput.end()) {
1321     output->append(tempOutput.data(), tempOutput.size());
1322   }
1323   cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
1324              "-- Process completed" << std::endl);
1325
1326   int result = cmsysProcess_GetState(cp);
1327
1328   if (result == cmsysProcess_State_Exited) {
1329     *retVal = cmsysProcess_GetExitValue(cp);
1330     if (*retVal != 0 && this->Impl->OutputTestOutputOnTestFailure) {
1331       this->OutputTestErrors(tempOutput);
1332     }
1333   } else if (result == cmsysProcess_State_Exception) {
1334     if (this->Impl->OutputTestOutputOnTestFailure) {
1335       this->OutputTestErrors(tempOutput);
1336     }
1337     *retVal = cmsysProcess_GetExitException(cp);
1338     std::string outerr = cmStrCat("\n*** Exception executing: ",
1339                                   cmsysProcess_GetExceptionString(cp));
1340     if (output) {
1341       *output += outerr;
1342     }
1343     cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl);
1344   } else if (result == cmsysProcess_State_Error) {
1345     std::string outerr =
1346       cmStrCat("\n*** ERROR executing: ", cmsysProcess_GetErrorString(cp));
1347     if (output) {
1348       *output += outerr;
1349     }
1350     cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl);
1351   }
1352   cmsysProcess_Delete(cp);
1353
1354   return result;
1355 }
1356
1357 std::string cmCTest::SafeBuildIdField(const std::string& value)
1358 {
1359   std::string safevalue(value);
1360
1361   if (!safevalue.empty()) {
1362     // Disallow non-filename and non-space whitespace characters.
1363     // If they occur, replace them with ""
1364     //
1365     const char* disallowed = "\\:*?\"<>|\n\r\t\f\v";
1366
1367     if (safevalue.find_first_of(disallowed) != std::string::npos) {
1368       std::string::size_type i = 0;
1369       std::string::size_type n = strlen(disallowed);
1370       char replace[2];
1371       replace[1] = 0;
1372
1373       for (i = 0; i < n; ++i) {
1374         replace[0] = disallowed[i];
1375         cmSystemTools::ReplaceString(safevalue, replace, "");
1376       }
1377     }
1378   }
1379
1380   if (safevalue.empty()) {
1381     safevalue = "(empty)";
1382   }
1383
1384   return safevalue;
1385 }
1386
1387 void cmCTest::StartXML(cmXMLWriter& xml, bool append)
1388 {
1389   if (this->Impl->CurrentTag.empty()) {
1390     cmCTestLog(this, ERROR_MESSAGE,
1391                "Current Tag empty, this may mean"
1392                " NightlStartTime was not set correctly."
1393                  << std::endl);
1394     cmSystemTools::SetFatalErrorOccurred();
1395   }
1396
1397   // find out about the system
1398   cmsys::SystemInformation info;
1399   info.RunCPUCheck();
1400   info.RunOSCheck();
1401   info.RunMemoryCheck();
1402
1403   std::string buildname =
1404     cmCTest::SafeBuildIdField(this->GetCTestConfiguration("BuildName"));
1405   std::string stamp = cmCTest::SafeBuildIdField(this->Impl->CurrentTag + "-" +
1406                                                 this->GetTestModelString());
1407   std::string site =
1408     cmCTest::SafeBuildIdField(this->GetCTestConfiguration("Site"));
1409
1410   xml.StartDocument();
1411   xml.StartElement("Site");
1412   xml.Attribute("BuildName", buildname);
1413   xml.BreakAttributes();
1414   xml.Attribute("BuildStamp", stamp);
1415   xml.Attribute("Name", site);
1416   xml.Attribute("Generator",
1417                 std::string("ctest-") + cmVersion::GetCMakeVersion());
1418   if (append) {
1419     xml.Attribute("Append", "true");
1420   }
1421   xml.Attribute("CompilerName", this->GetCTestConfiguration("Compiler"));
1422   xml.Attribute("CompilerVersion",
1423                 this->GetCTestConfiguration("CompilerVersion"));
1424   xml.Attribute("OSName", info.GetOSName());
1425   xml.Attribute("Hostname", info.GetHostname());
1426   xml.Attribute("OSRelease", info.GetOSRelease());
1427   xml.Attribute("OSVersion", info.GetOSVersion());
1428   xml.Attribute("OSPlatform", info.GetOSPlatform());
1429   xml.Attribute("Is64Bits", info.Is64Bits());
1430   xml.Attribute("VendorString", info.GetVendorString());
1431   xml.Attribute("VendorID", info.GetVendorID());
1432   xml.Attribute("FamilyID", info.GetFamilyID());
1433   xml.Attribute("ModelID", info.GetModelID());
1434   xml.Attribute("ProcessorCacheSize", info.GetProcessorCacheSize());
1435   xml.Attribute("NumberOfLogicalCPU", info.GetNumberOfLogicalCPU());
1436   xml.Attribute("NumberOfPhysicalCPU", info.GetNumberOfPhysicalCPU());
1437   xml.Attribute("TotalVirtualMemory", info.GetTotalVirtualMemory());
1438   xml.Attribute("TotalPhysicalMemory", info.GetTotalPhysicalMemory());
1439   xml.Attribute("LogicalProcessorsPerPhysical",
1440                 info.GetLogicalProcessorsPerPhysical());
1441   xml.Attribute("ProcessorClockFrequency", info.GetProcessorClockFrequency());
1442
1443   std::string changeId = this->GetCTestConfiguration("ChangeId");
1444   if (!changeId.empty()) {
1445     xml.Attribute("ChangeId", changeId);
1446   }
1447
1448   this->AddSiteProperties(xml);
1449 }
1450
1451 void cmCTest::AddSiteProperties(cmXMLWriter& xml)
1452 {
1453   cmCTestScriptHandler* ch = this->GetScriptHandler();
1454   cmake* cm = ch->GetCMake();
1455   // if no CMake then this is the old style script and props like
1456   // this will not work anyway.
1457   if (!cm) {
1458     return;
1459   }
1460   // This code should go when cdash is changed to use labels only
1461   cmValue subproject = cm->GetState()->GetGlobalProperty("SubProject");
1462   if (subproject) {
1463     xml.StartElement("Subproject");
1464     xml.Attribute("name", *subproject);
1465     cmValue labels =
1466       ch->GetCMake()->GetState()->GetGlobalProperty("SubProjectLabels");
1467     if (labels) {
1468       xml.StartElement("Labels");
1469       std::vector<std::string> args = cmExpandedList(*labels);
1470       for (std::string const& i : args) {
1471         xml.Element("Label", i);
1472       }
1473       xml.EndElement();
1474     }
1475     xml.EndElement();
1476   }
1477
1478   // This code should stay when cdash only does label based sub-projects
1479   cmValue label = cm->GetState()->GetGlobalProperty("Label");
1480   if (label) {
1481     xml.StartElement("Labels");
1482     xml.Element("Label", *label);
1483     xml.EndElement();
1484   }
1485 }
1486
1487 void cmCTest::GenerateSubprojectsOutput(cmXMLWriter& xml)
1488 {
1489   for (std::string const& subproj : this->GetLabelsForSubprojects()) {
1490     xml.StartElement("Subproject");
1491     xml.Attribute("name", subproj);
1492     xml.Element("Label", subproj);
1493     xml.EndElement(); // Subproject
1494   }
1495 }
1496
1497 std::vector<std::string> cmCTest::GetLabelsForSubprojects()
1498 {
1499   std::string labelsForSubprojects =
1500     this->GetCTestConfiguration("LabelsForSubprojects");
1501   std::vector<std::string> subprojects = cmExpandedList(labelsForSubprojects);
1502
1503   // sort the array
1504   std::sort(subprojects.begin(), subprojects.end());
1505   // remove duplicates
1506   auto new_end = std::unique(subprojects.begin(), subprojects.end());
1507   subprojects.erase(new_end, subprojects.end());
1508
1509   return subprojects;
1510 }
1511
1512 void cmCTest::EndXML(cmXMLWriter& xml)
1513 {
1514   xml.EndElement(); // Site
1515   xml.EndDocument();
1516 }
1517
1518 int cmCTest::GenerateCTestNotesOutput(cmXMLWriter& xml,
1519                                       std::vector<std::string> const& files)
1520 {
1521   std::string buildname =
1522     cmCTest::SafeBuildIdField(this->GetCTestConfiguration("BuildName"));
1523   xml.StartDocument();
1524   xml.ProcessingInstruction("xml-stylesheet",
1525                             "type=\"text/xsl\" "
1526                             "href=\"Dart/Source/Server/XSL/Build.xsl "
1527                             "<file:///Dart/Source/Server/XSL/Build.xsl> \"");
1528   xml.StartElement("Site");
1529   xml.Attribute("BuildName", buildname);
1530   xml.Attribute("BuildStamp",
1531                 this->Impl->CurrentTag + "-" + this->GetTestModelString());
1532   xml.Attribute("Name", this->GetCTestConfiguration("Site"));
1533   xml.Attribute("Generator",
1534                 std::string("ctest-") + cmVersion::GetCMakeVersion());
1535   this->AddSiteProperties(xml);
1536   xml.StartElement("Notes");
1537
1538   for (std::string const& file : files) {
1539     cmCTestLog(this, OUTPUT, "\tAdd file: " << file << std::endl);
1540     std::string note_time = this->CurrentTime();
1541     xml.StartElement("Note");
1542     xml.Attribute("Name", file);
1543     xml.Element("Time", std::chrono::system_clock::now());
1544     xml.Element("DateTime", note_time);
1545     xml.StartElement("Text");
1546     cmsys::ifstream ifs(file.c_str());
1547     if (ifs) {
1548       std::string line;
1549       while (cmSystemTools::GetLineFromStream(ifs, line)) {
1550         xml.Content(line);
1551         xml.Content("\n");
1552       }
1553       ifs.close();
1554     } else {
1555       xml.Content("Problem reading file: " + file + "\n");
1556       cmCTestLog(this, ERROR_MESSAGE,
1557                  "Problem reading file: " << file << " while creating notes"
1558                                           << std::endl);
1559     }
1560     xml.EndElement(); // Text
1561     xml.EndElement(); // Note
1562   }
1563   xml.EndElement(); // Notes
1564   xml.EndElement(); // Site
1565   xml.EndDocument();
1566   return 1;
1567 }
1568
1569 int cmCTest::GenerateNotesFile(std::vector<std::string> const& files)
1570 {
1571   cmGeneratedFileStream ofs;
1572   if (!this->OpenOutputFile(this->Impl->CurrentTag, "Notes.xml", ofs)) {
1573     cmCTestLog(this, ERROR_MESSAGE, "Cannot open notes file" << std::endl);
1574     return 1;
1575   }
1576   cmXMLWriter xml(ofs);
1577   this->GenerateCTestNotesOutput(xml, files);
1578   return 0;
1579 }
1580
1581 int cmCTest::GenerateNotesFile(const std::string& cfiles)
1582 {
1583   if (cfiles.empty()) {
1584     return 1;
1585   }
1586
1587   cmCTestLog(this, OUTPUT, "Create notes file" << std::endl);
1588
1589   std::vector<std::string> const files =
1590     cmSystemTools::SplitString(cfiles, ';');
1591   if (files.empty()) {
1592     return 1;
1593   }
1594
1595   return this->GenerateNotesFile(files);
1596 }
1597
1598 int cmCTest::GenerateDoneFile()
1599 {
1600   cmGeneratedFileStream ofs;
1601   if (!this->OpenOutputFile(this->Impl->CurrentTag, "Done.xml", ofs)) {
1602     cmCTestLog(this, ERROR_MESSAGE, "Cannot open done file" << std::endl);
1603     return 1;
1604   }
1605   cmXMLWriter xml(ofs);
1606   xml.StartDocument();
1607   xml.StartElement("Done");
1608   xml.Element("buildId", this->Impl->BuildID);
1609   xml.Element("time", std::chrono::system_clock::now());
1610   xml.EndElement(); // Done
1611   xml.EndDocument();
1612
1613   return 0;
1614 }
1615
1616 bool cmCTest::TryToChangeDirectory(std::string const& dir)
1617 {
1618   cmCTestLog(this, OUTPUT,
1619              "Internal ctest changing into directory: " << dir << std::endl);
1620   cmsys::Status status = cmSystemTools::ChangeDirectory(dir);
1621   if (!status) {
1622     auto msg = "Failed to change working directory to \"" + dir +
1623       "\" : " + status.GetString() + "\n";
1624     cmCTestLog(this, ERROR_MESSAGE, msg);
1625     return false;
1626   }
1627   return true;
1628 }
1629
1630 std::string cmCTest::Base64GzipEncodeFile(std::string const& file)
1631 {
1632   const std::string currDir = cmSystemTools::GetCurrentWorkingDirectory();
1633   std::string parentDir = cmSystemTools::GetParentDirectory(file);
1634
1635   // Temporarily change to the file's directory so the tar gets created
1636   // with a flat directory structure.
1637   if (currDir != parentDir) {
1638     if (!this->TryToChangeDirectory(parentDir)) {
1639       return "";
1640     }
1641   }
1642
1643   std::string tarFile = file + "_temp.tar.gz";
1644   std::vector<std::string> files;
1645   files.push_back(file);
1646
1647   if (!cmSystemTools::CreateTar(tarFile, files, cmSystemTools::TarCompressGZip,
1648                                 false)) {
1649     cmCTestLog(this, ERROR_MESSAGE,
1650                "Error creating tar while "
1651                "encoding file: "
1652                  << file << std::endl);
1653     return "";
1654   }
1655   std::string base64 = this->Base64EncodeFile(tarFile);
1656   cmSystemTools::RemoveFile(tarFile);
1657
1658   // Change back to the directory we started in.
1659   if (currDir != parentDir) {
1660     cmSystemTools::ChangeDirectory(currDir);
1661   }
1662
1663   return base64;
1664 }
1665
1666 std::string cmCTest::Base64EncodeFile(std::string const& file)
1667 {
1668   size_t const len = cmSystemTools::FileLength(file);
1669   cmsys::ifstream ifs(file.c_str(),
1670                       std::ios::in
1671 #ifdef _WIN32
1672                         | std::ios::binary
1673 #endif
1674   );
1675   std::vector<char> file_buffer(len + 1);
1676   ifs.read(file_buffer.data(), len);
1677   ifs.close();
1678
1679   std::vector<char> encoded_buffer((len * 3) / 2 + 5);
1680
1681   size_t const rlen = cmsysBase64_Encode(
1682     reinterpret_cast<unsigned char*>(file_buffer.data()), len,
1683     reinterpret_cast<unsigned char*>(encoded_buffer.data()), 1);
1684
1685   return std::string(encoded_buffer.data(), rlen);
1686 }
1687
1688 bool cmCTest::SubmitExtraFiles(std::vector<std::string> const& files)
1689 {
1690   for (std::string const& file : files) {
1691     if (!cmSystemTools::FileExists(file)) {
1692       cmCTestLog(this, ERROR_MESSAGE,
1693                  "Cannot find extra file: " << file << " to submit."
1694                                             << std::endl;);
1695       return false;
1696     }
1697     this->AddSubmitFile(PartExtraFiles, file);
1698   }
1699   return true;
1700 }
1701
1702 bool cmCTest::SubmitExtraFiles(const std::string& cfiles)
1703 {
1704   if (cfiles.empty()) {
1705     return true;
1706   }
1707
1708   cmCTestLog(this, OUTPUT, "Submit extra files" << std::endl);
1709
1710   std::vector<std::string> const files =
1711     cmSystemTools::SplitString(cfiles, ';');
1712   if (files.empty()) {
1713     return true;
1714   }
1715
1716   return this->SubmitExtraFiles(files);
1717 }
1718
1719 // for a -D argument convert the next argument into
1720 // the proper list of dashboard steps via SetTest
1721 bool cmCTest::AddTestsForDashboardType(std::string& targ)
1722 {
1723   if (targ == "Experimental") {
1724     this->SetTestModel(cmCTest::EXPERIMENTAL);
1725     this->SetTest("Start");
1726     this->SetTest("Configure");
1727     this->SetTest("Build");
1728     this->SetTest("Test");
1729     this->SetTest("Coverage");
1730     this->SetTest("Submit");
1731   } else if (targ == "ExperimentalStart") {
1732     this->SetTestModel(cmCTest::EXPERIMENTAL);
1733     this->SetTest("Start");
1734   } else if (targ == "ExperimentalUpdate") {
1735     this->SetTestModel(cmCTest::EXPERIMENTAL);
1736     this->SetTest("Update");
1737   } else if (targ == "ExperimentalConfigure") {
1738     this->SetTestModel(cmCTest::EXPERIMENTAL);
1739     this->SetTest("Configure");
1740   } else if (targ == "ExperimentalBuild") {
1741     this->SetTestModel(cmCTest::EXPERIMENTAL);
1742     this->SetTest("Build");
1743   } else if (targ == "ExperimentalTest") {
1744     this->SetTestModel(cmCTest::EXPERIMENTAL);
1745     this->SetTest("Test");
1746   } else if (targ == "ExperimentalMemCheck" || targ == "ExperimentalPurify") {
1747     this->SetTestModel(cmCTest::EXPERIMENTAL);
1748     this->SetTest("MemCheck");
1749   } else if (targ == "ExperimentalCoverage") {
1750     this->SetTestModel(cmCTest::EXPERIMENTAL);
1751     this->SetTest("Coverage");
1752   } else if (targ == "ExperimentalSubmit") {
1753     this->SetTestModel(cmCTest::EXPERIMENTAL);
1754     this->SetTest("Submit");
1755   } else if (targ == "Continuous") {
1756     this->SetTestModel(cmCTest::CONTINUOUS);
1757     this->SetTest("Start");
1758     this->SetTest("Update");
1759     this->SetTest("Configure");
1760     this->SetTest("Build");
1761     this->SetTest("Test");
1762     this->SetTest("Coverage");
1763     this->SetTest("Submit");
1764   } else if (targ == "ContinuousStart") {
1765     this->SetTestModel(cmCTest::CONTINUOUS);
1766     this->SetTest("Start");
1767   } else if (targ == "ContinuousUpdate") {
1768     this->SetTestModel(cmCTest::CONTINUOUS);
1769     this->SetTest("Update");
1770   } else if (targ == "ContinuousConfigure") {
1771     this->SetTestModel(cmCTest::CONTINUOUS);
1772     this->SetTest("Configure");
1773   } else if (targ == "ContinuousBuild") {
1774     this->SetTestModel(cmCTest::CONTINUOUS);
1775     this->SetTest("Build");
1776   } else if (targ == "ContinuousTest") {
1777     this->SetTestModel(cmCTest::CONTINUOUS);
1778     this->SetTest("Test");
1779   } else if (targ == "ContinuousMemCheck" || targ == "ContinuousPurify") {
1780     this->SetTestModel(cmCTest::CONTINUOUS);
1781     this->SetTest("MemCheck");
1782   } else if (targ == "ContinuousCoverage") {
1783     this->SetTestModel(cmCTest::CONTINUOUS);
1784     this->SetTest("Coverage");
1785   } else if (targ == "ContinuousSubmit") {
1786     this->SetTestModel(cmCTest::CONTINUOUS);
1787     this->SetTest("Submit");
1788   } else if (targ == "Nightly") {
1789     this->SetTestModel(cmCTest::NIGHTLY);
1790     this->SetTest("Start");
1791     this->SetTest("Update");
1792     this->SetTest("Configure");
1793     this->SetTest("Build");
1794     this->SetTest("Test");
1795     this->SetTest("Coverage");
1796     this->SetTest("Submit");
1797   } else if (targ == "NightlyStart") {
1798     this->SetTestModel(cmCTest::NIGHTLY);
1799     this->SetTest("Start");
1800   } else if (targ == "NightlyUpdate") {
1801     this->SetTestModel(cmCTest::NIGHTLY);
1802     this->SetTest("Update");
1803   } else if (targ == "NightlyConfigure") {
1804     this->SetTestModel(cmCTest::NIGHTLY);
1805     this->SetTest("Configure");
1806   } else if (targ == "NightlyBuild") {
1807     this->SetTestModel(cmCTest::NIGHTLY);
1808     this->SetTest("Build");
1809   } else if (targ == "NightlyTest") {
1810     this->SetTestModel(cmCTest::NIGHTLY);
1811     this->SetTest("Test");
1812   } else if (targ == "NightlyMemCheck" || targ == "NightlyPurify") {
1813     this->SetTestModel(cmCTest::NIGHTLY);
1814     this->SetTest("MemCheck");
1815   } else if (targ == "NightlyCoverage") {
1816     this->SetTestModel(cmCTest::NIGHTLY);
1817     this->SetTest("Coverage");
1818   } else if (targ == "NightlySubmit") {
1819     this->SetTestModel(cmCTest::NIGHTLY);
1820     this->SetTest("Submit");
1821   } else if (targ == "MemoryCheck") {
1822     this->SetTestModel(cmCTest::EXPERIMENTAL);
1823     this->SetTest("Start");
1824     this->SetTest("Configure");
1825     this->SetTest("Build");
1826     this->SetTest("MemCheck");
1827     this->SetTest("Coverage");
1828     this->SetTest("Submit");
1829   } else if (targ == "NightlyMemoryCheck") {
1830     this->SetTestModel(cmCTest::NIGHTLY);
1831     this->SetTest("Start");
1832     this->SetTest("Update");
1833     this->SetTest("Configure");
1834     this->SetTest("Build");
1835     this->SetTest("MemCheck");
1836     this->SetTest("Coverage");
1837     this->SetTest("Submit");
1838   } else {
1839     return false;
1840   }
1841   return true;
1842 }
1843
1844 void cmCTest::ErrorMessageUnknownDashDValue(std::string& val)
1845 {
1846   cmCTestLog(this, ERROR_MESSAGE,
1847              "CTest -D called with incorrect option: " << val << std::endl);
1848
1849   cmCTestLog(
1850     this, ERROR_MESSAGE,
1851     "Available options are:"
1852       << std::endl
1853       << "  ctest -D Continuous" << std::endl
1854       << "  ctest -D Continuous(Start|Update|Configure|Build)" << std::endl
1855       << "  ctest -D Continuous(Test|Coverage|MemCheck|Submit)" << std::endl
1856       << "  ctest -D Experimental" << std::endl
1857       << "  ctest -D Experimental(Start|Update|Configure|Build)" << std::endl
1858       << "  ctest -D Experimental(Test|Coverage|MemCheck|Submit)" << std::endl
1859       << "  ctest -D Nightly" << std::endl
1860       << "  ctest -D Nightly(Start|Update|Configure|Build)" << std::endl
1861       << "  ctest -D Nightly(Test|Coverage|MemCheck|Submit)" << std::endl
1862       << "  ctest -D NightlyMemoryCheck" << std::endl);
1863 }
1864
1865 bool cmCTest::CheckArgument(const std::string& arg, cm::string_view varg1,
1866                             const char* varg2)
1867 {
1868   return (arg == varg1) || (varg2 && arg == varg2);
1869 }
1870
1871 // Processes one command line argument (and its arguments if any)
1872 // for many simple options and then returns
1873 bool cmCTest::HandleCommandLineArguments(size_t& i,
1874                                          std::vector<std::string>& args,
1875                                          std::string& errormsg)
1876 {
1877   std::string arg = args[i];
1878   if (this->CheckArgument(arg, "-F"_s)) {
1879     this->Impl->Failover = true;
1880   } else if (this->CheckArgument(arg, "-j"_s, "--parallel") &&
1881              i < args.size() - 1) {
1882     i++;
1883     int plevel = atoi(args[i].c_str());
1884     this->SetParallelLevel(plevel);
1885     this->Impl->ParallelLevelSetInCli = true;
1886   } else if (cmHasPrefix(arg, "-j")) {
1887     int plevel = atoi(arg.substr(2).c_str());
1888     this->SetParallelLevel(plevel);
1889     this->Impl->ParallelLevelSetInCli = true;
1890   }
1891
1892   else if (this->CheckArgument(arg, "--repeat-until-fail"_s)) {
1893     if (i >= args.size() - 1) {
1894       errormsg = "'--repeat-until-fail' requires an argument";
1895       return false;
1896     }
1897     if (this->Impl->RepeatMode != cmCTest::Repeat::Never) {
1898       errormsg = "At most one '--repeat' option may be used.";
1899       return false;
1900     }
1901     i++;
1902     long repeat = 1;
1903     if (!cmStrToLong(args[i], &repeat)) {
1904       errormsg = cmStrCat("'--repeat-until-fail' given non-integer value '",
1905                           args[i], "'");
1906       return false;
1907     }
1908     this->Impl->RepeatCount = static_cast<int>(repeat);
1909     if (repeat > 1) {
1910       this->Impl->RepeatMode = cmCTest::Repeat::UntilFail;
1911     }
1912   }
1913
1914   else if (this->CheckArgument(arg, "--repeat"_s)) {
1915     if (i >= args.size() - 1) {
1916       errormsg = "'--repeat' requires an argument";
1917       return false;
1918     }
1919     if (this->Impl->RepeatMode != cmCTest::Repeat::Never) {
1920       errormsg = "At most one '--repeat' option may be used.";
1921       return false;
1922     }
1923     i++;
1924     cmsys::RegularExpression repeatRegex(
1925       "^(until-fail|until-pass|after-timeout):([0-9]+)$");
1926     if (repeatRegex.find(args[i])) {
1927       std::string const& count = repeatRegex.match(2);
1928       unsigned long n = 1;
1929       cmStrToULong(count, &n); // regex guarantees success
1930       this->Impl->RepeatCount = static_cast<int>(n);
1931       if (this->Impl->RepeatCount > 1) {
1932         std::string const& mode = repeatRegex.match(1);
1933         if (mode == "until-fail") {
1934           this->Impl->RepeatMode = cmCTest::Repeat::UntilFail;
1935         } else if (mode == "until-pass") {
1936           this->Impl->RepeatMode = cmCTest::Repeat::UntilPass;
1937         } else if (mode == "after-timeout") {
1938           this->Impl->RepeatMode = cmCTest::Repeat::AfterTimeout;
1939         }
1940       }
1941     } else {
1942       errormsg = cmStrCat("'--repeat' given invalid value '", args[i], "'");
1943       return false;
1944     }
1945   }
1946
1947   else if (this->CheckArgument(arg, "--test-load"_s) && i < args.size() - 1) {
1948     i++;
1949     unsigned long load;
1950     if (cmStrToULong(args[i], &load)) {
1951       this->SetTestLoad(load);
1952     } else {
1953       cmCTestLog(this, WARNING,
1954                  "Invalid value for 'Test Load' : " << args[i] << std::endl);
1955     }
1956   }
1957
1958   else if (this->CheckArgument(arg, "--no-compress-output"_s)) {
1959     this->Impl->CompressTestOutput = false;
1960   }
1961
1962   else if (this->CheckArgument(arg, "--print-labels"_s)) {
1963     this->Impl->PrintLabels = true;
1964   }
1965
1966   else if (this->CheckArgument(arg, "--http1.0"_s)) {
1967     this->Impl->UseHTTP10 = true;
1968   }
1969
1970   else if (this->CheckArgument(arg, "--timeout"_s) && i < args.size() - 1) {
1971     i++;
1972     auto timeout = cmDuration(atof(args[i].c_str()));
1973     this->Impl->GlobalTimeout = timeout;
1974   }
1975
1976   else if (this->CheckArgument(arg, "--stop-time"_s) && i < args.size() - 1) {
1977     i++;
1978     this->SetStopTime(args[i]);
1979   }
1980
1981   else if (this->CheckArgument(arg, "--stop-on-failure"_s)) {
1982     this->Impl->StopOnFailure = true;
1983   }
1984
1985   else if (this->CheckArgument(arg, "-C"_s, "--build-config") &&
1986            i < args.size() - 1) {
1987     i++;
1988     this->SetConfigType(args[i]);
1989   }
1990
1991   else if (this->CheckArgument(arg, "--debug"_s)) {
1992     this->Impl->Debug = true;
1993     this->Impl->ShowLineNumbers = true;
1994   } else if ((this->CheckArgument(arg, "--group"_s) ||
1995               // This is an undocumented / deprecated option.
1996               // "Track" has been renamed to "Group".
1997               this->CheckArgument(arg, "--track"_s)) &&
1998              i < args.size() - 1) {
1999     i++;
2000     this->Impl->SpecificGroup = args[i];
2001   } else if (this->CheckArgument(arg, "--show-line-numbers"_s)) {
2002     this->Impl->ShowLineNumbers = true;
2003   } else if (this->CheckArgument(arg, "--no-label-summary"_s)) {
2004     this->Impl->LabelSummary = false;
2005   } else if (this->CheckArgument(arg, "--no-subproject-summary"_s)) {
2006     this->Impl->SubprojectSummary = false;
2007   } else if (this->CheckArgument(arg, "-Q"_s, "--quiet")) {
2008     this->Impl->Quiet = true;
2009   } else if (this->CheckArgument(arg, "--progress"_s)) {
2010     this->Impl->TestProgressOutput = true;
2011   } else if (this->CheckArgument(arg, "-V"_s, "--verbose")) {
2012     this->Impl->Verbose = true;
2013   } else if (this->CheckArgument(arg, "-VV"_s, "--extra-verbose")) {
2014     this->Impl->ExtraVerbose = true;
2015     this->Impl->Verbose = true;
2016   } else if (this->CheckArgument(arg, "--output-on-failure"_s)) {
2017     this->Impl->OutputTestOutputOnTestFailure = true;
2018   } else if (this->CheckArgument(arg, "--test-output-size-passed"_s) &&
2019              i < args.size() - 1) {
2020     i++;
2021     long outputSize;
2022     if (cmStrToLong(args[i], &outputSize)) {
2023       this->Impl->TestHandler.SetTestOutputSizePassed(
2024         static_cast<int>(outputSize));
2025     } else {
2026       cmCTestLog(this, WARNING,
2027                  "Invalid value for '--test-output-size-passed': " << args[i]
2028                                                                    << "\n");
2029     }
2030   } else if (this->CheckArgument(arg, "--test-output-size-failed"_s) &&
2031              i < args.size() - 1) {
2032     i++;
2033     long outputSize;
2034     if (cmStrToLong(args[i], &outputSize)) {
2035       this->Impl->TestHandler.SetTestOutputSizeFailed(
2036         static_cast<int>(outputSize));
2037     } else {
2038       cmCTestLog(this, WARNING,
2039                  "Invalid value for '--test-output-size-failed': " << args[i]
2040                                                                    << "\n");
2041     }
2042   } else if (this->CheckArgument(arg, "--test-output-truncation"_s) &&
2043              i < args.size() - 1) {
2044     i++;
2045     if (!this->Impl->TestHandler.SetTestOutputTruncation(args[i])) {
2046       errormsg = "Invalid value for '--test-output-truncation': " + args[i];
2047       return false;
2048     }
2049   } else if (this->CheckArgument(arg, "-N"_s, "--show-only")) {
2050     this->Impl->ShowOnly = true;
2051   } else if (cmHasLiteralPrefix(arg, "--show-only=")) {
2052     this->Impl->ShowOnly = true;
2053
2054     // Check if a specific format is requested. Defaults to human readable
2055     // text.
2056     std::string argWithFormat = "--show-only=";
2057     std::string format = arg.substr(argWithFormat.length());
2058     if (format == "json-v1") {
2059       // Force quiet mode so the only output is the json object model.
2060       this->Impl->Quiet = true;
2061       this->Impl->OutputAsJson = true;
2062       this->Impl->OutputAsJsonVersion = 1;
2063     } else if (format != "human") {
2064       errormsg = "'--show-only=' given unknown value '" + format + "'";
2065       return false;
2066     }
2067   }
2068
2069   else if (this->CheckArgument(arg, "-O"_s, "--output-log") &&
2070            i < args.size() - 1) {
2071     i++;
2072     this->SetOutputLogFileName(args[i]);
2073   }
2074
2075   else if (this->CheckArgument(arg, "--tomorrow-tag"_s)) {
2076     this->Impl->TomorrowTag = true;
2077   } else if (this->CheckArgument(arg, "--force-new-ctest-process"_s)) {
2078     this->Impl->ForceNewCTestProcess = true;
2079   } else if (this->CheckArgument(arg, "-W"_s, "--max-width") &&
2080              i < args.size() - 1) {
2081     i++;
2082     this->Impl->MaxTestNameWidth = atoi(args[i].c_str());
2083   } else if (this->CheckArgument(arg, "--interactive-debug-mode"_s) &&
2084              i < args.size() - 1) {
2085     i++;
2086     this->Impl->InteractiveDebugMode = cmIsOn(args[i]);
2087   } else if (this->CheckArgument(arg, "--submit-index"_s) &&
2088              i < args.size() - 1) {
2089     i++;
2090     this->Impl->SubmitIndex = atoi(args[i].c_str());
2091     if (this->Impl->SubmitIndex < 0) {
2092       this->Impl->SubmitIndex = 0;
2093     }
2094   }
2095
2096   else if (this->CheckArgument(arg, "--overwrite"_s) && i < args.size() - 1) {
2097     i++;
2098     this->AddCTestConfigurationOverwrite(args[i]);
2099   } else if (this->CheckArgument(arg, "-A"_s, "--add-notes") &&
2100              i < args.size() - 1) {
2101     this->Impl->ProduceXML = true;
2102     this->SetTest("Notes");
2103     i++;
2104     this->SetNotesFiles(args[i]);
2105     return true;
2106   } else if (this->CheckArgument(arg, "--test-dir"_s)) {
2107     if (i >= args.size() - 1) {
2108       errormsg = "'--test-dir' requires an argument";
2109       return false;
2110     }
2111     i++;
2112     this->Impl->TestDir = std::string(args[i]);
2113   } else if (this->CheckArgument(arg, "--output-junit"_s)) {
2114     if (i >= args.size() - 1) {
2115       errormsg = "'--output-junit' requires an argument";
2116       return false;
2117     }
2118     i++;
2119     this->SetOutputJUnitFileName(std::string(args[i]));
2120   }
2121
2122   cm::string_view noTestsPrefix = "--no-tests=";
2123   if (cmHasPrefix(arg, noTestsPrefix)) {
2124     cm::string_view noTestsMode =
2125       cm::string_view(arg).substr(noTestsPrefix.length());
2126     if (noTestsMode == "error") {
2127       this->Impl->NoTestsMode = cmCTest::NoTests::Error;
2128     } else if (noTestsMode != "ignore") {
2129       errormsg =
2130         cmStrCat("'--no-tests=' given unknown value '", noTestsMode, '\'');
2131       return false;
2132     } else {
2133       this->Impl->NoTestsMode = cmCTest::NoTests::Ignore;
2134     }
2135   }
2136
2137   // options that control what tests are run
2138   else if (this->CheckArgument(arg, "-I"_s, "--tests-information") &&
2139            i < args.size() - 1) {
2140     i++;
2141     this->GetTestHandler()->SetPersistentOption("TestsToRunInformation",
2142                                                 args[i].c_str());
2143     this->GetMemCheckHandler()->SetPersistentOption("TestsToRunInformation",
2144                                                     args[i].c_str());
2145   } else if (this->CheckArgument(arg, "-U"_s, "--union")) {
2146     this->GetTestHandler()->SetPersistentOption("UseUnion", "true");
2147     this->GetMemCheckHandler()->SetPersistentOption("UseUnion", "true");
2148   } else if (this->CheckArgument(arg, "-R"_s, "--tests-regex") &&
2149              i < args.size() - 1) {
2150     i++;
2151     this->GetTestHandler()->SetPersistentOption("IncludeRegularExpression",
2152                                                 args[i].c_str());
2153     this->GetMemCheckHandler()->SetPersistentOption("IncludeRegularExpression",
2154                                                     args[i].c_str());
2155   } else if (this->CheckArgument(arg, "-L"_s, "--label-regex") &&
2156              i < args.size() - 1) {
2157     i++;
2158     this->GetTestHandler()->AddPersistentMultiOption("LabelRegularExpression",
2159                                                      args[i]);
2160     this->GetMemCheckHandler()->AddPersistentMultiOption(
2161       "LabelRegularExpression", args[i]);
2162   } else if (this->CheckArgument(arg, "-LE"_s, "--label-exclude") &&
2163              i < args.size() - 1) {
2164     i++;
2165     this->GetTestHandler()->AddPersistentMultiOption(
2166       "ExcludeLabelRegularExpression", args[i]);
2167     this->GetMemCheckHandler()->AddPersistentMultiOption(
2168       "ExcludeLabelRegularExpression", args[i]);
2169   }
2170
2171   else if (this->CheckArgument(arg, "-E"_s, "--exclude-regex") &&
2172            i < args.size() - 1) {
2173     i++;
2174     this->GetTestHandler()->SetPersistentOption("ExcludeRegularExpression",
2175                                                 args[i].c_str());
2176     this->GetMemCheckHandler()->SetPersistentOption("ExcludeRegularExpression",
2177                                                     args[i].c_str());
2178   }
2179
2180   else if (this->CheckArgument(arg, "-FA"_s, "--fixture-exclude-any") &&
2181            i < args.size() - 1) {
2182     i++;
2183     this->GetTestHandler()->SetPersistentOption(
2184       "ExcludeFixtureRegularExpression", args[i].c_str());
2185     this->GetMemCheckHandler()->SetPersistentOption(
2186       "ExcludeFixtureRegularExpression", args[i].c_str());
2187   } else if (this->CheckArgument(arg, "-FS"_s, "--fixture-exclude-setup") &&
2188              i < args.size() - 1) {
2189     i++;
2190     this->GetTestHandler()->SetPersistentOption(
2191       "ExcludeFixtureSetupRegularExpression", args[i].c_str());
2192     this->GetMemCheckHandler()->SetPersistentOption(
2193       "ExcludeFixtureSetupRegularExpression", args[i].c_str());
2194   } else if (this->CheckArgument(arg, "-FC"_s, "--fixture-exclude-cleanup") &&
2195              i < args.size() - 1) {
2196     i++;
2197     this->GetTestHandler()->SetPersistentOption(
2198       "ExcludeFixtureCleanupRegularExpression", args[i].c_str());
2199     this->GetMemCheckHandler()->SetPersistentOption(
2200       "ExcludeFixtureCleanupRegularExpression", args[i].c_str());
2201   }
2202
2203   else if (this->CheckArgument(arg, "--resource-spec-file"_s) &&
2204            i < args.size() - 1) {
2205     i++;
2206     this->GetTestHandler()->SetPersistentOption("ResourceSpecFile",
2207                                                 args[i].c_str());
2208     this->GetMemCheckHandler()->SetPersistentOption("ResourceSpecFile",
2209                                                     args[i].c_str());
2210   }
2211
2212   else if (this->CheckArgument(arg, "--rerun-failed"_s)) {
2213     this->GetTestHandler()->SetPersistentOption("RerunFailed", "true");
2214     this->GetMemCheckHandler()->SetPersistentOption("RerunFailed", "true");
2215   }
2216   return true;
2217 }
2218
2219 #if !defined(_WIN32)
2220 bool cmCTest::ConsoleIsNotDumb()
2221 {
2222   std::string term_env_variable;
2223   if (cmSystemTools::GetEnv("TERM", term_env_variable)) {
2224     return isatty(1) && term_env_variable != "dumb";
2225   }
2226   return false;
2227 }
2228 #endif
2229
2230 bool cmCTest::ProgressOutputSupportedByConsole()
2231 {
2232 #if defined(_WIN32)
2233   // On Windows we need a console buffer.
2234   void* console = GetStdHandle(STD_OUTPUT_HANDLE);
2235   CONSOLE_SCREEN_BUFFER_INFO csbi;
2236   return GetConsoleScreenBufferInfo(console, &csbi);
2237 #else
2238   // On UNIX we need a non-dumb tty.
2239   return ConsoleIsNotDumb();
2240 #endif
2241 }
2242
2243 bool cmCTest::ColoredOutputSupportedByConsole()
2244 {
2245 #if defined(_WIN32)
2246   // Not supported on Windows
2247   return false;
2248 #else
2249   // On UNIX we need a non-dumb tty.
2250   std::string clicolor_force;
2251   if (cmSystemTools::GetEnv("CLICOLOR_FORCE", clicolor_force) &&
2252       !clicolor_force.empty() && clicolor_force != "0") {
2253     return true;
2254   }
2255   std::string clicolor;
2256   if (cmSystemTools::GetEnv("CLICOLOR", clicolor) && clicolor == "0") {
2257     return false;
2258   }
2259   return ConsoleIsNotDumb();
2260 #endif
2261 }
2262
2263 // handle the -S -SR and -SP arguments
2264 void cmCTest::HandleScriptArguments(size_t& i, std::vector<std::string>& args,
2265                                     bool& SRArgumentSpecified)
2266 {
2267   std::string arg = args[i];
2268   if (this->CheckArgument(arg, "-SP"_s, "--script-new-process") &&
2269       i < args.size() - 1) {
2270     this->Impl->RunConfigurationScript = true;
2271     i++;
2272     cmCTestScriptHandler* ch = this->GetScriptHandler();
2273     // -SR is an internal argument, -SP should be ignored when it is passed
2274     if (!SRArgumentSpecified) {
2275       ch->AddConfigurationScript(args[i], false);
2276     }
2277   }
2278
2279   if (this->CheckArgument(arg, "-SR"_s, "--script-run") &&
2280       i < args.size() - 1) {
2281     SRArgumentSpecified = true;
2282     this->Impl->RunConfigurationScript = true;
2283     i++;
2284     cmCTestScriptHandler* ch = this->GetScriptHandler();
2285     ch->AddConfigurationScript(args[i], true);
2286   }
2287
2288   if (this->CheckArgument(arg, "-S"_s, "--script") && i < args.size() - 1) {
2289     this->Impl->RunConfigurationScript = true;
2290     i++;
2291     cmCTestScriptHandler* ch = this->GetScriptHandler();
2292     // -SR is an internal argument, -S should be ignored when it is passed
2293     if (!SRArgumentSpecified) {
2294       ch->AddConfigurationScript(args[i], true);
2295     }
2296   }
2297 }
2298
2299 bool cmCTest::AddVariableDefinition(const std::string& arg)
2300 {
2301   std::string name;
2302   std::string value;
2303   cmStateEnums::CacheEntryType type = cmStateEnums::UNINITIALIZED;
2304
2305   if (cmake::ParseCacheEntry(arg, name, value, type)) {
2306     this->Impl->Definitions[name] = value;
2307     return true;
2308   }
2309
2310   return false;
2311 }
2312
2313 void cmCTest::SetPersistentOptionIfNotEmpty(const std::string& value,
2314                                             const std::string& optionName)
2315 {
2316   if (!value.empty()) {
2317     this->GetTestHandler()->SetPersistentOption(optionName, value.c_str());
2318     this->GetMemCheckHandler()->SetPersistentOption(optionName, value.c_str());
2319   }
2320 }
2321
2322 void cmCTest::AddPersistentMultiOptionIfNotEmpty(const std::string& value,
2323                                                  const std::string& optionName)
2324 {
2325   if (!value.empty()) {
2326     this->GetTestHandler()->AddPersistentMultiOption(optionName, value);
2327     this->GetMemCheckHandler()->AddPersistentMultiOption(optionName, value);
2328   }
2329 }
2330
2331 bool cmCTest::SetArgsFromPreset(const std::string& presetName,
2332                                 bool listPresets)
2333 {
2334   const auto workingDirectory = cmSystemTools::GetCurrentWorkingDirectory();
2335
2336   cmCMakePresetsGraph settingsFile;
2337   auto result = settingsFile.ReadProjectPresets(workingDirectory);
2338   if (result != cmCMakePresetsGraph::ReadFileResult::READ_OK) {
2339     cmSystemTools::Error(
2340       cmStrCat("Could not read presets from ", workingDirectory, ": ",
2341                cmCMakePresetsGraph::ResultToString(result)));
2342     return false;
2343   }
2344
2345   if (listPresets) {
2346     settingsFile.PrintTestPresetList();
2347     return true;
2348   }
2349
2350   auto presetPair = settingsFile.TestPresets.find(presetName);
2351   if (presetPair == settingsFile.TestPresets.end()) {
2352     cmSystemTools::Error(cmStrCat("No such test preset in ", workingDirectory,
2353                                   ": \"", presetName, '"'));
2354     settingsFile.PrintTestPresetList();
2355     return false;
2356   }
2357
2358   if (presetPair->second.Unexpanded.Hidden) {
2359     cmSystemTools::Error(cmStrCat("Cannot use hidden test preset in ",
2360                                   workingDirectory, ": \"", presetName, '"'));
2361     settingsFile.PrintTestPresetList();
2362     return false;
2363   }
2364
2365   auto const& expandedPreset = presetPair->second.Expanded;
2366   if (!expandedPreset) {
2367     cmSystemTools::Error(cmStrCat("Could not evaluate test preset \"",
2368                                   presetName, "\": Invalid macro expansion"));
2369     settingsFile.PrintTestPresetList();
2370     return false;
2371   }
2372
2373   if (!expandedPreset->ConditionResult) {
2374     cmSystemTools::Error(cmStrCat("Cannot use disabled test preset in ",
2375                                   workingDirectory, ": \"", presetName, '"'));
2376     settingsFile.PrintTestPresetList();
2377     return false;
2378   }
2379
2380   auto configurePresetPair =
2381     settingsFile.ConfigurePresets.find(expandedPreset->ConfigurePreset);
2382   if (configurePresetPair == settingsFile.ConfigurePresets.end()) {
2383     cmSystemTools::Error(cmStrCat("No such configure preset in ",
2384                                   workingDirectory, ": \"",
2385                                   expandedPreset->ConfigurePreset, '"'));
2386     settingsFile.PrintConfigurePresetList();
2387     return false;
2388   }
2389
2390   if (configurePresetPair->second.Unexpanded.Hidden) {
2391     cmSystemTools::Error(cmStrCat("Cannot use hidden configure preset in ",
2392                                   workingDirectory, ": \"",
2393                                   expandedPreset->ConfigurePreset, '"'));
2394     settingsFile.PrintConfigurePresetList();
2395     return false;
2396   }
2397
2398   auto const& expandedConfigurePreset = configurePresetPair->second.Expanded;
2399   if (!expandedConfigurePreset) {
2400     cmSystemTools::Error(cmStrCat("Could not evaluate configure preset \"",
2401                                   expandedPreset->ConfigurePreset,
2402                                   "\": Invalid macro expansion"));
2403     return false;
2404   }
2405
2406   auto presetEnvironment = expandedPreset->Environment;
2407   for (auto const& var : presetEnvironment) {
2408     if (var.second) {
2409       cmSystemTools::PutEnv(cmStrCat(var.first, '=', *var.second));
2410     }
2411   }
2412
2413   if (!expandedPreset->Configuration.empty()) {
2414     this->SetConfigType(expandedPreset->Configuration);
2415   }
2416
2417   // Set build directory to value specified by the configure preset.
2418   this->AddCTestConfigurationOverwrite(
2419     cmStrCat("BuildDirectory=", expandedConfigurePreset->BinaryDir));
2420   for (const auto& kvp : expandedPreset->OverwriteConfigurationFile) {
2421     this->AddCTestConfigurationOverwrite(kvp);
2422   }
2423
2424   if (expandedPreset->Output) {
2425     this->Impl->TestProgressOutput =
2426       expandedPreset->Output->ShortProgress.value_or(false);
2427
2428     if (expandedPreset->Output->Verbosity) {
2429       const auto& verbosity = *expandedPreset->Output->Verbosity;
2430       switch (verbosity) {
2431         case cmCMakePresetsGraph::TestPreset::OutputOptions::VerbosityEnum::
2432           Extra:
2433           this->Impl->ExtraVerbose = true;
2434           CM_FALLTHROUGH;
2435         case cmCMakePresetsGraph::TestPreset::OutputOptions::VerbosityEnum::
2436           Verbose:
2437           this->Impl->Verbose = true;
2438           break;
2439         case cmCMakePresetsGraph::TestPreset::OutputOptions::VerbosityEnum::
2440           Default:
2441         default:
2442           // leave default settings
2443           break;
2444       }
2445     }
2446
2447     this->Impl->Debug = expandedPreset->Output->Debug.value_or(false);
2448     this->Impl->ShowLineNumbers =
2449       expandedPreset->Output->Debug.value_or(false);
2450     this->Impl->OutputTestOutputOnTestFailure =
2451       expandedPreset->Output->OutputOnFailure.value_or(false);
2452     this->Impl->Quiet = expandedPreset->Output->Quiet.value_or(false);
2453
2454     if (!expandedPreset->Output->OutputLogFile.empty()) {
2455       this->SetOutputLogFileName(expandedPreset->Output->OutputLogFile);
2456     }
2457     if (!expandedPreset->Output->OutputJUnitFile.empty()) {
2458       this->SetOutputJUnitFileName(expandedPreset->Output->OutputJUnitFile);
2459     }
2460
2461     this->Impl->LabelSummary =
2462       expandedPreset->Output->LabelSummary.value_or(true);
2463     this->Impl->SubprojectSummary =
2464       expandedPreset->Output->SubprojectSummary.value_or(true);
2465
2466     if (expandedPreset->Output->MaxPassedTestOutputSize) {
2467       this->Impl->TestHandler.SetTestOutputSizePassed(
2468         *expandedPreset->Output->MaxPassedTestOutputSize);
2469     }
2470
2471     if (expandedPreset->Output->MaxFailedTestOutputSize) {
2472       this->Impl->TestHandler.SetTestOutputSizeFailed(
2473         *expandedPreset->Output->MaxFailedTestOutputSize);
2474     }
2475
2476     if (expandedPreset->Output->TestOutputTruncation) {
2477       this->Impl->TestHandler.TestOutputTruncation =
2478         *expandedPreset->Output->TestOutputTruncation;
2479     }
2480
2481     if (expandedPreset->Output->MaxTestNameWidth) {
2482       this->Impl->MaxTestNameWidth = *expandedPreset->Output->MaxTestNameWidth;
2483     }
2484   }
2485
2486   if (expandedPreset->Filter) {
2487     if (expandedPreset->Filter->Include) {
2488       this->SetPersistentOptionIfNotEmpty(
2489         expandedPreset->Filter->Include->Name, "IncludeRegularExpression");
2490       this->AddPersistentMultiOptionIfNotEmpty(
2491         expandedPreset->Filter->Include->Label, "LabelRegularExpression");
2492
2493       if (expandedPreset->Filter->Include->Index) {
2494         if (expandedPreset->Filter->Include->Index->IndexFile.empty()) {
2495           const auto& start = expandedPreset->Filter->Include->Index->Start;
2496           const auto& end = expandedPreset->Filter->Include->Index->End;
2497           const auto& stride = expandedPreset->Filter->Include->Index->Stride;
2498           std::string indexOptions;
2499           indexOptions += (start ? std::to_string(*start) : "") + ",";
2500           indexOptions += (end ? std::to_string(*end) : "") + ",";
2501           indexOptions += (stride ? std::to_string(*stride) : "") + ",";
2502           indexOptions +=
2503             cmJoin(expandedPreset->Filter->Include->Index->SpecificTests, ",");
2504
2505           this->SetPersistentOptionIfNotEmpty(indexOptions,
2506                                               "TestsToRunInformation");
2507         } else {
2508           this->SetPersistentOptionIfNotEmpty(
2509             expandedPreset->Filter->Include->Index->IndexFile,
2510             "TestsToRunInformation");
2511         }
2512       }
2513
2514       if (expandedPreset->Filter->Include->UseUnion.value_or(false)) {
2515         this->GetTestHandler()->SetPersistentOption("UseUnion", "true");
2516         this->GetMemCheckHandler()->SetPersistentOption("UseUnion", "true");
2517       }
2518     }
2519
2520     if (expandedPreset->Filter->Exclude) {
2521       this->SetPersistentOptionIfNotEmpty(
2522         expandedPreset->Filter->Exclude->Name, "ExcludeRegularExpression");
2523       this->AddPersistentMultiOptionIfNotEmpty(
2524         expandedPreset->Filter->Exclude->Label,
2525         "ExcludeLabelRegularExpression");
2526
2527       if (expandedPreset->Filter->Exclude->Fixtures) {
2528         this->SetPersistentOptionIfNotEmpty(
2529           expandedPreset->Filter->Exclude->Fixtures->Any,
2530           "ExcludeFixtureRegularExpression");
2531         this->SetPersistentOptionIfNotEmpty(
2532           expandedPreset->Filter->Exclude->Fixtures->Setup,
2533           "ExcludeFixtureSetupRegularExpression");
2534         this->SetPersistentOptionIfNotEmpty(
2535           expandedPreset->Filter->Exclude->Fixtures->Cleanup,
2536           "ExcludeFixtureCleanupRegularExpression");
2537       }
2538     }
2539   }
2540
2541   if (expandedPreset->Execution) {
2542     this->Impl->StopOnFailure =
2543       expandedPreset->Execution->StopOnFailure.value_or(false);
2544     this->Impl->Failover =
2545       expandedPreset->Execution->EnableFailover.value_or(false);
2546
2547     if (expandedPreset->Execution->Jobs) {
2548       auto jobs = *expandedPreset->Execution->Jobs;
2549       this->SetParallelLevel(jobs);
2550       this->Impl->ParallelLevelSetInCli = true;
2551     }
2552
2553     this->SetPersistentOptionIfNotEmpty(
2554       expandedPreset->Execution->ResourceSpecFile, "ResourceSpecFile");
2555
2556     if (expandedPreset->Execution->TestLoad) {
2557       auto testLoad = *expandedPreset->Execution->TestLoad;
2558       this->SetTestLoad(testLoad);
2559     }
2560
2561     if (expandedPreset->Execution->ShowOnly) {
2562       this->Impl->ShowOnly = true;
2563
2564       switch (*expandedPreset->Execution->ShowOnly) {
2565         case cmCMakePresetsGraph::TestPreset::ExecutionOptions::ShowOnlyEnum::
2566           JsonV1:
2567           this->Impl->Quiet = true;
2568           this->Impl->OutputAsJson = true;
2569           this->Impl->OutputAsJsonVersion = 1;
2570           break;
2571         case cmCMakePresetsGraph::TestPreset::ExecutionOptions::ShowOnlyEnum::
2572           Human:
2573           // intentional fallthrough (human is the default)
2574         default:
2575           break;
2576       }
2577     }
2578
2579     if (expandedPreset->Execution->Repeat) {
2580       this->Impl->RepeatCount = expandedPreset->Execution->Repeat->Count;
2581       switch (expandedPreset->Execution->Repeat->Mode) {
2582         case cmCMakePresetsGraph::TestPreset::ExecutionOptions::RepeatOptions::
2583           ModeEnum::UntilFail:
2584           this->Impl->RepeatMode = cmCTest::Repeat::UntilFail;
2585           break;
2586         case cmCMakePresetsGraph::TestPreset::ExecutionOptions::RepeatOptions::
2587           ModeEnum::UntilPass:
2588           this->Impl->RepeatMode = cmCTest::Repeat::UntilPass;
2589           break;
2590         case cmCMakePresetsGraph::TestPreset::ExecutionOptions::RepeatOptions::
2591           ModeEnum::AfterTimeout:
2592           this->Impl->RepeatMode = cmCTest::Repeat::AfterTimeout;
2593           break;
2594         default:
2595           // should never default since mode is required
2596           return false;
2597       }
2598     }
2599
2600     if (expandedPreset->Execution->InteractiveDebugging) {
2601       this->Impl->InteractiveDebugMode =
2602         *expandedPreset->Execution->InteractiveDebugging;
2603     }
2604
2605     if (expandedPreset->Execution->ScheduleRandom.value_or(false)) {
2606       this->Impl->ScheduleType = "Random";
2607     }
2608
2609     if (expandedPreset->Execution->Timeout) {
2610       this->Impl->GlobalTimeout =
2611         cmDuration(*expandedPreset->Execution->Timeout);
2612     }
2613
2614     if (expandedPreset->Execution->NoTestsAction) {
2615       switch (*expandedPreset->Execution->NoTestsAction) {
2616         case cmCMakePresetsGraph::TestPreset::ExecutionOptions::
2617           NoTestsActionEnum::Error:
2618           this->Impl->NoTestsMode = cmCTest::NoTests::Error;
2619           break;
2620         case cmCMakePresetsGraph::TestPreset::ExecutionOptions::
2621           NoTestsActionEnum::Ignore:
2622           this->Impl->NoTestsMode = cmCTest::NoTests::Ignore;
2623           break;
2624         case cmCMakePresetsGraph::TestPreset::ExecutionOptions::
2625           NoTestsActionEnum::Default:
2626           break;
2627         default:
2628           // should never default
2629           return false;
2630       }
2631     }
2632   }
2633
2634   return true;
2635 }
2636
2637 // the main entry point of ctest, called from main
2638 int cmCTest::Run(std::vector<std::string>& args, std::string* output)
2639 {
2640   const char* ctestExec = "ctest";
2641   bool cmakeAndTest = false;
2642   bool executeTests = true;
2643   bool SRArgumentSpecified = false;
2644
2645   // copy the command line
2646   cm::append(this->Impl->InitialCommandLineArguments, args);
2647
2648   // check if a test preset was specified
2649
2650   bool listPresets =
2651     find(args.begin(), args.end(), "--list-presets") != args.end();
2652   auto it =
2653     std::find_if(args.begin(), args.end(), [](std::string const& arg) -> bool {
2654       return arg == "--preset" || cmHasLiteralPrefix(arg, "--preset=");
2655     });
2656   if (listPresets || it != args.end()) {
2657     std::string errormsg;
2658     bool success;
2659
2660     if (listPresets) {
2661       // If listing presets we don't need a presetName
2662       success = this->SetArgsFromPreset("", listPresets);
2663     } else {
2664       if (cmHasLiteralPrefix(*it, "--preset=")) {
2665         auto presetName = it->substr(9);
2666         success = this->SetArgsFromPreset(presetName, listPresets);
2667       } else if (++it != args.end()) {
2668         auto presetName = *it;
2669         success = this->SetArgsFromPreset(presetName, listPresets);
2670       } else {
2671         cmSystemTools::Error("'--preset' requires an argument");
2672         success = false;
2673       }
2674     }
2675
2676     if (listPresets) {
2677       return success ? 0 : 1;
2678     }
2679
2680     if (!success) {
2681       return 1;
2682     }
2683   }
2684
2685   // process the command line arguments
2686   for (size_t i = 1; i < args.size(); ++i) {
2687     // handle the simple commandline arguments
2688     std::string errormsg;
2689     if (!this->HandleCommandLineArguments(i, args, errormsg)) {
2690       cmSystemTools::Error(errormsg);
2691       return 1;
2692     }
2693
2694     // handle the script arguments -S -SR -SP
2695     this->HandleScriptArguments(i, args, SRArgumentSpecified);
2696
2697     // --dashboard: handle a request for a dashboard
2698     std::string arg = args[i];
2699     if (this->CheckArgument(arg, "-D"_s, "--dashboard") &&
2700         i < args.size() - 1) {
2701       this->Impl->ProduceXML = true;
2702       i++;
2703       std::string targ = args[i];
2704       // AddTestsForDashboard parses the dashboard type and converts it
2705       // into the separate stages
2706       if (!this->AddTestsForDashboardType(targ)) {
2707         if (!this->AddVariableDefinition(targ)) {
2708           this->ErrorMessageUnknownDashDValue(targ);
2709           executeTests = false;
2710         }
2711       }
2712     }
2713
2714     // If it's not exactly -D, but it starts with -D, then try to parse out
2715     // a variable definition from it, same as CMake does. Unsuccessful
2716     // attempts are simply ignored since previous ctest versions ignore
2717     // this too. (As well as many other unknown command line args.)
2718     //
2719     if (arg != "-D" && cmHasLiteralPrefix(arg, "-D")) {
2720       std::string input = arg.substr(2);
2721       this->AddVariableDefinition(input);
2722     }
2723
2724     // --test-action: calls SetTest(<stage>, /*report=*/ false) to enable
2725     // the corresponding stage
2726     if (!this->HandleTestActionArgument(ctestExec, i, args)) {
2727       executeTests = false;
2728     }
2729
2730     // --test-model: what type of test model
2731     if (!this->HandleTestModelArgument(ctestExec, i, args)) {
2732       executeTests = false;
2733     }
2734
2735     // --extra-submit
2736     if (this->CheckArgument(arg, "--extra-submit"_s) && i < args.size() - 1) {
2737       this->Impl->ProduceXML = true;
2738       this->SetTest("Submit");
2739       i++;
2740       if (!this->SubmitExtraFiles(args[i])) {
2741         return 0;
2742       }
2743     }
2744
2745     // --build-and-test options
2746     if (this->CheckArgument(arg, "--build-and-test"_s) &&
2747         i < args.size() - 1) {
2748       cmakeAndTest = true;
2749     }
2750
2751     // --schedule-random
2752     if (this->CheckArgument(arg, "--schedule-random"_s)) {
2753       this->Impl->ScheduleType = "Random";
2754     }
2755
2756     // pass the argument to all the handlers as well, but it may no longer be
2757     // set to what it was originally so I'm not sure this is working as
2758     // intended
2759     for (auto& handler : this->Impl->GetTestingHandlers()) {
2760       if (!handler->ProcessCommandLineArguments(arg, i, args)) {
2761         cmCTestLog(this, ERROR_MESSAGE,
2762                    "Problem parsing command line arguments within a handler");
2763         return 0;
2764       }
2765     }
2766   } // the close of the for argument loop
2767
2768   // handle CTEST_PARALLEL_LEVEL environment variable
2769   if (!this->Impl->ParallelLevelSetInCli) {
2770     std::string parallel;
2771     if (cmSystemTools::GetEnv("CTEST_PARALLEL_LEVEL", parallel)) {
2772       int plevel = atoi(parallel.c_str());
2773       this->SetParallelLevel(plevel);
2774     }
2775   }
2776
2777   // TestProgressOutput only supported if console supports it and not logging
2778   // to a file
2779   this->Impl->TestProgressOutput = this->Impl->TestProgressOutput &&
2780     !this->Impl->OutputLogFile && this->ProgressOutputSupportedByConsole();
2781 #ifdef _WIN32
2782   if (this->Impl->TestProgressOutput) {
2783     // Disable output line buffering so we can print content without
2784     // a newline.
2785     std::setvbuf(stdout, nullptr, _IONBF, 0);
2786   }
2787 #endif
2788
2789   // now what should cmake do? if --build-and-test was specified then
2790   // we run the build and test handler and return
2791   if (cmakeAndTest) {
2792     return this->RunCMakeAndTest(output);
2793   }
2794
2795   if (executeTests) {
2796     return this->ExecuteTests();
2797   }
2798
2799   return 1;
2800 }
2801
2802 bool cmCTest::HandleTestActionArgument(const char* ctestExec, size_t& i,
2803                                        const std::vector<std::string>& args)
2804 {
2805   bool success = true;
2806   std::string const& arg = args[i];
2807   if (this->CheckArgument(arg, "-T"_s, "--test-action") &&
2808       (i < args.size() - 1)) {
2809     this->Impl->ProduceXML = true;
2810     i++;
2811     if (!this->SetTest(args[i], false)) {
2812       success = false;
2813       cmCTestLog(this, ERROR_MESSAGE,
2814                  "CTest -T called with incorrect option: " << args[i]
2815                                                            << std::endl);
2816       cmCTestLog(this, ERROR_MESSAGE,
2817                  "Available options are:"
2818                    << std::endl
2819                    << "  " << ctestExec << " -T all" << std::endl
2820                    << "  " << ctestExec << " -T start" << std::endl
2821                    << "  " << ctestExec << " -T update" << std::endl
2822                    << "  " << ctestExec << " -T configure" << std::endl
2823                    << "  " << ctestExec << " -T build" << std::endl
2824                    << "  " << ctestExec << " -T test" << std::endl
2825                    << "  " << ctestExec << " -T coverage" << std::endl
2826                    << "  " << ctestExec << " -T memcheck" << std::endl
2827                    << "  " << ctestExec << " -T notes" << std::endl
2828                    << "  " << ctestExec << " -T submit" << std::endl);
2829     }
2830   }
2831   return success;
2832 }
2833
2834 bool cmCTest::HandleTestModelArgument(const char* ctestExec, size_t& i,
2835                                       const std::vector<std::string>& args)
2836 {
2837   bool success = true;
2838   std::string const& arg = args[i];
2839   if (this->CheckArgument(arg, "-M"_s, "--test-model") &&
2840       (i < args.size() - 1)) {
2841     i++;
2842     std::string const& str = args[i];
2843     if (cmSystemTools::LowerCase(str) == "nightly"_s) {
2844       this->SetTestModel(cmCTest::NIGHTLY);
2845     } else if (cmSystemTools::LowerCase(str) == "continuous"_s) {
2846       this->SetTestModel(cmCTest::CONTINUOUS);
2847     } else if (cmSystemTools::LowerCase(str) == "experimental"_s) {
2848       this->SetTestModel(cmCTest::EXPERIMENTAL);
2849     } else {
2850       success = false;
2851       cmCTestLog(this, ERROR_MESSAGE,
2852                  "CTest -M called with incorrect option: " << str
2853                                                            << std::endl);
2854       cmCTestLog(this, ERROR_MESSAGE,
2855                  "Available options are:"
2856                    << std::endl
2857                    << "  " << ctestExec << " -M Continuous" << std::endl
2858                    << "  " << ctestExec << " -M Experimental" << std::endl
2859                    << "  " << ctestExec << " -M Nightly" << std::endl);
2860     }
2861   }
2862   return success;
2863 }
2864
2865 int cmCTest::ExecuteTests()
2866 {
2867   int res;
2868   // call process directory
2869   if (this->Impl->RunConfigurationScript) {
2870     if (this->Impl->ExtraVerbose) {
2871       cmCTestLog(this, OUTPUT, "* Extra verbosity turned on" << std::endl);
2872     }
2873     for (auto& handler : this->Impl->GetTestingHandlers()) {
2874       handler->SetVerbose(this->Impl->ExtraVerbose);
2875       handler->SetSubmitIndex(this->Impl->SubmitIndex);
2876     }
2877     this->GetScriptHandler()->SetVerbose(this->Impl->Verbose);
2878     res = this->GetScriptHandler()->ProcessHandler();
2879     if (res != 0) {
2880       cmCTestLog(this, DEBUG,
2881                  "running script failing returning: " << res << std::endl);
2882     }
2883
2884   } else {
2885     // What is this?  -V seems to be the same as -VV,
2886     // and Verbose is always on in this case
2887     this->Impl->ExtraVerbose = this->Impl->Verbose;
2888     this->Impl->Verbose = true;
2889     for (auto& handler : this->Impl->GetTestingHandlers()) {
2890       handler->SetVerbose(this->Impl->Verbose);
2891       handler->SetSubmitIndex(this->Impl->SubmitIndex);
2892     }
2893
2894     const std::string currDir = cmSystemTools::GetCurrentWorkingDirectory();
2895     std::string workDir = currDir;
2896     if (!this->Impl->TestDir.empty()) {
2897       workDir = cmSystemTools::CollapseFullPath(this->Impl->TestDir);
2898     }
2899
2900     if (currDir != workDir) {
2901       if (!this->TryToChangeDirectory(workDir)) {
2902         return 1;
2903       }
2904     }
2905
2906     if (!this->Initialize(workDir.c_str(), nullptr)) {
2907       res = 12;
2908       cmCTestLog(this, ERROR_MESSAGE,
2909                  "Problem initializing the dashboard." << std::endl);
2910     } else {
2911       res = this->ProcessSteps();
2912     }
2913     this->Finalize();
2914
2915     if (currDir != workDir) {
2916       cmSystemTools::ChangeDirectory(currDir);
2917     }
2918   }
2919   if (res != 0) {
2920     cmCTestLog(this, DEBUG,
2921                "Running a test(s) failed returning : " << res << std::endl);
2922   }
2923   return res;
2924 }
2925
2926 int cmCTest::RunCMakeAndTest(std::string* output)
2927 {
2928   this->Impl->Verbose = true;
2929   cmCTestBuildAndTestHandler* handler = this->GetBuildAndTestHandler();
2930   int retv = handler->ProcessHandler();
2931   *output = handler->GetOutput();
2932 #ifndef CMAKE_BOOTSTRAP
2933   cmDynamicLoader::FlushCache();
2934 #endif
2935   if (retv != 0) {
2936     cmCTestLog(this, DEBUG,
2937                "build and test failing returning: " << retv << std::endl);
2938   }
2939   return retv;
2940 }
2941
2942 void cmCTest::SetNotesFiles(const std::string& notes)
2943 {
2944   this->Impl->NotesFiles = notes;
2945 }
2946
2947 bool cmCTest::GetStopOnFailure() const
2948 {
2949   return this->Impl->StopOnFailure;
2950 }
2951
2952 void cmCTest::SetStopOnFailure(bool stop)
2953 {
2954   this->Impl->StopOnFailure = stop;
2955 }
2956
2957 std::chrono::system_clock::time_point cmCTest::GetStopTime() const
2958 {
2959   return this->Impl->StopTime;
2960 }
2961
2962 void cmCTest::SetStopTime(std::string const& time_str)
2963 {
2964
2965   struct tm* lctime;
2966   time_t current_time = time(nullptr);
2967   lctime = gmtime(&current_time);
2968   int gm_hour = lctime->tm_hour;
2969   time_t gm_time = mktime(lctime);
2970   lctime = localtime(&current_time);
2971   int local_hour = lctime->tm_hour;
2972
2973   int tzone_offset = local_hour - gm_hour;
2974   if (gm_time > current_time && gm_hour < local_hour) {
2975     // this means gm_time is on the next day
2976     tzone_offset -= 24;
2977   } else if (gm_time < current_time && gm_hour > local_hour) {
2978     // this means gm_time is on the previous day
2979     tzone_offset += 24;
2980   }
2981
2982   tzone_offset *= 100;
2983   char buf[1024];
2984   snprintf(buf, sizeof(buf), "%d%02d%02d %s %+05i", lctime->tm_year + 1900,
2985            lctime->tm_mon + 1, lctime->tm_mday, time_str.c_str(),
2986            tzone_offset);
2987
2988   time_t stop_time = curl_getdate(buf, &current_time);
2989   if (stop_time == -1) {
2990     this->Impl->StopTime = std::chrono::system_clock::time_point();
2991     return;
2992   }
2993   this->Impl->StopTime = std::chrono::system_clock::from_time_t(stop_time);
2994
2995   if (stop_time < current_time) {
2996     this->Impl->StopTime += std::chrono::hours(24);
2997   }
2998 }
2999
3000 std::string cmCTest::GetScheduleType() const
3001 {
3002   return this->Impl->ScheduleType;
3003 }
3004
3005 void cmCTest::SetScheduleType(std::string const& type)
3006 {
3007   this->Impl->ScheduleType = type;
3008 }
3009
3010 int cmCTest::ReadCustomConfigurationFileTree(const std::string& dir,
3011                                              cmMakefile* mf)
3012 {
3013   bool found = false;
3014   cmCTestLog(this, DEBUG,
3015              "* Read custom CTest configuration directory: " << dir
3016                                                              << std::endl);
3017
3018   std::string fname = cmStrCat(dir, "/CTestCustom.cmake");
3019   cmCTestLog(this, DEBUG, "* Check for file: " << fname << std::endl);
3020   if (cmSystemTools::FileExists(fname)) {
3021     cmCTestLog(this, DEBUG,
3022                "* Read custom CTest configuration file: " << fname
3023                                                           << std::endl);
3024     bool erroroc = cmSystemTools::GetErrorOccurredFlag();
3025     cmSystemTools::ResetErrorOccurredFlag();
3026
3027     if (!mf->ReadListFile(fname) || cmSystemTools::GetErrorOccurredFlag()) {
3028       cmCTestLog(this, ERROR_MESSAGE,
3029                  "Problem reading custom configuration: " << fname
3030                                                           << std::endl);
3031     }
3032     found = true;
3033     if (erroroc) {
3034       cmSystemTools::SetErrorOccurred();
3035     }
3036   }
3037
3038   std::string rexpr = cmStrCat(dir, "/CTestCustom.ctest");
3039   cmCTestLog(this, DEBUG, "* Check for file: " << rexpr << std::endl);
3040   if (!found && cmSystemTools::FileExists(rexpr)) {
3041     cmsys::Glob gl;
3042     gl.RecurseOn();
3043     gl.FindFiles(rexpr);
3044     std::vector<std::string>& files = gl.GetFiles();
3045     for (const std::string& file : files) {
3046       cmCTestLog(this, DEBUG,
3047                  "* Read custom CTest configuration file: " << file
3048                                                             << std::endl);
3049       if (!mf->ReadListFile(file) || cmSystemTools::GetErrorOccurredFlag()) {
3050         cmCTestLog(this, ERROR_MESSAGE,
3051                    "Problem reading custom configuration: " << file
3052                                                             << std::endl);
3053       }
3054     }
3055     found = true;
3056   }
3057
3058   if (found) {
3059     for (auto& handler : this->Impl->GetNamedTestingHandlers()) {
3060       cmCTestLog(this, DEBUG,
3061                  "* Read custom CTest configuration vectors for handler: "
3062                    << handler.first << " (" << handler.second << ")"
3063                    << std::endl);
3064       handler.second->PopulateCustomVectors(mf);
3065     }
3066   }
3067
3068   return 1;
3069 }
3070
3071 void cmCTest::PopulateCustomVector(cmMakefile* mf, const std::string& def,
3072                                    std::vector<std::string>& vec)
3073 {
3074   cmValue dval = mf->GetDefinition(def);
3075   if (!dval) {
3076     return;
3077   }
3078   cmCTestLog(this, DEBUG, "PopulateCustomVector: " << def << std::endl);
3079
3080   vec.clear();
3081   cmExpandList(*dval, vec);
3082
3083   for (std::string const& it : vec) {
3084     cmCTestLog(this, DEBUG, "  -- " << it << std::endl);
3085   }
3086 }
3087
3088 void cmCTest::PopulateCustomInteger(cmMakefile* mf, const std::string& def,
3089                                     int& val)
3090 {
3091   cmValue dval = mf->GetDefinition(def);
3092   if (!dval) {
3093     return;
3094   }
3095   val = atoi(dval->c_str());
3096 }
3097
3098 std::string cmCTest::GetShortPathToFile(const std::string& cfname)
3099 {
3100   const std::string& sourceDir = cmSystemTools::CollapseFullPath(
3101     this->GetCTestConfiguration("SourceDirectory"));
3102   const std::string& buildDir = cmSystemTools::CollapseFullPath(
3103     this->GetCTestConfiguration("BuildDirectory"));
3104   std::string fname = cmSystemTools::CollapseFullPath(cfname);
3105
3106   // Find relative paths to both directories
3107   std::string srcRelpath = cmSystemTools::RelativePath(sourceDir, fname);
3108   std::string bldRelpath = cmSystemTools::RelativePath(buildDir, fname);
3109
3110   // If any contains "." it is not parent directory
3111   bool inSrc = srcRelpath.find("..") == std::string::npos;
3112   bool inBld = bldRelpath.find("..") == std::string::npos;
3113   // TODO: Handle files with .. in their name
3114
3115   std::string* res = nullptr;
3116
3117   if (inSrc && inBld) {
3118     // If both have relative path with no dots, pick the shorter one
3119     if (srcRelpath.size() < bldRelpath.size()) {
3120       res = &srcRelpath;
3121     } else {
3122       res = &bldRelpath;
3123     }
3124   } else if (inSrc) {
3125     res = &srcRelpath;
3126   } else if (inBld) {
3127     res = &bldRelpath;
3128   }
3129
3130   std::string path;
3131
3132   if (!res) {
3133     path = fname;
3134   } else {
3135     cmSystemTools::ConvertToUnixSlashes(*res);
3136
3137     path = "./" + *res;
3138     if (path.back() == '/') {
3139       path.resize(path.size() - 1);
3140     }
3141   }
3142
3143   cmsys::SystemTools::ReplaceString(path, ":", "_");
3144   cmsys::SystemTools::ReplaceString(path, " ", "_");
3145   return path;
3146 }
3147
3148 std::string cmCTest::GetCTestConfiguration(const std::string& name)
3149 {
3150   if (this->Impl->CTestConfigurationOverwrites.find(name) !=
3151       this->Impl->CTestConfigurationOverwrites.end()) {
3152     return this->Impl->CTestConfigurationOverwrites[name];
3153   }
3154   return this->Impl->CTestConfiguration[name];
3155 }
3156
3157 void cmCTest::EmptyCTestConfiguration()
3158 {
3159   this->Impl->CTestConfiguration.clear();
3160 }
3161
3162 void cmCTest::SetCTestConfiguration(const char* name, const std::string& value,
3163                                     bool suppress)
3164 {
3165   cmCTestOptionalLog(this, HANDLER_VERBOSE_OUTPUT,
3166                      "SetCTestConfiguration:" << name << ":" << value << "\n",
3167                      suppress);
3168
3169   if (!name) {
3170     return;
3171   }
3172   if (value.empty()) {
3173     this->Impl->CTestConfiguration.erase(name);
3174     return;
3175   }
3176   this->Impl->CTestConfiguration[name] = value;
3177 }
3178
3179 std::string cmCTest::GetSubmitURL()
3180 {
3181   std::string url = this->GetCTestConfiguration("SubmitURL");
3182   if (url.empty()) {
3183     std::string method = this->GetCTestConfiguration("DropMethod");
3184     std::string user = this->GetCTestConfiguration("DropSiteUser");
3185     std::string password = this->GetCTestConfiguration("DropSitePassword");
3186     std::string site = this->GetCTestConfiguration("DropSite");
3187     std::string location = this->GetCTestConfiguration("DropLocation");
3188
3189     url = cmStrCat(method.empty() ? "http" : method, "://"_s);
3190     if (!user.empty()) {
3191       url += user;
3192       if (!password.empty()) {
3193         url += ':';
3194         url += password;
3195       }
3196       url += '@';
3197     }
3198     url += site;
3199     url += location;
3200   }
3201   return url;
3202 }
3203
3204 std::string cmCTest::GetCurrentTag()
3205 {
3206   return this->Impl->CurrentTag;
3207 }
3208
3209 std::string cmCTest::GetBinaryDir()
3210 {
3211   return this->Impl->BinaryDir;
3212 }
3213
3214 std::string const& cmCTest::GetConfigType()
3215 {
3216   return this->Impl->ConfigType;
3217 }
3218
3219 cmDuration cmCTest::GetTimeOut() const
3220 {
3221   return this->Impl->TimeOut;
3222 }
3223
3224 void cmCTest::SetTimeOut(cmDuration t)
3225 {
3226   this->Impl->TimeOut = t;
3227 }
3228
3229 cmDuration cmCTest::GetGlobalTimeout() const
3230 {
3231   return this->Impl->GlobalTimeout;
3232 }
3233
3234 bool cmCTest::GetShowOnly()
3235 {
3236   return this->Impl->ShowOnly;
3237 }
3238
3239 bool cmCTest::GetOutputAsJson()
3240 {
3241   return this->Impl->OutputAsJson;
3242 }
3243
3244 int cmCTest::GetOutputAsJsonVersion()
3245 {
3246   return this->Impl->OutputAsJsonVersion;
3247 }
3248
3249 bool cmCTest::ShouldUseHTTP10() const
3250 {
3251   return this->Impl->UseHTTP10;
3252 }
3253
3254 bool cmCTest::ShouldPrintLabels() const
3255 {
3256   return this->Impl->PrintLabels;
3257 }
3258
3259 int cmCTest::GetMaxTestNameWidth() const
3260 {
3261   return this->Impl->MaxTestNameWidth;
3262 }
3263
3264 void cmCTest::SetMaxTestNameWidth(int w)
3265 {
3266   this->Impl->MaxTestNameWidth = w;
3267 }
3268
3269 void cmCTest::SetProduceXML(bool v)
3270 {
3271   this->Impl->ProduceXML = v;
3272 }
3273
3274 bool cmCTest::GetProduceXML()
3275 {
3276   return this->Impl->ProduceXML;
3277 }
3278
3279 std::vector<std::string>& cmCTest::GetInitialCommandLineArguments()
3280 {
3281   return this->Impl->InitialCommandLineArguments;
3282 }
3283
3284 const char* cmCTest::GetSpecificGroup()
3285 {
3286   if (this->Impl->SpecificGroup.empty()) {
3287     return nullptr;
3288   }
3289   return this->Impl->SpecificGroup.c_str();
3290 }
3291
3292 void cmCTest::SetSpecificGroup(const char* group)
3293 {
3294   if (!group) {
3295     this->Impl->SpecificGroup.clear();
3296     return;
3297   }
3298   this->Impl->SpecificGroup = group;
3299 }
3300
3301 void cmCTest::SetFailover(bool failover)
3302 {
3303   this->Impl->Failover = failover;
3304 }
3305
3306 bool cmCTest::GetFailover() const
3307 {
3308   return this->Impl->Failover;
3309 }
3310
3311 bool cmCTest::GetTestProgressOutput() const
3312 {
3313   return this->Impl->TestProgressOutput && !GetExtraVerbose();
3314 }
3315
3316 bool cmCTest::GetVerbose() const
3317 {
3318   return this->Impl->Verbose;
3319 }
3320
3321 bool cmCTest::GetExtraVerbose() const
3322 {
3323   return this->Impl->ExtraVerbose;
3324 }
3325
3326 void cmCTest::SetStreams(std::ostream* out, std::ostream* err)
3327 {
3328   this->Impl->StreamOut = out;
3329   this->Impl->StreamErr = err;
3330 }
3331
3332 bool cmCTest::GetLabelSummary() const
3333 {
3334   return this->Impl->LabelSummary;
3335 }
3336
3337 bool cmCTest::GetSubprojectSummary() const
3338 {
3339   return this->Impl->SubprojectSummary;
3340 }
3341
3342 bool cmCTest::GetOutputTestOutputOnTestFailure() const
3343 {
3344   return this->Impl->OutputTestOutputOnTestFailure;
3345 }
3346
3347 const std::map<std::string, std::string>& cmCTest::GetDefinitions() const
3348 {
3349   return this->Impl->Definitions;
3350 }
3351
3352 int cmCTest::GetRepeatCount() const
3353 {
3354   return this->Impl->RepeatCount;
3355 }
3356
3357 cmCTest::Repeat cmCTest::GetRepeatMode() const
3358 {
3359   return this->Impl->RepeatMode;
3360 }
3361
3362 cmCTest::NoTests cmCTest::GetNoTestsMode() const
3363 {
3364   return this->Impl->NoTestsMode;
3365 }
3366
3367 void cmCTest::SetBuildID(const std::string& id)
3368 {
3369   this->Impl->BuildID = id;
3370 }
3371
3372 std::string cmCTest::GetBuildID() const
3373 {
3374   return this->Impl->BuildID;
3375 }
3376
3377 void cmCTest::AddSubmitFile(Part part, const std::string& name)
3378 {
3379   this->Impl->Parts[part].SubmitFiles.emplace_back(name);
3380 }
3381
3382 std::vector<std::string> const& cmCTest::GetSubmitFiles(Part part) const
3383 {
3384   return this->Impl->Parts[part].SubmitFiles;
3385 }
3386
3387 void cmCTest::ClearSubmitFiles(Part part)
3388 {
3389   this->Impl->Parts[part].SubmitFiles.clear();
3390 }
3391
3392 void cmCTest::SetSuppressUpdatingCTestConfiguration(bool val)
3393 {
3394   this->Impl->SuppressUpdatingCTestConfiguration = val;
3395 }
3396
3397 void cmCTest::AddCTestConfigurationOverwrite(const std::string& overStr)
3398 {
3399   size_t epos = overStr.find('=');
3400   if (epos == std::string::npos) {
3401     cmCTestLog(this, ERROR_MESSAGE,
3402                "CTest configuration overwrite specified in the wrong format."
3403                  << std::endl
3404                  << "Valid format is: --overwrite key=value" << std::endl
3405                  << "The specified was: --overwrite " << overStr << std::endl);
3406     return;
3407   }
3408   std::string key = overStr.substr(0, epos);
3409   std::string value = overStr.substr(epos + 1);
3410   this->Impl->CTestConfigurationOverwrites[key] = value;
3411 }
3412
3413 void cmCTest::SetConfigType(const std::string& ct)
3414 {
3415   this->Impl->ConfigType = ct;
3416   cmSystemTools::ReplaceString(this->Impl->ConfigType, ".\\", "");
3417   std::string confTypeEnv = "CMAKE_CONFIG_TYPE=" + this->Impl->ConfigType;
3418   cmSystemTools::PutEnv(confTypeEnv);
3419 }
3420
3421 bool cmCTest::SetCTestConfigurationFromCMakeVariable(
3422   cmMakefile* mf, const char* dconfig, const std::string& cmake_var,
3423   bool suppress)
3424 {
3425   cmValue ctvar = mf->GetDefinition(cmake_var);
3426   if (!ctvar) {
3427     return false;
3428   }
3429   cmCTestOptionalLog(this, HANDLER_VERBOSE_OUTPUT,
3430                      "SetCTestConfigurationFromCMakeVariable:"
3431                        << dconfig << ":" << cmake_var << std::endl,
3432                      suppress);
3433   this->SetCTestConfiguration(dconfig, *ctvar, suppress);
3434   return true;
3435 }
3436
3437 bool cmCTest::RunCommand(std::vector<std::string> const& args,
3438                          std::string* stdOut, std::string* stdErr, int* retVal,
3439                          const char* dir, cmDuration timeout,
3440                          Encoding encoding)
3441 {
3442   std::vector<const char*> argv;
3443   argv.reserve(args.size() + 1);
3444   for (std::string const& a : args) {
3445     argv.push_back(a.c_str());
3446   }
3447   argv.push_back(nullptr);
3448
3449   stdOut->clear();
3450   stdErr->clear();
3451
3452   cmsysProcess* cp = cmsysProcess_New();
3453   cmsysProcess_SetCommand(cp, argv.data());
3454   cmsysProcess_SetWorkingDirectory(cp, dir);
3455   if (cmSystemTools::GetRunCommandHideConsole()) {
3456     cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
3457   }
3458   cmsysProcess_SetTimeout(cp, timeout.count());
3459   cmsysProcess_Execute(cp);
3460
3461   std::vector<char> tempOutput;
3462   std::vector<char> tempError;
3463   char* data;
3464   int length;
3465   cmProcessOutput processOutput(encoding);
3466   std::string strdata;
3467   int res;
3468   bool done = false;
3469   while (!done) {
3470     res = cmsysProcess_WaitForData(cp, &data, &length, nullptr);
3471     switch (res) {
3472       case cmsysProcess_Pipe_STDOUT:
3473         cm::append(tempOutput, data, data + length);
3474         break;
3475       case cmsysProcess_Pipe_STDERR:
3476         cm::append(tempError, data, data + length);
3477         break;
3478       default:
3479         done = true;
3480     }
3481     if ((res == cmsysProcess_Pipe_STDOUT || res == cmsysProcess_Pipe_STDERR) &&
3482         this->Impl->ExtraVerbose) {
3483       processOutput.DecodeText(data, length, strdata);
3484       cmSystemTools::Stdout(strdata);
3485     }
3486   }
3487   if (this->Impl->ExtraVerbose) {
3488     processOutput.DecodeText(std::string(), strdata);
3489     if (!strdata.empty()) {
3490       cmSystemTools::Stdout(strdata);
3491     }
3492   }
3493
3494   cmsysProcess_WaitForExit(cp, nullptr);
3495   if (!tempOutput.empty()) {
3496     processOutput.DecodeText(tempOutput, tempOutput);
3497     stdOut->append(tempOutput.data(), tempOutput.size());
3498   }
3499   if (!tempError.empty()) {
3500     processOutput.DecodeText(tempError, tempError);
3501     stdErr->append(tempError.data(), tempError.size());
3502   }
3503
3504   bool result = true;
3505   if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exited) {
3506     if (retVal) {
3507       *retVal = cmsysProcess_GetExitValue(cp);
3508     } else {
3509       if (cmsysProcess_GetExitValue(cp) != 0) {
3510         result = false;
3511       }
3512     }
3513   } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exception) {
3514     const char* exception_str = cmsysProcess_GetExceptionString(cp);
3515     cmCTestLog(this, ERROR_MESSAGE, exception_str << std::endl);
3516     stdErr->append(exception_str, strlen(exception_str));
3517     result = false;
3518   } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Error) {
3519     const char* error_str = cmsysProcess_GetErrorString(cp);
3520     cmCTestLog(this, ERROR_MESSAGE, error_str << std::endl);
3521     stdErr->append(error_str, strlen(error_str));
3522     result = false;
3523   } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Expired) {
3524     const char* error_str = "Process terminated due to timeout\n";
3525     cmCTestLog(this, ERROR_MESSAGE, error_str << std::endl);
3526     stdErr->append(error_str, strlen(error_str));
3527     result = false;
3528   }
3529
3530   cmsysProcess_Delete(cp);
3531   return result;
3532 }
3533
3534 void cmCTest::SetOutputLogFileName(const std::string& name)
3535 {
3536   if (!name.empty()) {
3537     this->Impl->OutputLogFile = cm::make_unique<cmGeneratedFileStream>(name);
3538   } else {
3539     this->Impl->OutputLogFile.reset();
3540   }
3541 }
3542
3543 void cmCTest::SetOutputJUnitFileName(const std::string& name)
3544 {
3545   this->Impl->TestHandler.SetJUnitXMLFileName(name);
3546   // Turn test output compression off.
3547   // This makes it easier to include test output in the resulting
3548   // JUnit XML report.
3549   this->Impl->CompressTestOutput = false;
3550 }
3551
3552 static const char* cmCTestStringLogType[] = { "DEBUG",
3553                                               "OUTPUT",
3554                                               "HANDLER_OUTPUT",
3555                                               "HANDLER_PROGRESS_OUTPUT",
3556                                               "HANDLER_TEST_PROGRESS_OUTPUT",
3557                                               "HANDLER_VERBOSE_OUTPUT",
3558                                               "WARNING",
3559                                               "ERROR_MESSAGE",
3560                                               nullptr };
3561
3562 #define cmCTestLogOutputFileLine(stream)                                      \
3563   do {                                                                        \
3564     if (this->Impl->ShowLineNumbers) {                                        \
3565       (stream) << std::endl << file << ":" << line << " ";                    \
3566     }                                                                         \
3567   } while (false)
3568
3569 void cmCTest::Log(int logType, const char* file, int line, const char* msg,
3570                   bool suppress)
3571 {
3572   if (!msg || !*msg) {
3573     return;
3574   }
3575   if (suppress && logType != cmCTest::ERROR_MESSAGE) {
3576     return;
3577   }
3578   if (logType == cmCTest::HANDLER_PROGRESS_OUTPUT &&
3579       (this->Impl->Debug || this->Impl->ExtraVerbose)) {
3580     return;
3581   }
3582   if (this->Impl->OutputLogFile) {
3583     bool display = true;
3584     if (logType == cmCTest::DEBUG && !this->Impl->Debug) {
3585       display = false;
3586     }
3587     if (logType == cmCTest::HANDLER_VERBOSE_OUTPUT && !this->Impl->Debug &&
3588         !this->Impl->ExtraVerbose) {
3589       display = false;
3590     }
3591     if (display) {
3592       cmCTestLogOutputFileLine(*this->Impl->OutputLogFile);
3593       if (logType != this->Impl->OutputLogFileLastTag) {
3594         *this->Impl->OutputLogFile << "[";
3595         if (logType >= OTHER || logType < 0) {
3596           *this->Impl->OutputLogFile << "OTHER";
3597         } else {
3598           *this->Impl->OutputLogFile << cmCTestStringLogType[logType];
3599         }
3600         *this->Impl->OutputLogFile << "] " << std::endl;
3601       }
3602       *this->Impl->OutputLogFile << msg << std::flush;
3603       if (logType != this->Impl->OutputLogFileLastTag) {
3604         *this->Impl->OutputLogFile << std::endl;
3605         this->Impl->OutputLogFileLastTag = logType;
3606       }
3607     }
3608   }
3609   if (!this->Impl->Quiet) {
3610     std::ostream& out = *this->Impl->StreamOut;
3611     std::ostream& err = *this->Impl->StreamErr;
3612
3613     if (logType == HANDLER_TEST_PROGRESS_OUTPUT) {
3614       if (this->Impl->TestProgressOutput) {
3615         cmCTestLogOutputFileLine(out);
3616         if (this->Impl->FlushTestProgressLine) {
3617           printf("\r");
3618           this->Impl->FlushTestProgressLine = false;
3619           out.flush();
3620         }
3621
3622         std::string msg_str{ msg };
3623         auto const lineBreakIt = msg_str.find('\n');
3624         if (lineBreakIt != std::string::npos) {
3625           this->Impl->FlushTestProgressLine = true;
3626           msg_str.erase(std::remove(msg_str.begin(), msg_str.end(), '\n'),
3627                         msg_str.end());
3628         }
3629
3630         out << msg_str;
3631 #ifndef _WIN32
3632         printf("\x1B[K"); // move caret to end
3633 #endif
3634         out.flush();
3635         return;
3636       }
3637       logType = HANDLER_OUTPUT;
3638     }
3639
3640     switch (logType) {
3641       case DEBUG:
3642         if (this->Impl->Debug) {
3643           cmCTestLogOutputFileLine(out);
3644           out << msg;
3645           out.flush();
3646         }
3647         break;
3648       case OUTPUT:
3649       case HANDLER_OUTPUT:
3650         if (this->Impl->Debug || this->Impl->Verbose) {
3651           cmCTestLogOutputFileLine(out);
3652           out << msg;
3653           out.flush();
3654         }
3655         break;
3656       case HANDLER_VERBOSE_OUTPUT:
3657         if (this->Impl->Debug || this->Impl->ExtraVerbose) {
3658           cmCTestLogOutputFileLine(out);
3659           out << msg;
3660           out.flush();
3661         }
3662         break;
3663       case WARNING:
3664         cmCTestLogOutputFileLine(err);
3665         err << msg;
3666         err.flush();
3667         break;
3668       case ERROR_MESSAGE:
3669         cmCTestLogOutputFileLine(err);
3670         err << msg;
3671         err.flush();
3672         cmSystemTools::SetErrorOccurred();
3673         break;
3674       default:
3675         cmCTestLogOutputFileLine(out);
3676         out << msg;
3677         out.flush();
3678     }
3679   }
3680 }
3681
3682 std::string cmCTest::GetColorCode(Color color) const
3683 {
3684   if (this->Impl->OutputColorCode) {
3685 #if defined(_WIN32)
3686     // Not supported on Windows
3687     static_cast<void>(color);
3688 #else
3689     return "\033[0;" + std::to_string(static_cast<int>(color)) + "m";
3690 #endif
3691   }
3692
3693   return "";
3694 }
3695
3696 cmDuration cmCTest::GetRemainingTimeAllowed()
3697 {
3698   return this->GetScriptHandler()->GetRemainingTimeAllowed();
3699 }
3700
3701 cmDuration cmCTest::MaxDuration()
3702 {
3703   return cmDuration(1.0e7);
3704 }
3705
3706 void cmCTest::SetRunCurrentScript(bool value)
3707 {
3708   this->GetScriptHandler()->SetRunCurrentScript(value);
3709 }
3710
3711 void cmCTest::OutputTestErrors(std::vector<char> const& process_output)
3712 {
3713   std::string test_outputs("\n*** Test Failed:\n");
3714   if (!process_output.empty()) {
3715     test_outputs.append(process_output.data(), process_output.size());
3716   }
3717   cmCTestLog(this, HANDLER_OUTPUT, test_outputs << std::endl);
3718 }
3719
3720 bool cmCTest::CompressString(std::string& str)
3721 {
3722   int ret;
3723   z_stream strm;
3724
3725   strm.zalloc = Z_NULL;
3726   strm.zfree = Z_NULL;
3727   strm.opaque = Z_NULL;
3728   ret = deflateInit(&strm, -1); // default compression level
3729   if (ret != Z_OK) {
3730     return false;
3731   }
3732
3733   unsigned char* in =
3734     reinterpret_cast<unsigned char*>(const_cast<char*>(str.c_str()));
3735   // zlib makes the guarantee that this is the maximum output size
3736   int outSize =
3737     static_cast<int>(static_cast<double>(str.size()) * 1.001 + 13.0);
3738   std::vector<unsigned char> out(outSize);
3739
3740   strm.avail_in = static_cast<uInt>(str.size());
3741   strm.next_in = in;
3742   strm.avail_out = outSize;
3743   strm.next_out = out.data();
3744   ret = deflate(&strm, Z_FINISH);
3745
3746   if (ret != Z_STREAM_END) {
3747     cmCTestLog(this, ERROR_MESSAGE,
3748                "Error during gzip compression." << std::endl);
3749     return false;
3750   }
3751
3752   (void)deflateEnd(&strm);
3753
3754   // Now base64 encode the resulting binary string
3755   std::vector<unsigned char> base64EncodedBuffer((outSize * 3) / 2);
3756
3757   size_t rlen = cmsysBase64_Encode(out.data(), strm.total_out,
3758                                    base64EncodedBuffer.data(), 1);
3759
3760   str.assign(reinterpret_cast<char*>(base64EncodedBuffer.data()), rlen);
3761
3762   return true;
3763 }