CMake/Source/cmCTest.cxx

2861 lines
90 KiB
C++
Raw Normal View History

Simplify CMake per-source license notices Per-source copyright/license notice headers that spell out copyright holder names and years are hard to maintain and often out-of-date or plain wrong. Precise contributor information is already maintained automatically by the version control tool. Ultimately it is the receiver of a file who is responsible for determining its licensing status, and per-source notices are merely a convenience. Therefore it is simpler and more accurate for each source to have a generic notice of the license name and references to more detailed information on copyright holders and full license terms. Our `Copyright.txt` file now contains a list of Contributors whose names appeared source-level copyright notices. It also references version control history for more precise information. Therefore we no longer need to spell out the list of Contributors in each source file notice. Replace CMake per-source copyright/license notice headers with a short description of the license and links to `Copyright.txt` and online information available from "https://cmake.org/licensing". The online URL also handles cases of modules being copied out of our source into other projects, so we can drop our notices about replacing links with full license text. Run the `Utilities/Scripts/filter-notices.bash` script to perform the majority of the replacements mechanically. Manually fix up shebang lines and trailing newlines in a few files. Manually update the notices in a few files that the script does not handle.
2016-09-27 19:01:08 +00:00
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
2002-12-17 02:19:21 +00:00
#include "cmCTest.h"
#include "cm_curl.h"
#include "cm_zlib.h"
#include "cmsys/Base64.h"
#include "cmsys/Directory.hxx"
#include "cmsys/FStream.hxx"
#include "cmsys/Glob.hxx"
#include "cmsys/Process.h"
#include "cmsys/String.hxx"
#include "cmsys/SystemInformation.hxx"
#include <algorithm>
#include <chrono>
#include <ctype.h>
#include <iostream>
#include <map>
#include <memory> // IWYU pragma: keep
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <time.h>
#include <utility>
#include <vector>
#include "cmAlgorithms.h"
#include "cmCTestBuildAndTestHandler.h"
#include "cmCTestBuildHandler.h"
2005-01-27 20:54:47 +00:00
#include "cmCTestConfigureHandler.h"
2004-09-09 12:41:05 +00:00
#include "cmCTestCoverageHandler.h"
#include "cmCTestGenericHandler.h"
2005-01-27 20:54:47 +00:00
#include "cmCTestMemCheckHandler.h"
2004-09-07 14:46:12 +00:00
#include "cmCTestScriptHandler.h"
#include "cmCTestStartCommand.h"
2011-02-18 17:11:51 +00:00
#include "cmCTestSubmitHandler.h"
2004-09-09 12:41:05 +00:00
#include "cmCTestTestHandler.h"
2004-09-07 14:46:12 +00:00
#include "cmCTestUpdateHandler.h"
2011-02-18 17:11:51 +00:00
#include "cmCTestUploadHandler.h"
#include "cmCurl.h"
#include "cmDynamicLoader.h"
#include "cmGeneratedFileStream.h"
#include "cmGlobalGenerator.h"
#include "cmMakefile.h"
#include "cmProcessOutput.h"
#include "cmState.h"
#include "cmStateSnapshot.h"
#include "cmStateTypes.h"
#include "cmSystemTools.h"
#include "cmVersion.h"
#include "cmVersionConfig.h"
#include "cmXMLWriter.h"
#include "cmake.h"
2003-01-07 04:13:15 +00:00
#if defined(__BEOS__) || defined(__HAIKU__)
#include <be/kernel/OS.h> /* disable_debugger() API. */
2006-12-04 22:26:41 +00:00
#endif
#define DEBUGOUT \
std::cout << __LINE__ << " "; \
std::cout
#define DEBUGERR \
std::cerr << __LINE__ << " "; \
std::cerr
struct tm* cmCTest::GetNightlyTime(std::string const& str, bool tomorrowtag)
2003-02-28 17:42:50 +00:00
{
struct tm* lctime;
2017-08-22 21:42:36 +00:00
time_t tctime = time(nullptr);
lctime = gmtime(&tctime);
char buf[1024];
// add todays year day and month to the time in str because
// curl_getdate no longer assumes the day is today
sprintf(buf, "%d%02d%02d %s", lctime->tm_year + 1900, lctime->tm_mon + 1,
lctime->tm_mday, str.c_str());
cmCTestLog(this, OUTPUT, "Determine Nightly Start Time"
<< std::endl
<< " Specified time: " << str << std::endl);
// Convert the nightly start time to seconds. Since we are
// providing only a time and a timezone, the current date of
// the local machine is assumed. Consequently, nightlySeconds
// is the time at which the nightly dashboard was opened or
// will be opened on the date of the current client machine.
// As such, this time may be in the past or in the future.
time_t ntime = curl_getdate(buf, &tctime);
cmCTestLog(this, DEBUG, " Get curl time: " << ntime << std::endl);
2017-08-22 21:42:36 +00:00
tctime = time(nullptr);
cmCTestLog(this, DEBUG, " Get the current time: " << tctime << std::endl);
const int dayLength = 24 * 60 * 60;
cmCTestLog(this, DEBUG, "Seconds: " << tctime << std::endl);
while (ntime > tctime) {
2003-02-28 17:42:50 +00:00
// If nightlySeconds is in the past, this is the current
// open dashboard, then return nightlySeconds. If
// nightlySeconds is in the future, this is the next
// dashboard to be opened, so subtract 24 hours to get the
// time of the current open dashboard
ntime -= dayLength;
cmCTestLog(this, DEBUG, "Pick yesterday" << std::endl);
2006-03-09 16:57:43 +00:00
cmCTestLog(this, DEBUG, " Future time, subtract day: " << ntime
<< std::endl);
}
while (tctime > (ntime + dayLength)) {
ntime += dayLength;
cmCTestLog(this, DEBUG, " Past time, add day: " << ntime << std::endl);
}
cmCTestLog(this, DEBUG, "nightlySeconds: " << ntime << std::endl);
cmCTestLog(this, DEBUG, " Current time: " << tctime << " Nightly time: "
<< ntime << std::endl);
if (tomorrowtag) {
cmCTestLog(this, OUTPUT, " Use future tag, Add a day" << std::endl);
ntime += dayLength;
}
2003-02-28 17:42:50 +00:00
lctime = gmtime(&ntime);
return lctime;
}
2004-09-09 12:41:05 +00:00
std::string cmCTest::CleanString(const std::string& str)
2002-12-17 02:19:21 +00:00
{
std::string::size_type spos = str.find_first_not_of(" \n\t\r\f\v");
std::string::size_type epos = str.find_last_not_of(" \n\t\r\f\v");
2017-05-30 19:37:46 +00:00
if (spos == std::string::npos) {
2002-12-17 02:19:21 +00:00
return std::string();
}
2017-05-30 19:37:46 +00:00
if (epos != std::string::npos) {
2002-12-17 02:19:21 +00:00
epos = epos - spos + 1;
}
2002-12-17 02:19:21 +00:00
return str.substr(spos, epos);
}
std::string cmCTest::CurrentTime()
2002-12-17 02:19:21 +00:00
{
2017-08-22 21:42:36 +00:00
time_t currenttime = time(nullptr);
2003-01-12 02:47:12 +00:00
struct tm* t = localtime(&currenttime);
// return ::CleanString(ctime(&currenttime));
2003-01-12 02:47:12 +00:00
char current_time[1024];
if (this->ShortDateFormat) {
2004-08-26 13:45:20 +00:00
strftime(current_time, 1000, "%b %d %H:%M %Z", t);
} else {
2004-08-26 13:45:20 +00:00
strftime(current_time, 1000, "%a %b %d %H:%M:%S %Z %Y", t);
}
cmCTestLog(this, DEBUG, " Current_Time: " << current_time << std::endl);
return cmCTest::CleanString(current_time);
2002-12-17 02:19:21 +00:00
}
std::string cmCTest::GetCostDataFile()
{
std::string fname = this->GetCTestConfiguration("CostDataFile");
if (fname.empty()) {
fname = this->GetBinaryDir() + "/Testing/Temporary/CTestCostData.txt";
}
return fname;
}
#ifdef CMAKE_BUILD_WITH_CMAKE
static size_t HTTPResponseCallback(void* ptr, size_t size, size_t nmemb,
void* data)
{
2017-08-23 07:41:23 +00:00
int realsize = static_cast<int>(size * nmemb);
std::string* response = static_cast<std::string*>(data);
const char* chPtr = static_cast<char*>(ptr);
*response += chPtr;
return realsize;
}
int cmCTest::HTTPRequest(std::string url, HTTPMethod method,
std::string& response, std::string const& fields,
std::string const& putFile, int timeout)
{
CURL* curl;
FILE* file;
::curl_global_init(CURL_GLOBAL_ALL);
curl = ::curl_easy_init();
cmCurlSetCAInfo(curl);
// set request options based on method
switch (method) {
case cmCTest::HTTP_POST:
::curl_easy_setopt(curl, CURLOPT_POST, 1);
::curl_easy_setopt(curl, CURLOPT_POSTFIELDS, fields.c_str());
break;
case cmCTest::HTTP_PUT:
if (!cmSystemTools::FileExists(putFile.c_str())) {
response = "Error: File ";
response += putFile + " does not exist.\n";
return -1;
}
::curl_easy_setopt(curl, CURLOPT_PUT, 1);
file = cmsys::SystemTools::Fopen(putFile, "rb");
::curl_easy_setopt(curl, CURLOPT_INFILE, file);
// fall through to append GET fields
CM_FALLTHROUGH;
case cmCTest::HTTP_GET:
if (!fields.empty()) {
url += "?" + fields;
}
break;
}
::curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
::curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
::curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
// set response options
::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HTTPResponseCallback);
2017-08-23 07:41:23 +00:00
::curl_easy_setopt(curl, CURLOPT_FILE, &response);
::curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
CURLcode res = ::curl_easy_perform(curl);
::curl_easy_cleanup(curl);
::curl_global_cleanup();
return static_cast<int>(res);
}
#endif
2003-04-02 14:19:45 +00:00
std::string cmCTest::MakeURLSafe(const std::string& str)
{
std::ostringstream ost;
2003-04-02 14:19:45 +00:00
char buffer[10];
for (unsigned char ch : str) {
if ((ch > 126 || ch < 32 || ch == '&' || ch == '%' || ch == '+' ||
ch == '=' || ch == '@') &&
ch != 9) {
2017-08-23 07:41:23 +00:00
sprintf(buffer, "%02x;", static_cast<unsigned int>(ch));
2003-04-02 14:19:45 +00:00
ost << buffer;
} else {
2003-04-02 14:19:45 +00:00
ost << ch;
}
}
2003-04-02 14:19:45 +00:00
return ost.str();
}
std::string cmCTest::DecodeURL(const std::string& in)
{
std::string out;
for (const char* c = in.c_str(); *c; ++c) {
if (*c == '%' && isxdigit(*(c + 1)) && isxdigit(*(c + 2))) {
char buf[3] = { *(c + 1), *(c + 2), 0 };
2017-08-22 21:42:36 +00:00
out.append(1, char(strtoul(buf, nullptr, 16)));
c += 2;
} else {
out.append(1, *c);
}
}
return out;
}
2006-03-09 16:57:43 +00:00
cmCTest::cmCTest()
{
this->LabelSummary = true;
this->SubprojectSummary = true;
this->ParallelLevel = 1;
this->ParallelLevelSetInCli = false;
this->TestLoad = 0;
this->SubmitIndex = 0;
this->Failover = false;
this->ForceNewCTestProcess = false;
this->TomorrowTag = false;
this->Verbose = false;
this->Debug = false;
this->ShowLineNumbers = false;
this->Quiet = false;
this->ExtraVerbose = false;
this->ProduceXML = false;
this->ShowOnly = false;
2006-03-15 16:02:08 +00:00
this->RunConfigurationScript = false;
this->UseHTTP10 = false;
this->PrintLabels = false;
this->CompressTestOutput = true;
this->TestModel = cmCTest::EXPERIMENTAL;
this->MaxTestNameWidth = 30;
this->InteractiveDebugMode = true;
this->TimeOut = cmDuration::zero();
this->GlobalTimeout = cmDuration::zero();
this->CompressXMLFiles = false;
this->ScheduleType.clear();
2017-08-22 21:42:36 +00:00
this->OutputLogFile = nullptr;
this->OutputLogFileLastTag = -1;
2006-03-15 16:02:08 +00:00
this->SuppressUpdatingCTestConfiguration = false;
this->DartVersion = 1;
this->DropSiteCDash = false;
this->OutputTestOutputOnTestFailure = false;
this->RepeatTests = 1; // default to run each test once
this->RepeatUntilFail = false;
std::string outOnFail;
if (cmSystemTools::GetEnv("CTEST_OUTPUT_ON_FAILURE", outOnFail)) {
this->OutputTestOutputOnTestFailure =
!cmSystemTools::IsOff(outOnFail.c_str());
}
this->InitStreams();
this->Parts[PartStart].SetName("Start");
this->Parts[PartUpdate].SetName("Update");
this->Parts[PartConfigure].SetName("Configure");
this->Parts[PartBuild].SetName("Build");
this->Parts[PartTest].SetName("Test");
this->Parts[PartCoverage].SetName("Coverage");
this->Parts[PartMemCheck].SetName("MemCheck");
this->Parts[PartSubmit].SetName("Submit");
this->Parts[PartNotes].SetName("Notes");
this->Parts[PartExtraFiles].SetName("ExtraFiles");
2011-02-18 17:11:51 +00:00
this->Parts[PartUpload].SetName("Upload");
// Fill the part name-to-id map.
for (Part p = PartStart; p != PartCount; p = Part(p + 1)) {
this->PartMap[cmSystemTools::LowerCase(this->Parts[p].GetName())] = p;
}
this->ShortDateFormat = true;
2004-09-06 16:46:35 +00:00
this->TestingHandlers["build"] = new cmCTestBuildHandler;
2006-03-15 16:02:08 +00:00
this->TestingHandlers["buildtest"] = new cmCTestBuildAndTestHandler;
this->TestingHandlers["coverage"] = new cmCTestCoverageHandler;
this->TestingHandlers["script"] = new cmCTestScriptHandler;
this->TestingHandlers["test"] = new cmCTestTestHandler;
this->TestingHandlers["update"] = new cmCTestUpdateHandler;
2006-03-15 16:02:08 +00:00
this->TestingHandlers["configure"] = new cmCTestConfigureHandler;
this->TestingHandlers["memcheck"] = new cmCTestMemCheckHandler;
this->TestingHandlers["submit"] = new cmCTestSubmitHandler;
this->TestingHandlers["upload"] = new cmCTestUploadHandler;
2005-01-27 20:54:47 +00:00
for (auto& handler : this->TestingHandlers) {
handler.second->SetCTestInstance(this);
}
// Make sure we can capture the build tool output.
cmSystemTools::EnableVSConsoleOutput();
2004-09-06 16:46:35 +00:00
}
2006-03-09 16:57:43 +00:00
cmCTest::~cmCTest()
{
cmDeleteAll(this->TestingHandlers);
2017-08-22 21:42:36 +00:00
this->SetOutputLogFileName(nullptr);
2002-12-17 02:19:21 +00:00
}
void cmCTest::SetParallelLevel(int level)
{
this->ParallelLevel = level < 1 ? 1 : level;
}
void cmCTest::SetTestLoad(unsigned long load)
{
this->TestLoad = load;
}
bool cmCTest::ShouldCompressTestOutput()
{
return this->CompressTestOutput;
}
cmCTest::Part cmCTest::GetPartFromName(const char* name)
{
// Look up by lower-case to make names case-insensitive.
std::string lower_name = cmSystemTools::LowerCase(name);
PartMapType::const_iterator i = this->PartMap.find(lower_name);
if (i != this->PartMap.end()) {
return i->second;
}
// The string does not name a valid part.
return PartCount;
}
int cmCTest::Initialize(const char* binary_dir, cmCTestStartCommand* command)
2002-12-17 02:19:21 +00:00
{
bool quiet = false;
if (command && command->ShouldBeQuiet()) {
quiet = true;
}
cmCTestOptionalLog(this, DEBUG, "Here: " << __LINE__ << std::endl, quiet);
if (!this->InteractiveDebugMode) {
this->BlockTestErrorDiagnostics();
} else {
cmSystemTools::PutEnv("CTEST_INTERACTIVE_DEBUG_MODE=1");
}
2006-03-09 16:57:43 +00:00
2006-03-15 16:02:08 +00:00
this->BinaryDir = binary_dir;
cmSystemTools::ConvertToUnixSlashes(this->BinaryDir);
this->UpdateCTestConfiguration();
cmCTestOptionalLog(this, DEBUG, "Here: " << __LINE__ << std::endl, quiet);
if (this->ProduceXML) {
cmCTestOptionalLog(this, DEBUG, "Here: " << __LINE__ << std::endl, quiet);
cmCTestOptionalLog(
this, OUTPUT, " Site: "
<< this->GetCTestConfiguration("Site") << std::endl
<< " Build name: "
<< cmCTest::SafeBuildIdField(this->GetCTestConfiguration("BuildName"))
<< std::endl,
quiet);
cmCTestOptionalLog(this, DEBUG, "Produce XML is on" << std::endl, quiet);
if (this->TestModel == cmCTest::NIGHTLY &&
this->GetCTestConfiguration("NightlyStartTime").empty()) {
cmCTestOptionalLog(
this, WARNING,
"WARNING: No nightly start time found please set in CTestConfig.cmake"
" or DartConfig.cmake"
<< std::endl,
quiet);
cmCTestOptionalLog(this, DEBUG, "Here: " << __LINE__ << std::endl,
quiet);
return 0;
}
}
cmake cm(cmake::RoleScript);
cm.SetHomeDirectory("");
cm.SetHomeOutputDirectory("");
cm.GetCurrentSnapshot().SetDefaultDefinitions();
cmGlobalGenerator gg(&cm);
cmMakefile mf(&gg, cm.GetCurrentSnapshot());
if (!this->ReadCustomConfigurationFileTree(this->BinaryDir.c_str(), &mf)) {
cmCTestOptionalLog(
this, DEBUG, "Cannot find custom configuration file tree" << std::endl,
quiet);
return 0;
}
if (this->ProduceXML) {
// Verify "Testing" directory exists:
//
2006-03-15 16:02:08 +00:00
std::string testingDir = this->BinaryDir + "/Testing";
if (cmSystemTools::FileExists(testingDir.c_str())) {
if (!cmSystemTools::FileIsDirectory(testingDir)) {
cmCTestLog(this, ERROR_MESSAGE, "File "
<< testingDir
<< " is in the place of the testing directory"
<< std::endl);
return 0;
2002-12-17 02:19:21 +00:00
}
} else {
if (!cmSystemTools::MakeDirectory(testingDir.c_str())) {
2006-03-09 16:57:43 +00:00
cmCTestLog(this, ERROR_MESSAGE, "Cannot create directory "
<< testingDir << std::endl);
return 0;
2002-12-17 02:19:21 +00:00
}
}
// Create new "TAG" file or read existing one:
//
bool createNewTag = true;
if (command) {
createNewTag = command->ShouldCreateNewTag();
}
2002-12-17 02:19:21 +00:00
std::string tagfile = testingDir + "/TAG";
cmsys::ifstream tfin(tagfile.c_str());
2002-12-17 02:19:21 +00:00
std::string tag;
if (createNewTag) {
2017-08-22 21:42:36 +00:00
time_t tctime = time(nullptr);
if (this->TomorrowTag) {
tctime += (24 * 60 * 60);
}
struct tm* lctime = gmtime(&tctime);
if (tfin && cmSystemTools::GetLineFromStream(tfin, tag)) {
int year = 0;
int mon = 0;
int day = 0;
int hour = 0;
int min = 0;
sscanf(tag.c_str(), "%04d%02d%02d-%02d%02d", &year, &mon, &day, &hour,
&min);
if (year != lctime->tm_year + 1900 || mon != lctime->tm_mon + 1 ||
day != lctime->tm_mday) {
tag.clear();
}
std::string tagmode;
if (cmSystemTools::GetLineFromStream(tfin, tagmode)) {
if (tagmode.size() > 4 && !this->Parts[PartStart]) {
this->TestModel = cmCTest::GetTestModelFromString(tagmode.c_str());
}
}
tfin.close();
}
2017-08-22 21:42:36 +00:00
if (tag.empty() || (nullptr != command) || this->Parts[PartStart]) {
cmCTestOptionalLog(
this, DEBUG,
"TestModel: " << this->GetTestModelString() << std::endl, quiet);
cmCTestOptionalLog(
this, DEBUG, "TestModel: " << this->TestModel << std::endl, quiet);
if (this->TestModel == cmCTest::NIGHTLY) {
lctime = this->GetNightlyTime(
2010-01-05 13:53:37 +00:00
this->GetCTestConfiguration("NightlyStartTime"),
this->TomorrowTag);
}
char datestring[100];
sprintf(datestring, "%04d%02d%02d-%02d%02d", lctime->tm_year + 1900,
lctime->tm_mon + 1, lctime->tm_mday, lctime->tm_hour,
lctime->tm_min);
tag = datestring;
cmsys::ofstream ofs(tagfile.c_str());
if (ofs) {
ofs << tag << std::endl;
ofs << this->GetTestModelString() << std::endl;
}
ofs.close();
2017-08-22 21:42:36 +00:00
if (nullptr == command) {
cmCTestOptionalLog(this, OUTPUT, "Create new tag: "
<< tag << " - " << this->GetTestModelString()
<< std::endl,
quiet);
2003-02-11 02:52:01 +00:00
}
}
} else {
if (tfin) {
cmSystemTools::GetLineFromStream(tfin, tag);
tfin.close();
}
if (tag.empty()) {
cmCTestLog(this, ERROR_MESSAGE, "Cannot read existing TAG file in "
<< testingDir << std::endl);
return 0;
2002-12-17 02:19:21 +00:00
}
cmCTestOptionalLog(this, OUTPUT, " Use existing tag: "
<< tag << " - " << this->GetTestModelString()
<< std::endl,
quiet);
2002-12-17 02:19:21 +00:00
}
this->CurrentTag = tag;
}
return 1;
2002-12-17 02:19:21 +00:00
}
bool cmCTest::InitializeFromCommand(cmCTestStartCommand* command)
{
std::string src_dir = this->GetCTestConfiguration("SourceDirectory");
std::string bld_dir = this->GetCTestConfiguration("BuildDirectory");
2006-03-15 16:02:08 +00:00
this->DartVersion = 1;
this->DropSiteCDash = false;
for (Part p = PartStart; p != PartCount; p = Part(p + 1)) {
this->Parts[p].SubmitFiles.clear();
}
cmMakefile* mf = command->GetMakefile();
std::string fname;
std::string src_dir_fname = src_dir;
src_dir_fname += "/CTestConfig.cmake";
cmSystemTools::ConvertToUnixSlashes(src_dir_fname);
std::string bld_dir_fname = bld_dir;
bld_dir_fname += "/CTestConfig.cmake";
cmSystemTools::ConvertToUnixSlashes(bld_dir_fname);
if (cmSystemTools::FileExists(bld_dir_fname.c_str())) {
fname = bld_dir_fname;
} else if (cmSystemTools::FileExists(src_dir_fname.c_str())) {
fname = src_dir_fname;
}
if (!fname.empty()) {
cmCTestOptionalLog(this, OUTPUT, " Reading ctest configuration file: "
<< fname << std::endl,
command->ShouldBeQuiet());
bool readit = mf->ReadDependentFile(fname.c_str());
if (!readit) {
std::string m = "Could not find include file: ";
m += fname;
command->SetError(m);
return false;
}
} else {
cmCTestOptionalLog(this, WARNING,
"Cannot locate CTest configuration: in BuildDirectory: "
<< bld_dir_fname << std::endl,
command->ShouldBeQuiet());
cmCTestOptionalLog(
this, WARNING, "Cannot locate CTest configuration: in SourceDirectory: "
<< src_dir_fname << std::endl,
command->ShouldBeQuiet());
}
2006-03-09 16:57:43 +00:00
this->SetCTestConfigurationFromCMakeVariable(mf, "NightlyStartTime",
"CTEST_NIGHTLY_START_TIME",
command->ShouldBeQuiet());
this->SetCTestConfigurationFromCMakeVariable(mf, "Site", "CTEST_SITE",
command->ShouldBeQuiet());
this->SetCTestConfigurationFromCMakeVariable(
mf, "BuildName", "CTEST_BUILD_NAME", command->ShouldBeQuiet());
const char* dartVersion = mf->GetDefinition("CTEST_DART_SERVER_VERSION");
if (dartVersion) {
2006-03-15 16:02:08 +00:00
this->DartVersion = atoi(dartVersion);
if (this->DartVersion < 0) {
2006-03-09 16:57:43 +00:00
cmCTestLog(this, ERROR_MESSAGE, "Invalid Dart server version: "
<< dartVersion << ". Please specify the version number."
<< std::endl);
return false;
}
}
this->DropSiteCDash = mf->IsOn("CTEST_DROP_SITE_CDASH");
if (!this->Initialize(bld_dir.c_str(), command)) {
return false;
}
cmCTestOptionalLog(this, OUTPUT, " Use "
<< this->GetTestModelString()
<< " tag: " << this->GetCurrentTag() << std::endl,
command->ShouldBeQuiet());
return true;
}
bool cmCTest::UpdateCTestConfiguration()
{
if (this->SuppressUpdatingCTestConfiguration) {
return true;
}
std::string fileName = this->BinaryDir + "/CTestConfiguration.ini";
if (!cmSystemTools::FileExists(fileName.c_str())) {
fileName = this->BinaryDir + "/DartConfiguration.tcl";
}
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
"UpdateCTestConfiguration from :" << fileName << "\n");
if (!cmSystemTools::FileExists(fileName.c_str())) {
// No need to exit if we are not producing XML
if (this->ProduceXML) {
cmCTestLog(this, ERROR_MESSAGE, "Cannot find file: " << fileName
<< std::endl);
return false;
}
} else {
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, "Parse Config file:" << fileName
<< "\n");
// parse the dart test file
cmsys::ifstream fin(fileName.c_str());
if (!fin) {
return false;
}
char buffer[1024];
while (fin) {
buffer[0] = 0;
fin.getline(buffer, 1023);
buffer[1023] = 0;
std::string line = cmCTest::CleanString(buffer);
if (line.empty()) {
continue;
}
while (fin && (line[line.size() - 1] == '\\')) {
line = line.substr(0, line.size() - 1);
buffer[0] = 0;
fin.getline(buffer, 1023);
buffer[1023] = 0;
line += cmCTest::CleanString(buffer);
}
if (line[0] == '#') {
continue;
}
std::string::size_type cpos = line.find_first_of(':');
2017-05-30 19:37:46 +00:00
if (cpos == std::string::npos) {
continue;
}
std::string key = line.substr(0, cpos);
std::string value = cmCTest::CleanString(line.substr(cpos + 1));
2006-03-15 16:02:08 +00:00
this->CTestConfiguration[key] = value;
}
fin.close();
}
if (!this->GetCTestConfiguration("BuildDirectory").empty()) {
this->BinaryDir = this->GetCTestConfiguration("BuildDirectory");
cmSystemTools::ChangeDirectory(this->BinaryDir);
}
this->TimeOut =
std::chrono::seconds(atoi(this->GetCTestConfiguration("TimeOut").c_str()));
std::string const& testLoad = this->GetCTestConfiguration("TestLoad");
if (!testLoad.empty()) {
unsigned long load;
if (cmSystemTools::StringToULong(testLoad.c_str(), &load)) {
this->SetTestLoad(load);
} else {
cmCTestLog(this, WARNING,
"Invalid value for 'Test Load' : " << testLoad << std::endl);
}
}
if (this->ProduceXML) {
2006-03-15 16:02:08 +00:00
this->CompressXMLFiles = cmSystemTools::IsOn(
2006-03-09 16:57:43 +00:00
this->GetCTestConfiguration("CompressSubmission").c_str());
}
return true;
}
void cmCTest::BlockTestErrorDiagnostics()
{
cmSystemTools::PutEnv("DART_TEST_FROM_DART=1");
2005-09-15 21:22:06 +00:00
cmSystemTools::PutEnv("DASHBOARD_TEST_FROM_CTEST=" CMake_VERSION);
#if defined(_WIN32)
2006-03-09 16:57:43 +00:00
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
#elif defined(__BEOS__) || defined(__HAIKU__)
2006-12-04 22:26:41 +00:00
disable_debugger(1);
#endif
}
void cmCTest::SetTestModel(int mode)
{
2006-03-15 16:02:08 +00:00
this->InteractiveDebugMode = false;
this->TestModel = mode;
}
bool cmCTest::SetTest(const char* ttype, bool report)
2002-12-17 02:19:21 +00:00
{
if (cmSystemTools::LowerCase(ttype) == "all") {
for (Part p = PartStart; p != PartCount; p = Part(p + 1)) {
this->Parts[p].Enable();
}
return true;
}
Part p = this->GetPartFromName(ttype);
if (p != PartCount) {
this->Parts[p].Enable();
return true;
}
2016-08-18 18:36:29 +00:00
if (report) {
cmCTestLog(this, ERROR_MESSAGE, "Don't know about test \""
<< ttype << "\" yet..." << std::endl);
}
return false;
2002-12-17 02:19:21 +00:00
}
void cmCTest::Finalize()
{
}
bool cmCTest::OpenOutputFile(const std::string& path, const std::string& name,
cmGeneratedFileStream& stream, bool compress)
2004-09-09 12:41:05 +00:00
{
2006-03-15 16:02:08 +00:00
std::string testingDir = this->BinaryDir + "/Testing";
if (!path.empty()) {
2004-09-09 12:41:05 +00:00
testingDir += "/" + path;
}
if (cmSystemTools::FileExists(testingDir.c_str())) {
if (!cmSystemTools::FileIsDirectory(testingDir)) {
cmCTestLog(this, ERROR_MESSAGE, "File "
<< testingDir << " is in the place of the testing directory"
<< std::endl);
2004-09-09 12:41:05 +00:00
return false;
2002-12-17 02:19:21 +00:00
}
} else {
if (!cmSystemTools::MakeDirectory(testingDir.c_str())) {
cmCTestLog(this, ERROR_MESSAGE, "Cannot create directory " << testingDir
<< std::endl);
2004-09-09 12:41:05 +00:00
return false;
2002-12-17 02:19:21 +00:00
}
}
2004-09-09 12:41:05 +00:00
std::string filename = testingDir + "/" + name;
stream.Open(filename.c_str());
if (!stream) {
2006-03-09 16:57:43 +00:00
cmCTestLog(this, ERROR_MESSAGE, "Problem opening file: " << filename
<< std::endl);
2004-09-09 12:41:05 +00:00
return false;
}
if (compress) {
if (this->CompressXMLFiles) {
stream.SetCompression(true);
}
}
return true;
}
bool cmCTest::AddIfExists(Part part, const char* file)
{
if (this->CTestFileExists(file)) {
this->AddSubmitFile(part, file);
} else {
std::string name = file;
name += ".gz";
if (this->CTestFileExists(name)) {
this->AddSubmitFile(part, file);
} else {
return false;
}
}
2004-09-09 12:41:05 +00:00
return true;
2002-12-17 02:19:21 +00:00
}
2004-09-09 12:41:05 +00:00
bool cmCTest::CTestFileExists(const std::string& filename)
{
std::string testingDir =
this->BinaryDir + "/Testing/" + this->CurrentTag + "/" + filename;
2004-09-09 12:41:05 +00:00
return cmSystemTools::FileExists(testingDir.c_str());
}
cmCTestGenericHandler* cmCTest::GetInitializedHandler(const char* handler)
{
cmCTest::t_TestingHandlers::iterator it =
2006-05-10 17:56:27 +00:00
this->TestingHandlers.find(handler);
if (it == this->TestingHandlers.end()) {
2017-08-22 21:42:36 +00:00
return nullptr;
}
it->second->Initialize();
return it->second;
}
cmCTestGenericHandler* cmCTest::GetHandler(const char* handler)
{
cmCTest::t_TestingHandlers::iterator it =
2006-05-10 17:56:27 +00:00
this->TestingHandlers.find(handler);
if (it == this->TestingHandlers.end()) {
2017-08-22 21:42:36 +00:00
return nullptr;
}
return it->second;
}
2004-09-09 12:41:05 +00:00
int cmCTest::ExecuteHandler(const char* shandler)
{
cmCTestGenericHandler* handler = this->GetHandler(shandler);
if (!handler) {
return -1;
}
handler->Initialize();
2006-03-09 16:57:43 +00:00
return handler->ProcessHandler();
}
2004-09-09 12:41:05 +00:00
int cmCTest::ProcessSteps()
2002-12-17 02:19:21 +00:00
{
int res = 0;
bool notest = true;
2003-02-11 04:19:01 +00:00
int update_count = 0;
2002-12-17 02:19:21 +00:00
for (Part p = PartStart; notest && p != PartCount; p = Part(p + 1)) {
notest = !this->Parts[p];
}
if (this->Parts[PartUpdate] &&
(this->GetRemainingTimeAllowed() > std::chrono::minutes(2))) {
cmCTestGenericHandler* uphandler = this->GetHandler("update");
uphandler->SetPersistentOption(
"SourceDirectory",
2006-03-09 16:57:43 +00:00
this->GetCTestConfiguration("SourceDirectory").c_str());
update_count = uphandler->ProcessHandler();
if (update_count < 0) {
2004-09-06 16:46:35 +00:00
res |= cmCTest::UPDATE_ERRORS;
2003-02-11 04:19:01 +00:00
}
}
if (this->TestModel == cmCTest::CONTINUOUS && !update_count) {
2003-02-11 04:19:01 +00:00
return 0;
}
if (this->Parts[PartConfigure] &&
(this->GetRemainingTimeAllowed() > std::chrono::minutes(2))) {
if (this->GetHandler("configure")->ProcessHandler() < 0) {
2004-09-06 16:46:35 +00:00
res |= cmCTest::CONFIGURE_ERRORS;
2002-12-17 02:19:21 +00:00
}
}
if (this->Parts[PartBuild] &&
(this->GetRemainingTimeAllowed() > std::chrono::minutes(2))) {
this->UpdateCTestConfiguration();
if (this->GetHandler("build")->ProcessHandler() < 0) {
2004-09-06 16:46:35 +00:00
res |= cmCTest::BUILD_ERRORS;
2002-12-17 02:19:21 +00:00
}
}
if ((this->Parts[PartTest] || notest) &&
(this->GetRemainingTimeAllowed() > std::chrono::minutes(2))) {
this->UpdateCTestConfiguration();
if (this->GetHandler("test")->ProcessHandler() < 0) {
2004-09-06 16:46:35 +00:00
res |= cmCTest::TEST_ERRORS;
2002-12-17 02:19:21 +00:00
}
}
if (this->Parts[PartCoverage] &&
(this->GetRemainingTimeAllowed() > std::chrono::minutes(2))) {
this->UpdateCTestConfiguration();
if (this->GetHandler("coverage")->ProcessHandler() < 0) {
2004-10-17 22:49:42 +00:00
res |= cmCTest::COVERAGE_ERRORS;
2002-12-17 02:19:21 +00:00
}
}
if (this->Parts[PartMemCheck] &&
(this->GetRemainingTimeAllowed() > std::chrono::minutes(2))) {
this->UpdateCTestConfiguration();
if (this->GetHandler("memcheck")->ProcessHandler() < 0) {
2004-09-06 16:46:35 +00:00
res |= cmCTest::MEMORY_ERRORS;
2002-12-17 02:19:21 +00:00
}
}
if (!notest) {
2006-03-15 16:02:08 +00:00
std::string notes_dir = this->BinaryDir + "/Testing/Notes";
if (cmSystemTools::FileIsDirectory(notes_dir)) {
cmsys::Directory d;
d.Load(notes_dir);
unsigned long kk;
for (kk = 0; kk < d.GetNumberOfFiles(); kk++) {
const char* file = d.GetFile(kk);
std::string fullname = notes_dir + "/" + file;
if (cmSystemTools::FileExists(fullname.c_str()) &&
!cmSystemTools::FileIsDirectory(fullname)) {
if (!this->NotesFiles.empty()) {
2006-03-15 16:02:08 +00:00
this->NotesFiles += ";";
}
2006-03-15 16:02:08 +00:00
this->NotesFiles += fullname;
this->Parts[PartNotes].Enable();
}
}
}
}
if (this->Parts[PartNotes]) {
this->UpdateCTestConfiguration();
if (!this->NotesFiles.empty()) {
2006-03-15 16:02:08 +00:00
this->GenerateNotesFile(this->NotesFiles.c_str());
}
}
if (this->Parts[PartSubmit]) {
this->UpdateCTestConfiguration();
if (this->GetHandler("submit")->ProcessHandler() < 0) {
res |= cmCTest::SUBMIT_ERRORS;
2005-10-04 14:58:21 +00:00
}
}
if (res != 0) {
cmCTestLog(this, ERROR_MESSAGE, "Errors while running CTest" << std::endl);
}
2002-12-17 02:19:21 +00:00
return res;
}
2003-02-11 02:52:01 +00:00
std::string cmCTest::GetTestModelString()
{
if (!this->SpecificTrack.empty()) {
return this->SpecificTrack;
}
switch (this->TestModel) {
case cmCTest::NIGHTLY:
return "Nightly";
case cmCTest::CONTINUOUS:
return "Continuous";
}
2003-02-11 02:52:01 +00:00
return "Experimental";
}
int cmCTest::GetTestModelFromString(const char* str)
{
if (!str) {
return cmCTest::EXPERIMENTAL;
}
std::string rstr = cmSystemTools::LowerCase(str);
if (cmHasLiteralPrefix(rstr.c_str(), "cont")) {
return cmCTest::CONTINUOUS;
}
if (cmHasLiteralPrefix(rstr.c_str(), "nigh")) {
return cmCTest::NIGHTLY;
}
return cmCTest::EXPERIMENTAL;
}
2005-06-10 13:01:37 +00:00
//######################################################################
//######################################################################
//######################################################################
//######################################################################
int cmCTest::RunMakeCommand(const char* command, std::string& output,
int* retVal, const char* dir, cmDuration timeout,
std::ostream& ofs, Encoding encoding)
{
2005-06-10 13:01:37 +00:00
// First generate the command and arguments
std::vector<std::string> args = cmSystemTools::ParseArguments(command);
if (args.empty()) {
return false;
}
std::vector<const char*> argv;
argv.reserve(args.size() + 1);
for (std::string const& a : args) {
argv.push_back(a.c_str());
}
2017-08-22 21:42:36 +00:00
argv.push_back(nullptr);
output.clear();
2005-06-10 13:01:37 +00:00
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, "Run command:");
for (char const* arg : argv) {
if (!arg) {
break;
}
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, " \"" << arg << "\"");
}
2005-06-10 13:01:37 +00:00
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, std::endl);
2006-03-09 16:57:43 +00:00
2005-06-10 13:01:37 +00:00
// Now create process object
cmsysProcess* cp = cmsysProcess_New();
cmsysProcess_SetCommand(cp, &*argv.begin());
cmsysProcess_SetWorkingDirectory(cp, dir);
cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
cmsysProcess_SetTimeout(cp, timeout.count());
cmsysProcess_Execute(cp);
2005-06-10 13:01:37 +00:00
// Initialize tick's
std::string::size_type tick = 0;
std::string::size_type tick_len = 1024;
std::string::size_type tick_line_len = 50;
char* data;
int length;
cmProcessOutput processOutput(encoding);
std::string strdata;
cmCTestLog(this, HANDLER_PROGRESS_OUTPUT, " Each . represents "
<< tick_len << " bytes of output" << std::endl
<< " " << std::flush);
2017-08-22 21:42:36 +00:00
while (cmsysProcess_WaitForData(cp, &data, &length, nullptr)) {
processOutput.DecodeText(data, length, strdata);
for (char& cc : strdata) {
if (cc == 0) {
cc = '\n';
}
}
output.append(strdata);
while (output.size() > (tick * tick_len)) {
tick++;
cmCTestLog(this, HANDLER_PROGRESS_OUTPUT, "." << std::flush);
if (tick % tick_line_len == 0 && tick > 0) {
cmCTestLog(this, HANDLER_PROGRESS_OUTPUT,
" Size: " << int((double(output.size()) / 1024.0) + 1)
<< "K" << std::endl
<< " " << std::flush);
}
}
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
cmCTestLogWrite(strdata.c_str(), strdata.size()));
if (ofs) {
ofs << cmCTestLogWrite(strdata.c_str(), strdata.size());
}
}
processOutput.DecodeText(std::string(), strdata);
if (!strdata.empty()) {
output.append(strdata);
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
cmCTestLogWrite(strdata.c_str(), strdata.size()));
if (ofs) {
ofs << cmCTestLogWrite(strdata.c_str(), strdata.size());
}
}
cmCTestLog(this, HANDLER_PROGRESS_OUTPUT, " Size of output: "
<< int(double(output.size()) / 1024.0) << "K" << std::endl);
2017-08-22 21:42:36 +00:00
cmsysProcess_WaitForExit(cp, nullptr);
int result = cmsysProcess_GetState(cp);
if (result == cmsysProcess_State_Exited) {
*retVal = cmsysProcess_GetExitValue(cp);
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
"Command exited with the value: " << *retVal << std::endl);
} else if (result == cmsysProcess_State_Exception) {
*retVal = cmsysProcess_GetExitException(cp);
2006-03-09 16:57:43 +00:00
cmCTestLog(this, WARNING, "There was an exception: " << *retVal
<< std::endl);
} else if (result == cmsysProcess_State_Expired) {
cmCTestLog(this, WARNING, "There was a timeout" << std::endl);
} else if (result == cmsysProcess_State_Error) {
output += "\n*** ERROR executing: ";
output += cmsysProcess_GetErrorString(cp);
output += "\n***The build process failed.";
2006-03-09 16:57:43 +00:00
cmCTestLog(this, ERROR_MESSAGE, "There was an error: "
<< cmsysProcess_GetErrorString(cp) << std::endl);
}
cmsysProcess_Delete(cp);
return result;
}
2003-02-11 02:52:01 +00:00
2005-06-10 13:01:37 +00:00
//######################################################################
//######################################################################
//######################################################################
//######################################################################
int cmCTest::RunTest(std::vector<const char*> argv, std::string* output,
int* retVal, std::ostream* log, cmDuration testTimeOut,
std::vector<std::string>* environment, Encoding encoding)
2003-08-13 22:17:11 +00:00
{
bool modifyEnv = (environment && !environment->empty());
// determine how much time we have
cmDuration timeout = this->GetRemainingTimeAllowed();
if (timeout != cmCTest::MaxDuration()) {
timeout -= std::chrono::minutes(2);
}
if (this->TimeOut > cmDuration::zero() && this->TimeOut < timeout) {
timeout = this->TimeOut;
}
if (testTimeOut > cmDuration::zero() &&
testTimeOut < this->GetRemainingTimeAllowed()) {
timeout = testTimeOut;
}
// always have at least 1 second if we got to here
if (timeout <= cmDuration::zero()) {
timeout = std::chrono::seconds(1);
}
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, "Test timeout computed to be: "
<< (timeout == cmCTest::MaxDuration()
? std::string("infinite")
: std::to_string(cmDurationTo<unsigned int>(timeout)))
<< "\n");
if (cmSystemTools::SameFile(argv[0], cmSystemTools::GetCTestCommand()) &&
!this->ForceNewCTestProcess) {
cmCTest inst;
2006-03-15 16:02:08 +00:00
inst.ConfigType = this->ConfigType;
inst.TimeOut = timeout;
// Capture output of the child ctest.
std::ostringstream oss;
inst.SetStreams(&oss, &oss);
std::vector<std::string> args;
for (char const* i : argv) {
if (i) {
// make sure we pass the timeout in for any build and test
// invocations. Since --build-generator is required this is a
// good place to check for it, and to add the arguments in
if (strcmp(i, "--build-generator") == 0 &&
timeout != cmCTest::MaxDuration() &&
timeout > cmDuration::zero()) {
2007-01-25 16:16:16 +00:00
args.push_back("--test-timeout");
std::ostringstream msg;
msg << cmDurationTo<unsigned int>(timeout);
2007-01-25 16:16:16 +00:00
args.push_back(msg.str());
}
args.push_back(i);
}
}
if (log) {
*log << "* Run internal CTest" << std::endl;
}
2006-03-09 16:57:43 +00:00
std::unique_ptr<cmSystemTools::SaveRestoreEnvironment> saveEnv;
if (modifyEnv) {
saveEnv = cm::make_unique<cmSystemTools::SaveRestoreEnvironment>();
cmSystemTools::AppendEnv(*environment);
}
*retVal = inst.Run(args, output);
if (output) {
2015-03-25 16:38:37 +00:00
*output += oss.str();
}
if (log && output) {
*log << *output;
}
if (output) {
2015-03-25 16:38:37 +00:00
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
"Internal cmCTest object used to run test." << std::endl
<< *output
<< std::endl);
}
return cmsysProcess_State_Exited;
}
std::vector<char> tempOutput;
if (output) {
output->clear();
}
2003-08-13 22:17:11 +00:00
std::unique_ptr<cmSystemTools::SaveRestoreEnvironment> saveEnv;
if (modifyEnv) {
saveEnv = cm::make_unique<cmSystemTools::SaveRestoreEnvironment>();
cmSystemTools::AppendEnv(*environment);
}
2003-08-13 22:17:11 +00:00
cmsysProcess* cp = cmsysProcess_New();
cmsysProcess_SetCommand(cp, &*argv.begin());
cmCTestLog(this, DEBUG, "Command is: " << argv[0] << std::endl);
if (cmSystemTools::GetRunCommandHideConsole()) {
2003-08-13 22:17:11 +00:00
cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
}
cmsysProcess_SetTimeout(cp, timeout.count());
2003-08-13 22:17:11 +00:00
cmsysProcess_Execute(cp);
2003-08-13 22:17:11 +00:00
char* data;
int length;
cmProcessOutput processOutput(encoding);
std::string strdata;
2017-08-22 21:42:36 +00:00
while (cmsysProcess_WaitForData(cp, &data, &length, nullptr)) {
processOutput.DecodeText(data, length, strdata);
if (output) {
tempOutput.insert(tempOutput.end(), data, data + length);
}
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
cmCTestLogWrite(strdata.c_str(), strdata.size()));
if (log) {
log->write(strdata.c_str(), strdata.size());
}
}
processOutput.DecodeText(std::string(), strdata);
if (!strdata.empty()) {
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
cmCTestLogWrite(strdata.c_str(), strdata.size()));
if (log) {
log->write(strdata.c_str(), strdata.size());
2003-08-13 22:17:11 +00:00
}
}
2017-08-22 21:42:36 +00:00
cmsysProcess_WaitForExit(cp, nullptr);
processOutput.DecodeText(tempOutput, tempOutput);
if (output && tempOutput.begin() != tempOutput.end()) {
output->append(&*tempOutput.begin(), tempOutput.size());
}
2006-03-09 16:57:43 +00:00
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, "-- Process completed"
<< std::endl);
2003-08-13 22:17:11 +00:00
int result = cmsysProcess_GetState(cp);
if (result == cmsysProcess_State_Exited) {
2003-08-13 22:17:11 +00:00
*retVal = cmsysProcess_GetExitValue(cp);
if (*retVal != 0 && this->OutputTestOutputOnTestFailure) {
OutputTestErrors(tempOutput);
}
} else if (result == cmsysProcess_State_Exception) {
if (this->OutputTestOutputOnTestFailure) {
OutputTestErrors(tempOutput);
2003-08-13 22:17:11 +00:00
}
*retVal = cmsysProcess_GetExitException(cp);
std::string outerr = "\n*** Exception executing: ";
outerr += cmsysProcess_GetExceptionString(cp);
if (output) {
2015-03-25 16:38:37 +00:00
*output += outerr;
2003-08-13 22:17:11 +00:00
}
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl
<< std::flush);
} else if (result == cmsysProcess_State_Error) {
std::string outerr = "\n*** ERROR executing: ";
outerr += cmsysProcess_GetErrorString(cp);
if (output) {
2015-03-25 16:38:37 +00:00
*output += outerr;
2003-08-13 22:17:11 +00:00
}
cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl
<< std::flush);
}
2003-08-13 22:17:11 +00:00
cmsysProcess_Delete(cp);
2003-08-13 22:17:11 +00:00
return result;
}
std::string cmCTest::SafeBuildIdField(const std::string& value)
{
std::string safevalue(value);
if (!safevalue.empty()) {
// Disallow non-filename and non-space whitespace characters.
// If they occur, replace them with ""
//
const char* disallowed = "\\:*?\"<>|\n\r\t\f\v";
2017-05-30 19:37:46 +00:00
if (safevalue.find_first_of(disallowed) != std::string::npos) {
std::string::size_type i = 0;
std::string::size_type n = strlen(disallowed);
char replace[2];
replace[1] = 0;
for (i = 0; i < n; ++i) {
replace[0] = disallowed[i];
cmSystemTools::ReplaceString(safevalue, replace, "");
}
}
}
if (safevalue.empty()) {
safevalue = "(empty)";
}
return safevalue;
}
void cmCTest::StartXML(cmXMLWriter& xml, bool append)
{
if (this->CurrentTag.empty()) {
cmCTestLog(this, ERROR_MESSAGE, "Current Tag empty, this may mean"
" NightlStartTime was not set correctly."
<< std::endl);
cmSystemTools::SetFatalErrorOccured();
}
// find out about the system
cmsys::SystemInformation info;
info.RunCPUCheck();
info.RunOSCheck();
info.RunMemoryCheck();
std::string buildname =
cmCTest::SafeBuildIdField(this->GetCTestConfiguration("BuildName"));
std::string stamp = cmCTest::SafeBuildIdField(this->CurrentTag + "-" +
this->GetTestModelString());
std::string site =
cmCTest::SafeBuildIdField(this->GetCTestConfiguration("Site"));
xml.StartDocument();
xml.StartElement("Site");
xml.Attribute("BuildName", buildname);
xml.BreakAttributes();
xml.Attribute("BuildStamp", stamp);
xml.Attribute("Name", site);
xml.Attribute("Generator",
std::string("ctest-") + cmVersion::GetCMakeVersion());
if (append) {
xml.Attribute("Append", "true");
}
xml.Attribute("CompilerName", this->GetCTestConfiguration("Compiler"));
xml.Attribute("CompilerVersion",
this->GetCTestConfiguration("CompilerVersion"));
xml.Attribute("OSName", info.GetOSName());
xml.Attribute("Hostname", info.GetHostname());
xml.Attribute("OSRelease", info.GetOSRelease());
xml.Attribute("OSVersion", info.GetOSVersion());
xml.Attribute("OSPlatform", info.GetOSPlatform());
xml.Attribute("Is64Bits", info.Is64Bits());
xml.Attribute("VendorString", info.GetVendorString());
xml.Attribute("VendorID", info.GetVendorID());
xml.Attribute("FamilyID", info.GetFamilyID());
xml.Attribute("ModelID", info.GetModelID());
xml.Attribute("ProcessorCacheSize", info.GetProcessorCacheSize());
xml.Attribute("NumberOfLogicalCPU", info.GetNumberOfLogicalCPU());
xml.Attribute("NumberOfPhysicalCPU", info.GetNumberOfPhysicalCPU());
xml.Attribute("TotalVirtualMemory", info.GetTotalVirtualMemory());
xml.Attribute("TotalPhysicalMemory", info.GetTotalPhysicalMemory());
xml.Attribute("LogicalProcessorsPerPhysical",
info.GetLogicalProcessorsPerPhysical());
xml.Attribute("ProcessorClockFrequency", info.GetProcessorClockFrequency());
std::string changeId = this->GetCTestConfiguration("ChangeId");
if (!changeId.empty()) {
xml.Attribute("ChangeId", changeId);
}
this->AddSiteProperties(xml);
}
void cmCTest::AddSiteProperties(cmXMLWriter& xml)
{
cmCTestScriptHandler* ch =
static_cast<cmCTestScriptHandler*>(this->GetHandler("script"));
cmake* cm = ch->GetCMake();
2009-01-09 17:56:09 +00:00
// if no CMake then this is the old style script and props like
// this will not work anyway.
if (!cm) {
2009-01-09 17:56:09 +00:00
return;
}
// This code should go when cdash is changed to use labels only
const char* subproject = cm->GetState()->GetGlobalProperty("SubProject");
if (subproject) {
xml.StartElement("Subproject");
xml.Attribute("name", subproject);
const char* labels =
ch->GetCMake()->GetState()->GetGlobalProperty("SubProjectLabels");
if (labels) {
xml.StartElement("Labels");
std::string l = labels;
std::vector<std::string> args;
cmSystemTools::ExpandListArgument(l, args);
for (std::string const& i : args) {
xml.Element("Label", i);
}
xml.EndElement();
}
xml.EndElement();
}
// This code should stay when cdash only does label based sub-projects
const char* label = cm->GetState()->GetGlobalProperty("Label");
if (label) {
xml.StartElement("Labels");
xml.Element("Label", label);
xml.EndElement();
}
}
void cmCTest::GenerateSubprojectsOutput(cmXMLWriter& xml)
{
for (std::string const& subproj : this->GetLabelsForSubprojects()) {
xml.StartElement("Subproject");
xml.Attribute("name", subproj);
xml.Element("Label", subproj);
xml.EndElement(); // Subproject
}
}
std::vector<std::string> cmCTest::GetLabelsForSubprojects()
{
std::string labelsForSubprojects =
this->GetCTestConfiguration("LabelsForSubprojects");
std::vector<std::string> subprojects;
cmSystemTools::ExpandListArgument(labelsForSubprojects, subprojects);
// sort the array
std::sort(subprojects.begin(), subprojects.end());
// remove duplicates
std::vector<std::string>::iterator new_end =
std::unique(subprojects.begin(), subprojects.end());
subprojects.erase(new_end, subprojects.end());
return subprojects;
}
void cmCTest::EndXML(cmXMLWriter& xml)
{
xml.EndElement(); // Site
xml.EndDocument();
}
int cmCTest::GenerateCTestNotesOutput(cmXMLWriter& xml,
const cmCTest::VectorOfStrings& files)
{
std::string buildname =
cmCTest::SafeBuildIdField(this->GetCTestConfiguration("BuildName"));
xml.StartDocument();
xml.ProcessingInstruction("xml-stylesheet",
"type=\"text/xsl\" "
"href=\"Dart/Source/Server/XSL/Build.xsl "
"<file:///Dart/Source/Server/XSL/Build.xsl> \"");
xml.StartElement("Site");
xml.Attribute("BuildName", buildname);
xml.Attribute("BuildStamp",
this->CurrentTag + "-" + this->GetTestModelString());
xml.Attribute("Name", this->GetCTestConfiguration("Site"));
xml.Attribute("Generator",
std::string("ctest") + cmVersion::GetCMakeVersion());
this->AddSiteProperties(xml);
xml.StartElement("Notes");
for (cmsys::String const& file : files) {
cmCTestLog(this, OUTPUT, "\tAdd file: " << file << std::endl);
std::string note_time = this->CurrentTime();
xml.StartElement("Note");
xml.Attribute("Name", file);
xml.Element("Time", std::chrono::system_clock::now());
xml.Element("DateTime", note_time);
xml.StartElement("Text");
cmsys::ifstream ifs(file.c_str());
if (ifs) {
std::string line;
while (cmSystemTools::GetLineFromStream(ifs, line)) {
xml.Content(line);
xml.Content("\n");
}
ifs.close();
} else {
xml.Content("Problem reading file: " + file + "\n");
cmCTestLog(this, ERROR_MESSAGE, "Problem reading file: "
<< file << " while creating notes" << std::endl);
}
xml.EndElement(); // Text
xml.EndElement(); // Note
}
xml.EndElement(); // Notes
xml.EndElement(); // Site
xml.EndDocument();
return 1;
}
int cmCTest::GenerateNotesFile(const VectorOfStrings& files)
{
cmGeneratedFileStream ofs;
if (!this->OpenOutputFile(this->CurrentTag, "Notes.xml", ofs)) {
cmCTestLog(this, ERROR_MESSAGE, "Cannot open notes file" << std::endl);
return 1;
}
cmXMLWriter xml(ofs);
this->GenerateCTestNotesOutput(xml, files);
return 0;
}
int cmCTest::GenerateNotesFile(const char* cfiles)
{
if (!cfiles) {
return 1;
}
VectorOfStrings files;
cmCTestLog(this, OUTPUT, "Create notes file" << std::endl);
files = cmSystemTools::SplitString(cfiles, ';');
if (files.empty()) {
return 1;
}
return this->GenerateNotesFile(files);
}
std::string cmCTest::Base64GzipEncodeFile(std::string const& file)
2011-02-18 17:11:51 +00:00
{
std::string tarFile = file + "_temp.tar.gz";
std::vector<std::string> files;
2011-02-18 17:11:51 +00:00
files.push_back(file);
if (!cmSystemTools::CreateTar(tarFile.c_str(), files,
cmSystemTools::TarCompressGZip, false)) {
2011-02-18 17:11:51 +00:00
cmCTestLog(this, ERROR_MESSAGE, "Error creating tar while "
"encoding file: "
<< file << std::endl);
2011-02-18 17:11:51 +00:00
return "";
}
2011-02-28 20:26:24 +00:00
std::string base64 = this->Base64EncodeFile(tarFile);
cmSystemTools::RemoveFile(tarFile);
2011-02-28 20:26:24 +00:00
return base64;
}
std::string cmCTest::Base64EncodeFile(std::string const& file)
2011-02-28 20:26:24 +00:00
{
size_t const len = cmSystemTools::FileLength(file);
cmsys::ifstream ifs(file.c_str(), std::ios::in
2011-02-18 17:11:51 +00:00
#ifdef _WIN32
| std::ios::binary
2011-02-18 17:11:51 +00:00
#endif
);
std::vector<char> file_buffer(len + 1);
ifs.read(&file_buffer[0], len);
2011-02-18 17:11:51 +00:00
ifs.close();
std::vector<char> encoded_buffer((len * 3) / 2 + 5);
2011-02-18 17:11:51 +00:00
size_t const rlen = cmsysBase64_Encode(
reinterpret_cast<unsigned char*>(&file_buffer[0]), len,
reinterpret_cast<unsigned char*>(&encoded_buffer[0]), 1);
2011-02-18 17:11:51 +00:00
return std::string(&encoded_buffer[0], rlen);
2011-02-18 17:11:51 +00:00
}
bool cmCTest::SubmitExtraFiles(const VectorOfStrings& files)
{
for (cmsys::String const& file : files) {
if (!cmSystemTools::FileExists(file.c_str())) {
2006-03-09 16:57:43 +00:00
cmCTestLog(this, ERROR_MESSAGE, "Cannot find extra file: "
<< file << " to submit." << std::endl;);
return false;
}
this->AddSubmitFile(PartExtraFiles, file.c_str());
}
return true;
}
bool cmCTest::SubmitExtraFiles(const char* cfiles)
{
if (!cfiles) {
return true;
}
VectorOfStrings files;
cmCTestLog(this, OUTPUT, "Submit extra files" << std::endl);
files = cmSystemTools::SplitString(cfiles, ';');
if (files.empty()) {
return true;
}
return this->SubmitExtraFiles(files);
}
// for a -D argument convert the next argument into
// the proper list of dashboard steps via SetTest
bool cmCTest::AddTestsForDashboardType(std::string& targ)
{
if (targ == "Experimental") {
this->SetTestModel(cmCTest::EXPERIMENTAL);
this->SetTest("Start");
this->SetTest("Configure");
this->SetTest("Build");
this->SetTest("Test");
this->SetTest("Coverage");
this->SetTest("Submit");
} else if (targ == "ExperimentalStart") {
this->SetTestModel(cmCTest::EXPERIMENTAL);
this->SetTest("Start");
} else if (targ == "ExperimentalUpdate") {
this->SetTestModel(cmCTest::EXPERIMENTAL);
this->SetTest("Update");
} else if (targ == "ExperimentalConfigure") {
this->SetTestModel(cmCTest::EXPERIMENTAL);
this->SetTest("Configure");
} else if (targ == "ExperimentalBuild") {
this->SetTestModel(cmCTest::EXPERIMENTAL);
this->SetTest("Build");
} else if (targ == "ExperimentalTest") {
this->SetTestModel(cmCTest::EXPERIMENTAL);
this->SetTest("Test");
} else if (targ == "ExperimentalMemCheck" || targ == "ExperimentalPurify") {
this->SetTestModel(cmCTest::EXPERIMENTAL);
this->SetTest("MemCheck");
} else if (targ == "ExperimentalCoverage") {
this->SetTestModel(cmCTest::EXPERIMENTAL);
this->SetTest("Coverage");
} else if (targ == "ExperimentalSubmit") {
this->SetTestModel(cmCTest::EXPERIMENTAL);
this->SetTest("Submit");
} else if (targ == "Continuous") {
this->SetTestModel(cmCTest::CONTINUOUS);
this->SetTest("Start");
this->SetTest("Update");
this->SetTest("Configure");
this->SetTest("Build");
this->SetTest("Test");
this->SetTest("Coverage");
this->SetTest("Submit");
} else if (targ == "ContinuousStart") {
this->SetTestModel(cmCTest::CONTINUOUS);
this->SetTest("Start");
} else if (targ == "ContinuousUpdate") {
this->SetTestModel(cmCTest::CONTINUOUS);
this->SetTest("Update");
} else if (targ == "ContinuousConfigure") {
this->SetTestModel(cmCTest::CONTINUOUS);
this->SetTest("Configure");
} else if (targ == "ContinuousBuild") {
this->SetTestModel(cmCTest::CONTINUOUS);
this->SetTest("Build");
} else if (targ == "ContinuousTest") {
this->SetTestModel(cmCTest::CONTINUOUS);
this->SetTest("Test");
} else if (targ == "ContinuousMemCheck" || targ == "ContinuousPurify") {
this->SetTestModel(cmCTest::CONTINUOUS);
this->SetTest("MemCheck");
} else if (targ == "ContinuousCoverage") {
this->SetTestModel(cmCTest::CONTINUOUS);
this->SetTest("Coverage");
} else if (targ == "ContinuousSubmit") {
this->SetTestModel(cmCTest::CONTINUOUS);
this->SetTest("Submit");
} else if (targ == "Nightly") {
this->SetTestModel(cmCTest::NIGHTLY);
this->SetTest("Start");
this->SetTest("Update");
this->SetTest("Configure");
this->SetTest("Build");
this->SetTest("Test");
this->SetTest("Coverage");
this->SetTest("Submit");
} else if (targ == "NightlyStart") {
this->SetTestModel(cmCTest::NIGHTLY);
this->SetTest("Start");
} else if (targ == "NightlyUpdate") {
this->SetTestModel(cmCTest::NIGHTLY);
this->SetTest("Update");
} else if (targ == "NightlyConfigure") {
this->SetTestModel(cmCTest::NIGHTLY);
this->SetTest("Configure");
} else if (targ == "NightlyBuild") {
this->SetTestModel(cmCTest::NIGHTLY);
this->SetTest("Build");
} else if (targ == "NightlyTest") {
this->SetTestModel(cmCTest::NIGHTLY);
this->SetTest("Test");
} else if (targ == "NightlyMemCheck" || targ == "NightlyPurify") {
this->SetTestModel(cmCTest::NIGHTLY);
this->SetTest("MemCheck");
} else if (targ == "NightlyCoverage") {
this->SetTestModel(cmCTest::NIGHTLY);
this->SetTest("Coverage");
} else if (targ == "NightlySubmit") {
this->SetTestModel(cmCTest::NIGHTLY);
this->SetTest("Submit");
} else if (targ == "MemoryCheck") {
this->SetTestModel(cmCTest::EXPERIMENTAL);
this->SetTest("Start");
this->SetTest("Configure");
this->SetTest("Build");
this->SetTest("MemCheck");
this->SetTest("Coverage");
this->SetTest("Submit");
} else if (targ == "NightlyMemoryCheck") {
this->SetTestModel(cmCTest::NIGHTLY);
this->SetTest("Start");
this->SetTest("Update");
this->SetTest("Configure");
this->SetTest("Build");
this->SetTest("MemCheck");
this->SetTest("Coverage");
this->SetTest("Submit");
} else {
return false;
}
return true;
}
void cmCTest::ErrorMessageUnknownDashDValue(std::string& val)
{
cmCTestLog(this, ERROR_MESSAGE,
"CTest -D called with incorrect option: " << val << std::endl);
cmCTestLog(
this, ERROR_MESSAGE, "Available options are:"
<< std::endl
<< " ctest -D Continuous" << std::endl
<< " ctest -D Continuous(Start|Update|Configure|Build)" << std::endl
<< " ctest -D Continuous(Test|Coverage|MemCheck|Submit)" << std::endl
<< " ctest -D Experimental" << std::endl
<< " ctest -D Experimental(Start|Update|Configure|Build)" << std::endl
<< " ctest -D Experimental(Test|Coverage|MemCheck|Submit)" << std::endl
<< " ctest -D Nightly" << std::endl
<< " ctest -D Nightly(Start|Update|Configure|Build)" << std::endl
<< " ctest -D Nightly(Test|Coverage|MemCheck|Submit)" << std::endl
<< " ctest -D NightlyMemoryCheck" << std::endl);
}
2006-03-09 16:57:43 +00:00
bool cmCTest::CheckArgument(const std::string& arg, const char* varg1,
const char* varg2)
{
return (varg1 && arg == varg1) || (varg2 && arg == varg2);
}
// Processes one command line argument (and its arguments if any)
// for many simple options and then returns
bool cmCTest::HandleCommandLineArguments(size_t& i,
std::vector<std::string>& args,
std::string& errormsg)
{
std::string arg = args[i];
if (this->CheckArgument(arg, "-F")) {
this->Failover = true;
}
if (this->CheckArgument(arg, "-j", "--parallel") && i < args.size() - 1) {
2008-07-03 13:31:33 +00:00
i++;
int plevel = atoi(args[i].c_str());
this->SetParallelLevel(plevel);
this->ParallelLevelSetInCli = true;
} else if (arg.find("-j") == 0) {
2008-07-03 13:31:33 +00:00
int plevel = atoi(arg.substr(2).c_str());
this->SetParallelLevel(plevel);
this->ParallelLevelSetInCli = true;
}
if (this->CheckArgument(arg, "--repeat-until-fail")) {
if (i >= args.size() - 1) {
errormsg = "'--repeat-until-fail' requires an argument";
return false;
}
i++;
long repeat = 1;
if (!cmSystemTools::StringToLong(args[i].c_str(), &repeat)) {
errormsg =
"'--repeat-until-fail' given non-integer value '" + args[i] + "'";
return false;
}
this->RepeatTests = static_cast<int>(repeat);
if (repeat > 1) {
this->RepeatUntilFail = true;
}
}
if (this->CheckArgument(arg, "--test-load") && i < args.size() - 1) {
i++;
unsigned long load;
if (cmSystemTools::StringToULong(args[i].c_str(), &load)) {
this->SetTestLoad(load);
} else {
cmCTestLog(this, WARNING,
"Invalid value for 'Test Load' : " << args[i] << std::endl);
}
}
if (this->CheckArgument(arg, "--no-compress-output")) {
this->CompressTestOutput = false;
}
if (this->CheckArgument(arg, "--print-labels")) {
this->PrintLabels = true;
}
if (this->CheckArgument(arg, "--http1.0")) {
this->UseHTTP10 = true;
}
if (this->CheckArgument(arg, "--timeout") && i < args.size() - 1) {
i++;
auto timeout = cmDuration(atof(args[i].c_str()));
this->GlobalTimeout = timeout;
}
if (this->CheckArgument(arg, "--stop-time") && i < args.size() - 1) {
i++;
this->SetStopTime(args[i]);
}
if (this->CheckArgument(arg, "-C", "--build-config") &&
i < args.size() - 1) {
i++;
this->SetConfigType(args[i].c_str());
}
if (this->CheckArgument(arg, "--debug")) {
this->Debug = true;
this->ShowLineNumbers = true;
}
if (this->CheckArgument(arg, "--track") && i < args.size() - 1) {
i++;
this->SpecificTrack = args[i];
}
if (this->CheckArgument(arg, "--show-line-numbers")) {
this->ShowLineNumbers = true;
}
if (this->CheckArgument(arg, "--no-label-summary")) {
this->LabelSummary = false;
}
if (this->CheckArgument(arg, "--no-subproject-summary")) {
this->SubprojectSummary = false;
}
if (this->CheckArgument(arg, "-Q", "--quiet")) {
this->Quiet = true;
}
if (this->CheckArgument(arg, "-V", "--verbose")) {
this->Verbose = true;
}
if (this->CheckArgument(arg, "-VV", "--extra-verbose")) {
this->ExtraVerbose = true;
this->Verbose = true;
}
if (this->CheckArgument(arg, "--output-on-failure")) {
this->OutputTestOutputOnTestFailure = true;
}
if (this->CheckArgument(arg, "--test-output-size-passed") &&
i < args.size() - 1) {
i++;
long outputSize;
if (cmSystemTools::StringToLong(args[i].c_str(), &outputSize)) {
if (cmCTestTestHandler* pCTestTestHandler =
static_cast<cmCTestTestHandler*>(this->TestingHandlers["test"])) {
pCTestTestHandler->SetTestOutputSizePassed(int(outputSize));
}
} else {
cmCTestLog(this, WARNING,
"Invalid value for '--test-output-size-passed': " << args[i]
<< "\n");
}
}
if (this->CheckArgument(arg, "--test-output-size-failed") &&
i < args.size() - 1) {
i++;
long outputSize;
if (cmSystemTools::StringToLong(args[i].c_str(), &outputSize)) {
if (cmCTestTestHandler* pCTestTestHandler =
static_cast<cmCTestTestHandler*>(this->TestingHandlers["test"])) {
pCTestTestHandler->SetTestOutputSizeFailed(int(outputSize));
}
} else {
cmCTestLog(this, WARNING,
"Invalid value for '--test-output-size-failed': " << args[i]
<< "\n");
}
}
if (this->CheckArgument(arg, "-N", "--show-only")) {
this->ShowOnly = true;
}
if (this->CheckArgument(arg, "-O", "--output-log") && i < args.size() - 1) {
i++;
this->SetOutputLogFileName(args[i].c_str());
}
if (this->CheckArgument(arg, "--tomorrow-tag")) {
this->TomorrowTag = true;
}
if (this->CheckArgument(arg, "--force-new-ctest-process")) {
this->ForceNewCTestProcess = true;
}
if (this->CheckArgument(arg, "-W", "--max-width") && i < args.size() - 1) {
i++;
this->MaxTestNameWidth = atoi(args[i].c_str());
}
if (this->CheckArgument(arg, "--interactive-debug-mode") &&
i < args.size() - 1) {
i++;
this->InteractiveDebugMode = cmSystemTools::IsOn(args[i].c_str());
}
if (this->CheckArgument(arg, "--submit-index") && i < args.size() - 1) {
i++;
this->SubmitIndex = atoi(args[i].c_str());
if (this->SubmitIndex < 0) {
this->SubmitIndex = 0;
}
}
if (this->CheckArgument(arg, "--overwrite") && i < args.size() - 1) {
i++;
2014-02-04 02:20:33 +00:00
this->AddCTestConfigurationOverwrite(args[i]);
}
if (this->CheckArgument(arg, "-A", "--add-notes") && i < args.size() - 1) {
this->ProduceXML = true;
this->SetTest("Notes");
i++;
this->SetNotesFiles(args[i].c_str());
}
// options that control what tests are run
if (this->CheckArgument(arg, "-I", "--tests-information") &&
i < args.size() - 1) {
i++;
this->GetHandler("test")->SetPersistentOption("TestsToRunInformation",
args[i].c_str());
this->GetHandler("memcheck")
->SetPersistentOption("TestsToRunInformation", args[i].c_str());
}
if (this->CheckArgument(arg, "-U", "--union")) {
this->GetHandler("test")->SetPersistentOption("UseUnion", "true");
this->GetHandler("memcheck")->SetPersistentOption("UseUnion", "true");
}
if (this->CheckArgument(arg, "-R", "--tests-regex") && i < args.size() - 1) {
i++;
this->GetHandler("test")->SetPersistentOption("IncludeRegularExpression",
args[i].c_str());
this->GetHandler("memcheck")
->SetPersistentOption("IncludeRegularExpression", args[i].c_str());
}
if (this->CheckArgument(arg, "-L", "--label-regex") && i < args.size() - 1) {
i++;
this->GetHandler("test")->SetPersistentOption("LabelRegularExpression",
args[i].c_str());
this->GetHandler("memcheck")
->SetPersistentOption("LabelRegularExpression", args[i].c_str());
}
if (this->CheckArgument(arg, "-LE", "--label-exclude") &&
i < args.size() - 1) {
i++;
this->GetHandler("test")->SetPersistentOption(
"ExcludeLabelRegularExpression", args[i].c_str());
this->GetHandler("memcheck")
->SetPersistentOption("ExcludeLabelRegularExpression", args[i].c_str());
}
if (this->CheckArgument(arg, "-E", "--exclude-regex") &&
i < args.size() - 1) {
i++;
this->GetHandler("test")->SetPersistentOption("ExcludeRegularExpression",
args[i].c_str());
this->GetHandler("memcheck")
->SetPersistentOption("ExcludeRegularExpression", args[i].c_str());
}
if (this->CheckArgument(arg, "-FA", "--fixture-exclude-any") &&
i < args.size() - 1) {
i++;
this->GetHandler("test")->SetPersistentOption(
"ExcludeFixtureRegularExpression", args[i].c_str());
this->GetHandler("memcheck")
->SetPersistentOption("ExcludeFixtureRegularExpression",
args[i].c_str());
}
if (this->CheckArgument(arg, "-FS", "--fixture-exclude-setup") &&
i < args.size() - 1) {
i++;
this->GetHandler("test")->SetPersistentOption(
"ExcludeFixtureSetupRegularExpression", args[i].c_str());
this->GetHandler("memcheck")
->SetPersistentOption("ExcludeFixtureSetupRegularExpression",
args[i].c_str());
}
if (this->CheckArgument(arg, "-FC", "--fixture-exclude-cleanup") &&
i < args.size() - 1) {
i++;
this->GetHandler("test")->SetPersistentOption(
"ExcludeFixtureCleanupRegularExpression", args[i].c_str());
this->GetHandler("memcheck")
->SetPersistentOption("ExcludeFixtureCleanupRegularExpression",
args[i].c_str());
}
if (this->CheckArgument(arg, "--rerun-failed")) {
this->GetHandler("test")->SetPersistentOption("RerunFailed", "true");
this->GetHandler("memcheck")->SetPersistentOption("RerunFailed", "true");
}
return true;
}
// handle the -S -SR and -SP arguments
void cmCTest::HandleScriptArguments(size_t& i, std::vector<std::string>& args,
bool& SRArgumentSpecified)
{
std::string arg = args[i];
if (this->CheckArgument(arg, "-SP", "--script-new-process") &&
i < args.size() - 1) {
this->RunConfigurationScript = true;
i++;
cmCTestScriptHandler* ch =
static_cast<cmCTestScriptHandler*>(this->GetHandler("script"));
// -SR is an internal argument, -SP should be ignored when it is passed
if (!SRArgumentSpecified) {
ch->AddConfigurationScript(args[i].c_str(), false);
}
}
if (this->CheckArgument(arg, "-SR", "--script-run") && i < args.size() - 1) {
SRArgumentSpecified = true;
this->RunConfigurationScript = true;
i++;
cmCTestScriptHandler* ch =
static_cast<cmCTestScriptHandler*>(this->GetHandler("script"));
ch->AddConfigurationScript(args[i].c_str(), true);
}
if (this->CheckArgument(arg, "-S", "--script") && i < args.size() - 1) {
this->RunConfigurationScript = true;
i++;
cmCTestScriptHandler* ch =
static_cast<cmCTestScriptHandler*>(this->GetHandler("script"));
// -SR is an internal argument, -S should be ignored when it is passed
if (!SRArgumentSpecified) {
ch->AddConfigurationScript(args[i].c_str(), true);
}
}
}
bool cmCTest::AddVariableDefinition(const std::string& arg)
{
std::string name;
std::string value;
cmStateEnums::CacheEntryType type = cmStateEnums::UNINITIALIZED;
if (cmake::ParseCacheEntry(arg, name, value, type)) {
this->Definitions[name] = value;
return true;
}
return false;
}
// the main entry point of ctest, called from main
int cmCTest::Run(std::vector<std::string>& args, std::string* output)
{
const char* ctestExec = "ctest";
bool cmakeAndTest = false;
bool executeTests = true;
bool SRArgumentSpecified = false;
// copy the command line
this->InitialCommandLineArguments.insert(
this->InitialCommandLineArguments.end(), args.begin(), args.end());
// process the command line arguments
for (size_t i = 1; i < args.size(); ++i) {
// handle the simple commandline arguments
std::string errormsg;
if (!this->HandleCommandLineArguments(i, args, errormsg)) {
cmSystemTools::Error(errormsg.c_str());
return 1;
}
// handle the script arguments -S -SR -SP
this->HandleScriptArguments(i, args, SRArgumentSpecified);
// --dashboard: handle a request for a dashboard
std::string arg = args[i];
if (this->CheckArgument(arg, "-D", "--dashboard") && i < args.size() - 1) {
2006-03-15 16:02:08 +00:00
this->ProduceXML = true;
i++;
std::string targ = args[i];
// AddTestsForDashboard parses the dashboard type and converts it
// into the separate stages
if (!this->AddTestsForDashboardType(targ)) {
if (!this->AddVariableDefinition(targ)) {
this->ErrorMessageUnknownDashDValue(targ);
executeTests = false;
}
}
}
// If it's not exactly -D, but it starts with -D, then try to parse out
// a variable definition from it, same as CMake does. Unsuccessful
// attempts are simply ignored since previous ctest versions ignore
// this too. (As well as many other unknown command line args.)
//
if (arg != "-D" && cmSystemTools::StringStartsWith(arg.c_str(), "-D")) {
std::string input = arg.substr(2);
this->AddVariableDefinition(input);
}
// --test-action: calls SetTest(<stage>, /*report=*/ false) to enable
// the corresponding stage
if (!this->HandleTestActionArgument(ctestExec, i, args)) {
executeTests = false;
}
// --test-model: what type of test model
if (!this->HandleTestModelArgument(ctestExec, i, args)) {
executeTests = false;
}
// --extra-submit
if (this->CheckArgument(arg, "--extra-submit") && i < args.size() - 1) {
2006-03-15 16:02:08 +00:00
this->ProduceXML = true;
this->SetTest("Submit");
i++;
if (!this->SubmitExtraFiles(args[i].c_str())) {
return 0;
}
}
// --build-and-test options
if (this->CheckArgument(arg, "--build-and-test") && i < args.size() - 1) {
cmakeAndTest = true;
}
// --schedule-random
if (this->CheckArgument(arg, "--schedule-random")) {
this->ScheduleType = "Random";
}
// pass the argument to all the handlers as well, but i may no longer be
// set to what it was originally so I'm not sure this is working as
// intended
for (auto& handler : this->TestingHandlers) {
if (!handler.second->ProcessCommandLineArguments(arg, i, args)) {
2006-03-09 16:57:43 +00:00
cmCTestLog(this, ERROR_MESSAGE,
"Problem parsing command line arguments within a handler");
return 0;
}
}
} // the close of the for argument loop
// handle CTEST_PARALLEL_LEVEL environment variable
if (!this->ParallelLevelSetInCli) {
std::string parallel;
if (cmSystemTools::GetEnv("CTEST_PARALLEL_LEVEL", parallel)) {
int plevel = atoi(parallel.c_str());
this->SetParallelLevel(plevel);
}
}
2016-05-04 18:23:24 +00:00
// now what should cmake do? if --build-and-test was specified then
// we run the build and test handler and return
if (cmakeAndTest) {
return this->RunCMakeAndTest(output);
}
if (executeTests) {
return this->ExecuteTests();
}
return 1;
}
bool cmCTest::HandleTestActionArgument(const char* ctestExec, size_t& i,
const std::vector<std::string>& args)
{
bool success = true;
std::string arg = args[i];
if (this->CheckArgument(arg, "-T", "--test-action") &&
(i < args.size() - 1)) {
this->ProduceXML = true;
i++;
if (!this->SetTest(args[i].c_str(), false)) {
success = false;
cmCTestLog(this, ERROR_MESSAGE, "CTest -T called with incorrect option: "
<< args[i] << std::endl);
cmCTestLog(this, ERROR_MESSAGE, "Available options are:"
<< std::endl
<< " " << ctestExec << " -T all" << std::endl
<< " " << ctestExec << " -T start" << std::endl
<< " " << ctestExec << " -T update" << std::endl
<< " " << ctestExec << " -T configure" << std::endl
<< " " << ctestExec << " -T build" << std::endl
<< " " << ctestExec << " -T test" << std::endl
<< " " << ctestExec << " -T coverage" << std::endl
<< " " << ctestExec << " -T memcheck" << std::endl
<< " " << ctestExec << " -T notes" << std::endl
<< " " << ctestExec << " -T submit" << std::endl);
}
}
return success;
}
bool cmCTest::HandleTestModelArgument(const char* ctestExec, size_t& i,
const std::vector<std::string>& args)
{
bool success = true;
std::string arg = args[i];
if (this->CheckArgument(arg, "-M", "--test-model") &&
(i < args.size() - 1)) {
i++;
std::string const& str = args[i];
if (cmSystemTools::LowerCase(str) == "nightly") {
this->SetTestModel(cmCTest::NIGHTLY);
} else if (cmSystemTools::LowerCase(str) == "continuous") {
this->SetTestModel(cmCTest::CONTINUOUS);
} else if (cmSystemTools::LowerCase(str) == "experimental") {
this->SetTestModel(cmCTest::EXPERIMENTAL);
} else {
success = false;
cmCTestLog(this, ERROR_MESSAGE, "CTest -M called with incorrect option: "
<< str << std::endl);
cmCTestLog(this, ERROR_MESSAGE, "Available options are:"
<< std::endl
<< " " << ctestExec << " -M Continuous" << std::endl
<< " " << ctestExec << " -M Experimental" << std::endl
<< " " << ctestExec << " -M Nightly" << std::endl);
}
}
return success;
}
int cmCTest::ExecuteTests()
{
int res;
// call process directory
if (this->RunConfigurationScript) {
if (this->ExtraVerbose) {
cmCTestLog(this, OUTPUT, "* Extra verbosity turned on" << std::endl);
}
for (auto& handler : this->TestingHandlers) {
handler.second->SetVerbose(this->ExtraVerbose);
handler.second->SetSubmitIndex(this->SubmitIndex);
}
this->GetHandler("script")->SetVerbose(this->Verbose);
res = this->GetHandler("script")->ProcessHandler();
if (res != 0) {
cmCTestLog(this, DEBUG,
"running script failing returning: " << res << std::endl);
}
} else {
// What is this? -V seems to be the same as -VV,
// and Verbose is always on in this case
this->ExtraVerbose = this->Verbose;
this->Verbose = true;
for (auto& handler : this->TestingHandlers) {
handler.second->SetVerbose(this->Verbose);
handler.second->SetSubmitIndex(this->SubmitIndex);
}
std::string cwd = cmSystemTools::GetCurrentWorkingDirectory();
2017-08-22 21:42:36 +00:00
if (!this->Initialize(cwd.c_str(), nullptr)) {
res = 12;
cmCTestLog(this, ERROR_MESSAGE, "Problem initializing the dashboard."
<< std::endl);
} else {
res = this->ProcessSteps();
}
this->Finalize();
}
if (res != 0) {
cmCTestLog(this, DEBUG,
"Running a test(s) failed returning : " << res << std::endl);
}
return res;
}
int cmCTest::RunCMakeAndTest(std::string* output)
{
this->Verbose = true;
cmCTestBuildAndTestHandler* handler =
static_cast<cmCTestBuildAndTestHandler*>(this->GetHandler("buildtest"));
int retv = handler->ProcessHandler();
*output = handler->GetOutput();
#ifdef CMAKE_BUILD_WITH_CMAKE
cmDynamicLoader::FlushCache();
#endif
if (retv != 0) {
cmCTestLog(this, DEBUG, "build and test failing returning: " << retv
<< std::endl);
}
return retv;
}
void cmCTest::SetNotesFiles(const char* notes)
{
if (!notes) {
return;
}
2006-03-15 16:02:08 +00:00
this->NotesFiles = notes;
}
void cmCTest::SetStopTime(std::string const& time_str)
{
struct tm* lctime;
time_t current_time = time(nullptr);
lctime = gmtime(&current_time);
int gm_hour = lctime->tm_hour;
time_t gm_time = mktime(lctime);
lctime = localtime(&current_time);
int local_hour = lctime->tm_hour;
int tzone_offset = local_hour - gm_hour;
if (gm_time > current_time && gm_hour < local_hour) {
// this means gm_time is on the next day
tzone_offset -= 24;
} else if (gm_time < current_time && gm_hour > local_hour) {
// this means gm_time is on the previous day
tzone_offset += 24;
}
tzone_offset *= 100;
char buf[1024];
sprintf(buf, "%d%02d%02d %s %+05i", lctime->tm_year + 1900,
lctime->tm_mon + 1, lctime->tm_mday, time_str.c_str(), tzone_offset);
time_t stop_time = curl_getdate(buf, &current_time);
if (stop_time == -1) {
this->StopTime = std::chrono::system_clock::time_point();
return;
}
this->StopTime = std::chrono::system_clock::from_time_t(stop_time);
if (stop_time < current_time) {
this->StopTime += std::chrono::hours(24);
}
}
int cmCTest::ReadCustomConfigurationFileTree(const char* dir, cmMakefile* mf)
{
bool found = false;
2006-03-10 20:03:09 +00:00
VectorOfStrings dirs;
VectorOfStrings ndirs;
cmCTestLog(this, DEBUG, "* Read custom CTest configuration directory: "
<< dir << std::endl);
std::string fname = dir;
fname += "/CTestCustom.cmake";
cmCTestLog(this, DEBUG, "* Check for file: " << fname << std::endl);
if (cmSystemTools::FileExists(fname.c_str())) {
cmCTestLog(this, DEBUG, "* Read custom CTest configuration file: "
<< fname << std::endl);
bool erroroc = cmSystemTools::GetErrorOccuredFlag();
cmSystemTools::ResetErrorOccuredFlag();
if (!mf->ReadListFile(fname.c_str()) ||
cmSystemTools::GetErrorOccuredFlag()) {
cmCTestLog(this, ERROR_MESSAGE, "Problem reading custom configuration: "
<< fname << std::endl);
}
found = true;
if (erroroc) {
cmSystemTools::SetErrorOccured();
}
}
std::string rexpr = dir;
rexpr += "/CTestCustom.ctest";
cmCTestLog(this, DEBUG, "* Check for file: " << rexpr << std::endl);
if (!found && cmSystemTools::FileExists(rexpr.c_str())) {
cmsys::Glob gl;
gl.RecurseOn();
gl.FindFiles(rexpr);
std::vector<std::string>& files = gl.GetFiles();
std::vector<std::string>::iterator fileIt;
for (fileIt = files.begin(); fileIt != files.end(); ++fileIt) {
cmCTestLog(this, DEBUG, "* Read custom CTest configuration file: "
<< *fileIt << std::endl);
if (!mf->ReadListFile(fileIt->c_str()) ||
cmSystemTools::GetErrorOccuredFlag()) {
cmCTestLog(this, ERROR_MESSAGE,
"Problem reading custom configuration: " << *fileIt
<< std::endl);
}
}
found = true;
}
if (found) {
for (auto& handler : this->TestingHandlers) {
cmCTestLog(
this, DEBUG, "* Read custom CTest configuration vectors for handler: "
<< handler.first << " (" << handler.second << ")" << std::endl);
handler.second->PopulateCustomVectors(mf);
2005-01-27 20:54:47 +00:00
}
}
2006-03-09 16:57:43 +00:00
return 1;
}
void cmCTest::PopulateCustomVector(cmMakefile* mf, const std::string& def,
std::vector<std::string>& vec)
{
const char* dval = mf->GetDefinition(def);
if (!dval) {
return;
}
cmCTestLog(this, DEBUG, "PopulateCustomVector: " << def << std::endl);
vec.clear();
cmSystemTools::ExpandListArgument(dval, vec);
for (std::string const& it : vec) {
cmCTestLog(this, DEBUG, " -- " << it << std::endl);
}
}
void cmCTest::PopulateCustomInteger(cmMakefile* mf, const std::string& def,
int& val)
{
const char* dval = mf->GetDefinition(def);
if (!dval) {
return;
}
val = atoi(dval);
}
std::string cmCTest::GetShortPathToFile(const char* cfname)
{
const std::string& sourceDir = cmSystemTools::CollapseFullPath(
this->GetCTestConfiguration("SourceDirectory"));
const std::string& buildDir = cmSystemTools::CollapseFullPath(
this->GetCTestConfiguration("BuildDirectory"));
std::string fname = cmSystemTools::CollapseFullPath(cfname);
// Find relative paths to both directories
std::string srcRelpath =
cmSystemTools::RelativePath(sourceDir.c_str(), fname.c_str());
std::string bldRelpath =
cmSystemTools::RelativePath(buildDir.c_str(), fname.c_str());
// If any contains "." it is not parent directory
2017-05-30 19:37:46 +00:00
bool inSrc = srcRelpath.find("..") == std::string::npos;
bool inBld = bldRelpath.find("..") == std::string::npos;
// TODO: Handle files with .. in their name
2017-08-22 21:42:36 +00:00
std::string* res = nullptr;
if (inSrc && inBld) {
// If both have relative path with no dots, pick the shorter one
if (srcRelpath.size() < bldRelpath.size()) {
res = &srcRelpath;
} else {
res = &bldRelpath;
}
} else if (inSrc) {
res = &srcRelpath;
} else if (inBld) {
res = &bldRelpath;
}
std::string path;
if (!res) {
path = fname;
} else {
cmSystemTools::ConvertToUnixSlashes(*res);
path = "./" + *res;
if (path[path.size() - 1] == '/') {
path = path.substr(0, path.size() - 1);
2004-10-19 16:38:32 +00:00
}
}
cmsys::SystemTools::ReplaceString(path, ":", "_");
cmsys::SystemTools::ReplaceString(path, " ", "_");
2004-10-19 16:38:32 +00:00
return path;
}
std::string cmCTest::GetCTestConfiguration(const std::string& name)
2004-06-08 21:36:30 +00:00
{
if (this->CTestConfigurationOverwrites.find(name) !=
this->CTestConfigurationOverwrites.end()) {
2006-03-15 16:02:08 +00:00
return this->CTestConfigurationOverwrites[name];
}
2006-03-15 16:02:08 +00:00
return this->CTestConfiguration[name];
2004-06-08 21:36:30 +00:00
}
void cmCTest::EmptyCTestConfiguration()
{
2006-03-15 16:02:08 +00:00
this->CTestConfiguration.clear();
}
void cmCTest::SetCTestConfiguration(const char* name, const char* value,
bool suppress)
{
cmCTestOptionalLog(this, HANDLER_VERBOSE_OUTPUT, "SetCTestConfiguration:"
<< name << ":" << (value ? value : "(null)") << "\n",
suppress);
2007-06-14 17:05:09 +00:00
if (!name) {
return;
}
if (!value) {
2006-03-15 16:02:08 +00:00
this->CTestConfiguration.erase(name);
return;
}
2006-03-15 16:02:08 +00:00
this->CTestConfiguration[name] = value;
}
2004-09-09 12:41:05 +00:00
std::string cmCTest::GetCurrentTag()
{
2006-03-15 16:02:08 +00:00
return this->CurrentTag;
2004-04-16 18:52:13 +00:00
}
std::string cmCTest::GetBinaryDir()
2004-04-16 18:52:13 +00:00
{
2006-03-15 16:02:08 +00:00
return this->BinaryDir;
}
std::string const& cmCTest::GetConfigType()
{
2006-03-15 16:02:08 +00:00
return this->ConfigType;
}
2004-09-09 12:41:05 +00:00
bool cmCTest::GetShowOnly()
{
2006-03-15 16:02:08 +00:00
return this->ShowOnly;
}
int cmCTest::GetMaxTestNameWidth() const
{
return this->MaxTestNameWidth;
}
void cmCTest::SetProduceXML(bool v)
{
2006-03-15 16:02:08 +00:00
this->ProduceXML = v;
}
2004-09-09 12:41:05 +00:00
bool cmCTest::GetProduceXML()
{
2006-03-15 16:02:08 +00:00
return this->ProduceXML;
}
const char* cmCTest::GetSpecificTrack()
{
if (this->SpecificTrack.empty()) {
2017-08-22 21:42:36 +00:00
return nullptr;
}
return this->SpecificTrack.c_str();
}
void cmCTest::SetSpecificTrack(const char* track)
{
if (!track) {
this->SpecificTrack.clear();
return;
}
this->SpecificTrack = track;
}
void cmCTest::AddSubmitFile(Part part, const char* name)
{
this->Parts[part].SubmitFiles.push_back(name);
}
2014-02-04 02:20:33 +00:00
void cmCTest::AddCTestConfigurationOverwrite(const std::string& overStr)
{
size_t epos = overStr.find('=');
2017-05-30 19:37:46 +00:00
if (epos == std::string::npos) {
cmCTestLog(this, ERROR_MESSAGE,
"CTest configuration overwrite specified in the wrong format."
<< std::endl
<< "Valid format is: --overwrite key=value" << std::endl
<< "The specified was: --overwrite " << overStr << std::endl);
return;
}
std::string key = overStr.substr(0, epos);
std::string value = overStr.substr(epos + 1);
2006-03-15 16:02:08 +00:00
this->CTestConfigurationOverwrites[key] = value;
}
void cmCTest::SetConfigType(const char* ct)
{
this->ConfigType = ct ? ct : "";
cmSystemTools::ReplaceString(this->ConfigType, ".\\", "");
std::string confTypeEnv = "CMAKE_CONFIG_TYPE=" + this->ConfigType;
2014-11-23 10:05:50 +00:00
cmSystemTools::PutEnv(confTypeEnv);
}
bool cmCTest::SetCTestConfigurationFromCMakeVariable(
cmMakefile* mf, const char* dconfig, const std::string& cmake_var,
bool suppress)
{
const char* ctvar;
ctvar = mf->GetDefinition(cmake_var);
if (!ctvar) {
return false;
}
cmCTestOptionalLog(this, HANDLER_VERBOSE_OUTPUT,
"SetCTestConfigurationFromCMakeVariable:"
<< dconfig << ":" << cmake_var << std::endl,
suppress);
this->SetCTestConfiguration(dconfig, ctvar, suppress);
return true;
}
bool cmCTest::RunCommand(std::vector<std::string> const& args,
std::string* stdOut, std::string* stdErr, int* retVal,
const char* dir, cmDuration timeout,
Encoding encoding)
{
std::vector<const char*> argv;
argv.reserve(args.size() + 1);
for (std::string const& a : args) {
argv.push_back(a.c_str());
}
2017-08-22 21:42:36 +00:00
argv.push_back(nullptr);
stdOut->clear();
stdErr->clear();
cmsysProcess* cp = cmsysProcess_New();
cmsysProcess_SetCommand(cp, &*argv.begin());
cmsysProcess_SetWorkingDirectory(cp, dir);
if (cmSystemTools::GetRunCommandHideConsole()) {
cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
}
cmsysProcess_SetTimeout(cp, timeout.count());
cmsysProcess_Execute(cp);
2006-03-09 16:57:43 +00:00
std::vector<char> tempOutput;
std::vector<char> tempError;
char* data;
int length;
cmProcessOutput processOutput(encoding);
std::string strdata;
int res;
bool done = false;
while (!done) {
2017-08-22 21:42:36 +00:00
res = cmsysProcess_WaitForData(cp, &data, &length, nullptr);
switch (res) {
case cmsysProcess_Pipe_STDOUT:
tempOutput.insert(tempOutput.end(), data, data + length);
break;
case cmsysProcess_Pipe_STDERR:
tempError.insert(tempError.end(), data, data + length);
break;
default:
done = true;
}
if ((res == cmsysProcess_Pipe_STDOUT || res == cmsysProcess_Pipe_STDERR) &&
this->ExtraVerbose) {
processOutput.DecodeText(data, length, strdata);
cmSystemTools::Stdout(strdata.c_str(), strdata.size());
}
}
if (this->ExtraVerbose) {
processOutput.DecodeText(std::string(), strdata);
if (!strdata.empty()) {
cmSystemTools::Stdout(strdata.c_str(), strdata.size());
}
}
2006-03-09 16:57:43 +00:00
2017-08-22 21:42:36 +00:00
cmsysProcess_WaitForExit(cp, nullptr);
if (!tempOutput.empty()) {
processOutput.DecodeText(tempOutput, tempOutput);
2006-03-21 21:01:50 +00:00
stdOut->append(&*tempOutput.begin(), tempOutput.size());
}
if (!tempError.empty()) {
processOutput.DecodeText(tempError, tempError);
2006-03-21 21:01:50 +00:00
stdErr->append(&*tempError.begin(), tempError.size());
}
2006-03-09 16:57:43 +00:00
bool result = true;
if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exited) {
if (retVal) {
*retVal = cmsysProcess_GetExitValue(cp);
} else {
if (cmsysProcess_GetExitValue(cp) != 0) {
result = false;
}
}
} else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exception) {
const char* exception_str = cmsysProcess_GetExceptionString(cp);
cmCTestLog(this, ERROR_MESSAGE, exception_str << std::endl);
stdErr->append(exception_str, strlen(exception_str));
result = false;
} else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Error) {
const char* error_str = cmsysProcess_GetErrorString(cp);
cmCTestLog(this, ERROR_MESSAGE, error_str << std::endl);
stdErr->append(error_str, strlen(error_str));
result = false;
} else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Expired) {
const char* error_str = "Process terminated due to timeout\n";
cmCTestLog(this, ERROR_MESSAGE, error_str << std::endl);
stdErr->append(error_str, strlen(error_str));
result = false;
}
2006-03-09 16:57:43 +00:00
cmsysProcess_Delete(cp);
return result;
}
void cmCTest::SetOutputLogFileName(const char* name)
{
if (this->OutputLogFile) {
2006-03-15 16:02:08 +00:00
delete this->OutputLogFile;
2017-08-22 21:42:36 +00:00
this->OutputLogFile = nullptr;
}
if (name) {
2006-03-15 16:02:08 +00:00
this->OutputLogFile = new cmGeneratedFileStream(name);
}
}
static const char* cmCTestStringLogType[] = { "DEBUG",
"OUTPUT",
"HANDLER_OUTPUT",
"HANDLER_PROGRESS_OUTPUT",
"HANDLER_VERBOSE_OUTPUT",
"WARNING",
"ERROR_MESSAGE",
2017-08-22 21:42:36 +00:00
nullptr };
#define cmCTestLogOutputFileLine(stream) \
if (this->ShowLineNumbers) { \
(stream) << std::endl << file << ":" << line << " "; \
}
void cmCTest::InitStreams()
{
// By default we write output to the process output streams.
this->StreamOut = &std::cout;
this->StreamErr = &std::cerr;
}
void cmCTest::Log(int logType, const char* file, int line, const char* msg,
bool suppress)
{
if (!msg || !*msg) {
return;
}
if (suppress && logType != cmCTest::ERROR_MESSAGE) {
return;
}
if (logType == cmCTest::HANDLER_PROGRESS_OUTPUT &&
(this->Debug || this->ExtraVerbose)) {
return;
}
if (this->OutputLogFile) {
bool display = true;
if (logType == cmCTest::DEBUG && !this->Debug) {
display = false;
}
if (logType == cmCTest::HANDLER_VERBOSE_OUTPUT && !this->Debug &&
!this->ExtraVerbose) {
display = false;
}
if (display) {
2006-03-15 16:02:08 +00:00
cmCTestLogOutputFileLine(*this->OutputLogFile);
if (logType != this->OutputLogFileLastTag) {
2006-03-15 16:02:08 +00:00
*this->OutputLogFile << "[";
if (logType >= OTHER || logType < 0) {
2006-03-15 16:02:08 +00:00
*this->OutputLogFile << "OTHER";
} else {
2006-03-15 16:02:08 +00:00
*this->OutputLogFile << cmCTestStringLogType[logType];
}
*this->OutputLogFile << "] " << std::endl << std::flush;
}
2006-03-15 16:02:08 +00:00
*this->OutputLogFile << msg << std::flush;
if (logType != this->OutputLogFileLastTag) {
2006-03-15 16:02:08 +00:00
*this->OutputLogFile << std::endl << std::flush;
this->OutputLogFileLastTag = logType;
}
}
}
if (!this->Quiet) {
std::ostream& out = *this->StreamOut;
std::ostream& err = *this->StreamErr;
switch (logType) {
case DEBUG:
if (this->Debug) {
cmCTestLogOutputFileLine(out);
out << msg;
out.flush();
}
break;
case OUTPUT:
case HANDLER_OUTPUT:
if (this->Debug || this->Verbose) {
cmCTestLogOutputFileLine(out);
out << msg;
out.flush();
}
break;
case HANDLER_VERBOSE_OUTPUT:
if (this->Debug || this->ExtraVerbose) {
cmCTestLogOutputFileLine(out);
out << msg;
out.flush();
}
break;
case WARNING:
cmCTestLogOutputFileLine(err);
err << msg;
err.flush();
break;
case ERROR_MESSAGE:
cmCTestLogOutputFileLine(err);
err << msg;
err.flush();
cmSystemTools::SetErrorOccured();
break;
default:
cmCTestLogOutputFileLine(out);
out << msg;
out.flush();
}
}
}
cmDuration cmCTest::GetRemainingTimeAllowed()
{
if (!this->GetHandler("script")) {
return cmCTest::MaxDuration();
}
cmCTestScriptHandler* ch =
static_cast<cmCTestScriptHandler*>(this->GetHandler("script"));
return ch->GetRemainingTimeAllowed();
}
cmDuration cmCTest::MaxDuration()
{
return cmDuration(1.0e7);
}
void cmCTest::SetRunCurrentScript(bool value)
{
cmCTestScriptHandler* ch =
static_cast<cmCTestScriptHandler*>(this->GetHandler("script"));
ch->SetRunCurrentScript(value);
}
void cmCTest::OutputTestErrors(std::vector<char> const& process_output)
{
std::string test_outputs("\n*** Test Failed:\n");
if (!process_output.empty()) {
2009-07-10 13:12:39 +00:00
test_outputs.append(&*process_output.begin(), process_output.size());
}
cmCTestLog(this, HANDLER_OUTPUT, test_outputs << std::endl << std::flush);
}
bool cmCTest::CompressString(std::string& str)
{
int ret;
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
ret = deflateInit(&strm, -1); // default compression level
if (ret != Z_OK) {
return false;
}
unsigned char* in =
reinterpret_cast<unsigned char*>(const_cast<char*>(str.c_str()));
// zlib makes the guarantee that this is the maximum output size
int outSize =
static_cast<int>(static_cast<double>(str.size()) * 1.001 + 13.0);
std::vector<unsigned char> out(outSize);
2011-05-27 14:45:41 +00:00
strm.avail_in = static_cast<uInt>(str.size());
strm.next_in = in;
strm.avail_out = outSize;
strm.next_out = &out[0];
ret = deflate(&strm, Z_FINISH);
if (ret != Z_STREAM_END) {
cmCTestLog(this, ERROR_MESSAGE, "Error during gzip compression."
<< std::endl);
return false;
}
(void)deflateEnd(&strm);
// Now base64 encode the resulting binary string
std::vector<unsigned char> base64EncodedBuffer((outSize * 3) / 2);
size_t rlen =
cmsysBase64_Encode(&out[0], strm.total_out, &base64EncodedBuffer[0], 1);
str.assign(reinterpret_cast<char*>(&base64EncodedBuffer[0]), rlen);
return true;
}