CMake/Source/CTest/cmCTestTestHandler.cxx

2174 lines
71 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 22:01:08 +03:00
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
2004-09-09 16:41:05 +04:00
#include "cmCTestTestHandler.h"
2004-09-09 16:41:05 +04:00
#include "cmCTest.h"
#include "cmCTestBatchTestHandler.h"
#include "cmCTestMultiProcessHandler.h"
#include "cmCommand.h"
#include "cmGeneratedFileStream.h"
#include "cmGlobalGenerator.h"
#include "cmMakefile.h"
#include "cmState.h"
#include "cmSystemTools.h"
#include "cmXMLWriter.h"
#include "cm_auto_ptr.hxx"
#include "cm_utf8.h"
#include "cmake.h"
#include <algorithm>
#include <cmsys/Base64.h>
#include <cmsys/Directory.hxx>
#include <cmsys/FStream.hxx>
#include <cmsys/RegularExpression.hxx>
#include <functional>
#include <iomanip>
#include <iterator>
#include <set>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
2004-09-09 18:52:51 +04:00
class cmExecutionStatus;
2008-01-19 23:09:36 +03:00
class cmCTestSubdirCommand : public cmCommand
{
public:
/**
* This is a virtual constructor for the command.
*/
cmCommand* Clone() CM_OVERRIDE
{
2008-01-19 23:09:36 +03:00
cmCTestSubdirCommand* c = new cmCTestSubdirCommand;
c->TestHandler = this->TestHandler;
return c;
}
2008-01-19 23:09:36 +03:00
/**
* This is called when the command is first encountered in
* the CMakeLists.txt file.
*/
bool InitialPass(std::vector<std::string> const& args,
cmExecutionStatus& /*unused*/) CM_OVERRIDE;
2008-01-19 23:09:36 +03:00
/**
* The name of the command as specified in CMakeList.txt.
*/
std::string GetName() const CM_OVERRIDE { return "subdirs"; }
2008-01-19 23:09:36 +03:00
cmTypeMacro(cmCTestSubdirCommand, cmCommand);
cmCTestTestHandler* TestHandler;
};
bool cmCTestSubdirCommand::InitialPass(std::vector<std::string> const& args,
cmExecutionStatus& /*unused*/)
2008-01-19 23:09:36 +03:00
{
if (args.empty()) {
2008-01-19 23:09:36 +03:00
this->SetError("called with incorrect number of arguments");
return false;
}
2008-01-19 23:09:36 +03:00
std::vector<std::string>::const_iterator it;
std::string cwd = cmSystemTools::GetCurrentWorkingDirectory();
for (it = args.begin(); it != args.end(); ++it) {
std::string fname;
2008-01-19 23:09:36 +03:00
if (cmSystemTools::FileIsFullPath(it->c_str())) {
fname = *it;
} else {
fname = cwd;
fname += "/";
fname += *it;
}
if (!cmSystemTools::FileIsDirectory(fname)) {
2008-01-19 23:09:36 +03:00
// No subdirectory? So what...
continue;
}
cmSystemTools::ChangeDirectory(fname);
2008-01-19 23:09:36 +03:00
const char* testFilename;
if (cmSystemTools::FileExists("CTestTestfile.cmake")) {
2008-01-19 23:09:36 +03:00
// does the CTestTestfile.cmake exist ?
testFilename = "CTestTestfile.cmake";
} else if (cmSystemTools::FileExists("DartTestfile.txt")) {
// does the DartTestfile.txt exist ?
testFilename = "DartTestfile.txt";
} else {
2008-01-19 23:09:36 +03:00
// No CTestTestfile? Who cares...
continue;
}
2008-01-19 23:09:36 +03:00
fname += "/";
fname += testFilename;
bool readit = this->Makefile->ReadDependentFile(fname.c_str());
cmSystemTools::ChangeDirectory(cwd);
if (!readit) {
2008-01-19 23:09:36 +03:00
std::string m = "Could not find include file: ";
m += fname;
this->SetError(m);
2008-01-19 23:09:36 +03:00
return false;
}
}
cmSystemTools::ChangeDirectory(cwd);
2008-01-19 23:09:36 +03:00
return true;
}
class cmCTestAddSubdirectoryCommand : public cmCommand
{
public:
/**
* This is a virtual constructor for the command.
*/
cmCommand* Clone() CM_OVERRIDE
{
cmCTestAddSubdirectoryCommand* c = new cmCTestAddSubdirectoryCommand;
2006-03-10 23:03:09 +03:00
c->TestHandler = this->TestHandler;
return c;
}
/**
* This is called when the command is first encountered in
* the CMakeLists.txt file.
*/
bool InitialPass(std::vector<std::string> const& args,
cmExecutionStatus& /*unused*/) CM_OVERRIDE;
/**
* The name of the command as specified in CMakeList.txt.
*/
std::string GetName() const CM_OVERRIDE { return "add_subdirectory"; }
cmTypeMacro(cmCTestAddSubdirectoryCommand, cmCommand);
2006-03-10 23:03:09 +03:00
cmCTestTestHandler* TestHandler;
};
bool cmCTestAddSubdirectoryCommand::InitialPass(
std::vector<std::string> const& args, cmExecutionStatus& /*unused*/)
{
if (args.empty()) {
this->SetError("called with incorrect number of arguments");
return false;
}
2006-03-09 19:17:10 +03:00
std::string cwd = cmSystemTools::GetCurrentWorkingDirectory();
cmSystemTools::ChangeDirectory(cwd);
std::string fname = cwd;
fname += "/";
fname += args[0];
2006-03-09 19:17:10 +03:00
if (!cmSystemTools::FileExists(fname.c_str())) {
// No subdirectory? So what...
return true;
}
cmSystemTools::ChangeDirectory(fname);
const char* testFilename;
if (cmSystemTools::FileExists("CTestTestfile.cmake")) {
// does the CTestTestfile.cmake exist ?
testFilename = "CTestTestfile.cmake";
} else if (cmSystemTools::FileExists("DartTestfile.txt")) {
// does the DartTestfile.txt exist ?
testFilename = "DartTestfile.txt";
} else {
// No CTestTestfile? Who cares...
cmSystemTools::ChangeDirectory(cwd);
return true;
}
fname += "/";
fname += testFilename;
bool readit = this->Makefile->ReadDependentFile(fname.c_str());
cmSystemTools::ChangeDirectory(cwd);
if (!readit) {
std::string m = "Could not find include file: ";
m += fname;
this->SetError(m);
return false;
}
return true;
}
class cmCTestAddTestCommand : public cmCommand
{
public:
/**
* This is a virtual constructor for the command.
*/
cmCommand* Clone() CM_OVERRIDE
{
cmCTestAddTestCommand* c = new cmCTestAddTestCommand;
2006-03-10 23:03:09 +03:00
c->TestHandler = this->TestHandler;
return c;
}
/**
* This is called when the command is first encountered in
* the CMakeLists.txt file.
*/
bool InitialPass(std::vector<std::string> const& /*args*/,
cmExecutionStatus& /*unused*/) CM_OVERRIDE;
/**
* The name of the command as specified in CMakeList.txt.
*/
std::string GetName() const CM_OVERRIDE { return "add_test"; }
cmTypeMacro(cmCTestAddTestCommand, cmCommand);
2006-03-10 23:03:09 +03:00
cmCTestTestHandler* TestHandler;
};
bool cmCTestAddTestCommand::InitialPass(std::vector<std::string> const& args,
cmExecutionStatus& /*unused*/)
{
if (args.size() < 2) {
this->SetError("called with incorrect number of arguments");
return false;
}
return this->TestHandler->AddTest(args);
}
class cmCTestSetTestsPropertiesCommand : public cmCommand
{
public:
/**
* This is a virtual constructor for the command.
*/
cmCommand* Clone() CM_OVERRIDE
{
cmCTestSetTestsPropertiesCommand* c = new cmCTestSetTestsPropertiesCommand;
2006-03-10 23:03:09 +03:00
c->TestHandler = this->TestHandler;
return c;
}
/**
* This is called when the command is first encountered in
* the CMakeLists.txt file.
*/
bool InitialPass(std::vector<std::string> const& /*args*/,
cmExecutionStatus& /*unused*/) CM_OVERRIDE;
/**
* The name of the command as specified in CMakeList.txt.
*/
std::string GetName() const CM_OVERRIDE { return "set_tests_properties"; }
cmTypeMacro(cmCTestSetTestsPropertiesCommand, cmCommand);
2006-03-10 23:03:09 +03:00
cmCTestTestHandler* TestHandler;
};
bool cmCTestSetTestsPropertiesCommand::InitialPass(
std::vector<std::string> const& args, cmExecutionStatus& /*unused*/)
{
2006-03-10 23:03:09 +03:00
return this->TestHandler->SetTestsProperties(args);
}
2004-09-09 16:41:05 +04:00
// get the next number in a string with numbers separated by ,
// pos is the start of the search and pos2 is the end of the search
2006-03-09 19:17:10 +03:00
// pos becomes pos2 after a call to GetNextNumber.
2004-09-09 16:41:05 +04:00
// -1 is returned at the end of the list.
inline int GetNextNumber(std::string const& in, int& val,
2004-09-09 16:41:05 +04:00
std::string::size_type& pos,
std::string::size_type& pos2)
{
pos2 = in.find(',', pos);
if (pos2 != in.npos) {
if (pos2 - pos == 0) {
2004-09-09 16:41:05 +04:00
val = -1;
} else {
val = atoi(in.substr(pos, pos2 - pos).c_str());
}
pos = pos2 + 1;
2004-09-09 16:41:05 +04:00
return 1;
2016-08-18 21:04:21 +03:00
}
if (in.size() - pos == 0) {
val = -1;
} else {
2016-08-18 21:04:21 +03:00
val = atoi(in.substr(pos, in.size() - pos).c_str());
}
2016-08-18 21:04:21 +03:00
return 0;
2004-09-09 16:41:05 +04:00
}
// get the next number in a string with numbers separated by ,
// pos is the start of the search and pos2 is the end of the search
2006-03-09 19:17:10 +03:00
// pos becomes pos2 after a call to GetNextNumber.
2004-09-09 16:41:05 +04:00
// -1 is returned at the end of the list.
inline int GetNextRealNumber(std::string const& in, double& val,
2004-09-09 16:41:05 +04:00
std::string::size_type& pos,
std::string::size_type& pos2)
{
pos2 = in.find(',', pos);
if (pos2 != in.npos) {
if (pos2 - pos == 0) {
2004-09-09 16:41:05 +04:00
val = -1;
} else {
val = atof(in.substr(pos, pos2 - pos).c_str());
}
pos = pos2 + 1;
2004-09-09 16:41:05 +04:00
return 1;
2016-08-18 21:04:21 +03:00
}
if (in.size() - pos == 0) {
val = -1;
} else {
2016-08-18 21:04:21 +03:00
val = atof(in.substr(pos, in.size() - pos).c_str());
}
2016-08-18 21:04:21 +03:00
return 0;
2004-09-09 16:41:05 +04:00
}
cmCTestTestHandler::cmCTestTestHandler()
{
2006-03-10 23:03:09 +03:00
this->UseUnion = false;
2006-03-09 19:17:10 +03:00
this->UseIncludeLabelRegExpFlag = false;
this->UseExcludeLabelRegExpFlag = false;
this->UseIncludeRegExpFlag = false;
this->UseExcludeRegExpFlag = false;
this->UseExcludeRegExpFirst = false;
2006-03-10 23:03:09 +03:00
this->CustomMaximumPassedTestOutputSize = 1 * 1024;
this->CustomMaximumFailedTestOutputSize = 300 * 1024;
2006-03-09 19:17:10 +03:00
2006-03-10 23:03:09 +03:00
this->MemCheck = false;
2016-06-27 23:44:16 +03:00
this->LogFile = CM_NULLPTR;
2005-08-18 21:50:16 +04:00
2009-01-15 18:32:56 +03:00
// regex to detect <DartMeasurement>...</DartMeasurement>
this->DartStuff.compile("(<DartMeasurement.*/DartMeasurement[a-zA-Z]*>)");
2009-01-15 18:32:56 +03:00
// regex to detect each individual <DartMeasurement>...</DartMeasurement>
this->DartStuff1.compile(
"(<DartMeasurement[^<]*</DartMeasurement[a-zA-Z]*>)");
2004-09-09 16:41:05 +04:00
}
void cmCTestTestHandler::Initialize()
{
this->Superclass::Initialize();
2006-03-10 23:03:09 +03:00
this->ElapsedTestingTime = -1;
2006-03-10 23:03:09 +03:00
this->TestResults.clear();
2006-03-10 23:03:09 +03:00
this->CustomTestsIgnore.clear();
this->StartTest = "";
this->EndTest = "";
2006-03-10 23:03:09 +03:00
this->CustomPreTest.clear();
this->CustomPostTest.clear();
this->CustomMaximumPassedTestOutputSize = 1 * 1024;
this->CustomMaximumFailedTestOutputSize = 300 * 1024;
2006-03-10 23:03:09 +03:00
this->TestsToRun.clear();
this->UseIncludeLabelRegExpFlag = false;
this->UseExcludeLabelRegExpFlag = false;
2006-03-10 23:03:09 +03:00
this->UseIncludeRegExpFlag = false;
this->UseExcludeRegExpFlag = false;
this->UseExcludeRegExpFirst = false;
this->IncludeLabelRegularExpression = "";
this->ExcludeLabelRegularExpression = "";
2006-03-10 23:03:09 +03:00
this->IncludeRegExp = "";
this->ExcludeRegExp = "";
TestsToRunString = "";
2006-03-10 23:03:09 +03:00
this->UseUnion = false;
this->TestList.clear();
}
void cmCTestTestHandler::PopulateCustomVectors(cmMakefile* mf)
2004-09-09 16:41:05 +04:00
{
this->CTest->PopulateCustomVector(mf, "CTEST_CUSTOM_PRE_TEST",
this->CustomPreTest);
this->CTest->PopulateCustomVector(mf, "CTEST_CUSTOM_POST_TEST",
this->CustomPostTest);
this->CTest->PopulateCustomVector(mf, "CTEST_CUSTOM_TESTS_IGNORE",
this->CustomTestsIgnore);
this->CTest->PopulateCustomInteger(
mf, "CTEST_CUSTOM_MAXIMUM_PASSED_TEST_OUTPUT_SIZE",
this->CustomMaximumPassedTestOutputSize);
this->CTest->PopulateCustomInteger(
mf, "CTEST_CUSTOM_MAXIMUM_FAILED_TEST_OUTPUT_SIZE",
this->CustomMaximumFailedTestOutputSize);
2004-09-09 16:41:05 +04:00
}
2005-01-27 23:54:47 +03:00
int cmCTestTestHandler::PreProcessHandler()
2004-09-09 16:41:05 +04:00
{
if (!this->ExecuteCommands(this->CustomPreTest)) {
2006-03-10 23:03:09 +03:00
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Problem executing pre-test command(s)." << std::endl);
2005-01-27 23:54:47 +03:00
return 0;
}
2005-01-27 23:54:47 +03:00
return 1;
}
2004-09-09 16:41:05 +04:00
2005-01-27 23:54:47 +03:00
int cmCTestTestHandler::PostProcessHandler()
{
if (!this->ExecuteCommands(this->CustomPostTest)) {
2006-03-10 23:03:09 +03:00
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Problem executing post-test command(s)." << std::endl);
2005-01-27 23:54:47 +03:00
return 0;
}
2005-01-27 23:54:47 +03:00
return 1;
}
// clearly it would be nice if this were broken up into a few smaller
// functions and commented...
2005-01-27 23:54:47 +03:00
int cmCTestTestHandler::ProcessHandler()
{
2005-02-18 00:11:10 +03:00
// Update internal data structure from generic one
this->SetTestsToRunInformation(this->GetOption("TestsToRunInformation"));
this->SetUseUnion(cmSystemTools::IsOn(this->GetOption("UseUnion")));
if (cmSystemTools::IsOn(this->GetOption("ScheduleRandom"))) {
this->CTest->SetScheduleType("Random");
}
if (this->GetOption("ParallelLevel")) {
this->CTest->SetParallelLevel(atoi(this->GetOption("ParallelLevel")));
}
2005-02-18 00:11:10 +03:00
const char* val;
val = this->GetOption("LabelRegularExpression");
if (val) {
this->UseIncludeLabelRegExpFlag = true;
this->IncludeLabelRegExp = val;
}
val = this->GetOption("ExcludeLabelRegularExpression");
if (val) {
this->UseExcludeLabelRegExpFlag = true;
this->ExcludeLabelRegExp = val;
}
2005-02-18 00:11:10 +03:00
val = this->GetOption("IncludeRegularExpression");
if (val) {
2005-02-18 00:11:10 +03:00
this->UseIncludeRegExp();
this->SetIncludeRegExp(val);
}
2005-02-18 00:11:10 +03:00
val = this->GetOption("ExcludeRegularExpression");
if (val) {
2005-02-18 00:11:10 +03:00
this->UseExcludeRegExp();
this->SetExcludeRegExp(val);
}
this->SetRerunFailed(cmSystemTools::IsOn(this->GetOption("RerunFailed")));
2011-03-11 16:04:58 +03:00
2006-03-10 23:03:09 +03:00
this->TestResults.clear();
cmCTestOptionalLog(
this->CTest, HANDLER_OUTPUT, (this->MemCheck ? "Memory check" : "Test")
<< " project " << cmSystemTools::GetCurrentWorkingDirectory()
<< std::endl,
this->Quiet);
if (!this->PreProcessHandler()) {
2005-01-27 23:54:47 +03:00
return -1;
}
2004-09-09 16:41:05 +04:00
2005-08-18 21:50:16 +04:00
cmGeneratedFileStream mLogFile;
this->StartLogFile((this->MemCheck ? "DynamicAnalysis" : "Test"), mLogFile);
2006-03-10 23:03:09 +03:00
this->LogFile = &mLogFile;
2005-08-18 21:50:16 +04:00
std::vector<std::string> passed;
std::vector<std::string> failed;
2004-09-09 16:41:05 +04:00
int total;
// start the real time clock
double clock_start, clock_finish;
clock_start = cmSystemTools::GetTime();
2005-01-27 23:54:47 +03:00
this->ProcessDirectory(passed, failed);
2004-09-09 16:41:05 +04:00
clock_finish = cmSystemTools::GetTime();
2004-09-09 16:41:05 +04:00
total = int(passed.size()) + int(failed.size());
if (total == 0) {
if (!this->CTest->GetShowOnly() && !this->CTest->ShouldPrintLabels()) {
2006-03-10 23:03:09 +03:00
cmCTestLog(this->CTest, ERROR_MESSAGE, "No tests were found!!!"
<< std::endl);
2004-09-09 16:41:05 +04:00
}
} else {
if (this->HandlerVerbose && !passed.empty() &&
(this->UseIncludeRegExpFlag || this->UseExcludeRegExpFlag)) {
2015-02-17 22:23:56 +03:00
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, std::endl
<< "The following tests passed:" << std::endl,
this->Quiet);
for (std::vector<std::string>::iterator j = passed.begin();
j != passed.end(); ++j) {
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"\t" << *j << std::endl, this->Quiet);
2004-09-09 16:41:05 +04:00
}
}
2004-09-09 16:41:05 +04:00
2009-10-02 23:30:01 +04:00
float percent = float(passed.size()) * 100.0f / float(total);
if (!failed.empty() && percent > 99) {
2004-09-09 16:41:05 +04:00
percent = 99;
}
2009-10-02 23:30:01 +04:00
cmCTestLog(this->CTest, HANDLER_OUTPUT, std::endl
<< static_cast<int>(percent + .5f) << "% tests passed, "
<< failed.size() << " tests failed out of " << total
<< std::endl);
if (this->CTest->GetLabelSummary()) {
this->PrintLabelSummary();
}
char realBuf[1024];
sprintf(realBuf, "%6.2f sec", (double)(clock_finish - clock_start));
2015-02-17 22:23:56 +03:00
cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
"\nTotal Test time (real) = " << realBuf << "\n",
this->Quiet);
if (!failed.empty()) {
cmGeneratedFileStream ofs;
cmCTestLog(this->CTest, HANDLER_OUTPUT, std::endl
<< "The following tests FAILED:" << std::endl);
this->StartLogFile("TestsFailed", ofs);
2011-03-11 16:04:58 +03:00
typedef std::set<cmCTestTestHandler::cmCTestTestResult,
cmCTestTestResultLess>
SetOfTests;
2011-08-03 19:37:59 +04:00
SetOfTests resultsSet(this->TestResults.begin(),
this->TestResults.end());
for (SetOfTests::iterator ftit = resultsSet.begin();
ftit != resultsSet.end(); ++ftit) {
if (ftit->Status != cmCTestTestHandler::COMPLETED) {
ofs << ftit->TestCount << ":" << ftit->Name << std::endl;
cmCTestLog(
this->CTest, HANDLER_OUTPUT, "\t"
<< std::setw(3) << ftit->TestCount << " - " << ftit->Name << " ("
<< this->GetTestStatus(ftit->Status) << ")" << std::endl);
2004-09-09 16:41:05 +04:00
}
}
}
}
2004-09-09 16:41:05 +04:00
if (this->CTest->GetProduceXML()) {
cmGeneratedFileStream xmlfile;
if (!this->StartResultingXML(
(this->MemCheck ? cmCTest::PartMemCheck : cmCTest::PartTest),
(this->MemCheck ? "DynamicAnalysis" : "Test"), xmlfile)) {
2006-03-10 23:03:09 +03:00
cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot create "
<< (this->MemCheck ? "memory check" : "testing")
<< " XML file" << std::endl);
2016-06-27 23:44:16 +03:00
this->LogFile = CM_NULLPTR;
2004-09-09 16:41:05 +04:00
return 1;
}
cmXMLWriter xml(xmlfile);
this->GenerateDartOutput(xml);
}
2004-09-09 16:41:05 +04:00
if (!this->PostProcessHandler()) {
2016-06-27 23:44:16 +03:00
this->LogFile = CM_NULLPTR;
2005-01-27 23:54:47 +03:00
return -1;
}
2005-01-27 23:54:47 +03:00
if (!failed.empty()) {
2016-06-27 23:44:16 +03:00
this->LogFile = CM_NULLPTR;
2005-01-27 23:54:47 +03:00
return -1;
}
2016-06-27 23:44:16 +03:00
this->LogFile = CM_NULLPTR;
2005-01-27 23:54:47 +03:00
return 0;
2004-09-09 16:41:05 +04:00
}
void cmCTestTestHandler::PrintLabelSummary()
{
cmCTestTestHandler::ListOfTests::iterator it = this->TestList.begin();
std::map<std::string, double> labelTimes;
std::map<std::string, int> labelCounts;
std::set<std::string> labels;
// initialize maps
std::string::size_type maxlen = 0;
for (; it != this->TestList.end(); ++it) {
cmCTestTestProperties& p = *it;
if (!p.Labels.empty()) {
for (std::vector<std::string>::iterator l = p.Labels.begin();
l != p.Labels.end(); ++l) {
if ((*l).size() > maxlen) {
maxlen = (*l).size();
}
labels.insert(*l);
labelTimes[*l] = 0;
labelCounts[*l] = 0;
}
}
}
cmCTestTestHandler::TestResultsVector::iterator ri =
this->TestResults.begin();
// fill maps
for (; ri != this->TestResults.end(); ++ri) {
cmCTestTestResult& result = *ri;
cmCTestTestProperties& p = *result.Properties;
if (!p.Labels.empty()) {
for (std::vector<std::string>::iterator l = p.Labels.begin();
l != p.Labels.end(); ++l) {
labelTimes[*l] += result.ExecutionTime;
++labelCounts[*l];
}
}
}
2011-03-11 16:04:58 +03:00
// now print times
if (!labels.empty()) {
2015-02-17 22:23:56 +03:00
cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "\nLabel Time Summary:",
this->Quiet);
}
for (std::set<std::string>::const_iterator i = labels.begin();
i != labels.end(); ++i) {
std::string label = *i;
label.resize(maxlen + 3, ' ');
char buf[1024];
sprintf(buf, "%6.2f sec", labelTimes[*i]);
std::ostringstream labelCountStr;
labelCountStr << "(" << labelCounts[*i] << " test";
if (labelCounts[*i] > 1) {
labelCountStr << "s";
}
labelCountStr << ")";
2015-02-17 22:23:56 +03:00
cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "\n"
<< label << " = " << buf << " "
<< labelCountStr.str(),
this->Quiet);
if (this->LogFile) {
*this->LogFile << "\n" << *i << " = " << buf << "\n";
}
}
if (!labels.empty()) {
if (this->LogFile) {
*this->LogFile << "\n";
}
cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "\n", this->Quiet);
}
}
void cmCTestTestHandler::CheckLabelFilterInclude(cmCTestTestProperties& it)
{
// if not using Labels to filter then return
if (!this->UseIncludeLabelRegExpFlag) {
return;
}
// if there are no labels and we are filtering by labels
// then exclude the test as it does not have the label
if (it.Labels.empty()) {
it.IsInBasedOnREOptions = false;
return;
}
// check to see if the label regular expression matches
bool found = false; // assume it does not match
// loop over all labels and look for match
for (std::vector<std::string>::iterator l = it.Labels.begin();
l != it.Labels.end(); ++l) {
if (this->IncludeLabelRegularExpression.find(*l)) {
found = true;
}
}
// if no match was found, exclude the test
if (!found) {
it.IsInBasedOnREOptions = false;
}
}
void cmCTestTestHandler::CheckLabelFilterExclude(cmCTestTestProperties& it)
{
// if not using Labels to filter then return
if (!this->UseExcludeLabelRegExpFlag) {
return;
}
// if there are no labels and we are excluding by labels
// then do nothing as a no label can not be a match
if (it.Labels.empty()) {
return;
}
// check to see if the label regular expression matches
bool found = false; // assume it does not match
// loop over all labels and look for match
for (std::vector<std::string>::iterator l = it.Labels.begin();
l != it.Labels.end(); ++l) {
if (this->ExcludeLabelRegularExpression.find(*l)) {
found = true;
}
}
// if match was found, exclude the test
if (found) {
it.IsInBasedOnREOptions = false;
}
}
void cmCTestTestHandler::CheckLabelFilter(cmCTestTestProperties& it)
{
this->CheckLabelFilterInclude(it);
this->CheckLabelFilterExclude(it);
}
2008-07-03 17:31:33 +04:00
void cmCTestTestHandler::ComputeTestList()
2004-09-09 16:41:05 +04:00
{
2008-07-03 17:31:33 +04:00
this->TestList.clear(); // clear list of test
this->GetListOfTests();
if (this->RerunFailed) {
this->ComputeTestListForRerunFailed();
return;
}
2006-03-10 23:03:09 +03:00
cmCTestTestHandler::ListOfTests::size_type tmsize = this->TestList.size();
// how many tests are in based on RegExp?
int inREcnt = 0;
2006-03-10 23:03:09 +03:00
cmCTestTestHandler::ListOfTests::iterator it;
for (it = this->TestList.begin(); it != this->TestList.end(); it++) {
this->CheckLabelFilter(*it);
if (it->IsInBasedOnREOptions) {
inREcnt++;
}
}
// expand the test list based on the union flag
if (this->UseUnion) {
this->ExpandTestsToRunInformation((int)tmsize);
} else {
this->ExpandTestsToRunInformation(inREcnt);
}
2008-07-03 17:31:33 +04:00
// Now create a final list of tests to run
2004-09-09 16:41:05 +04:00
int cnt = 0;
inREcnt = 0;
2011-03-11 16:04:58 +03:00
std::string last_directory = "";
2008-07-03 17:31:33 +04:00
ListOfTests finalList;
for (it = this->TestList.begin(); it != this->TestList.end(); it++) {
cnt++;
if (it->IsInBasedOnREOptions) {
inREcnt++;
}
if (this->UseUnion) {
// if it is not in the list and not in the regexp then skip
if ((!this->TestsToRun.empty() &&
std::find(this->TestsToRun.begin(), this->TestsToRun.end(), cnt) ==
this->TestsToRun.end()) &&
!it->IsInBasedOnREOptions) {
continue;
2004-09-09 16:41:05 +04:00
}
} else {
// is this test in the list of tests to run? If not then skip it
if ((!this->TestsToRun.empty() &&
std::find(this->TestsToRun.begin(), this->TestsToRun.end(),
inREcnt) == this->TestsToRun.end()) ||
!it->IsInBasedOnREOptions) {
continue;
}
2008-07-03 17:31:33 +04:00
}
it->Index = cnt; // save the index into the test list for this test
finalList.push_back(*it);
}
UpdateForFixtures(finalList);
2008-07-03 17:31:33 +04:00
// Save the total number of tests before exclusions
this->TotalNumberOfTests = this->TestList.size();
// Set the TestList to the final list of all test
this->TestList = finalList;
this->UpdateMaxTestNameWidth();
}
void cmCTestTestHandler::ComputeTestListForRerunFailed()
{
this->ExpandTestsToRunInformationForRerunFailed();
cmCTestTestHandler::ListOfTests::iterator it;
ListOfTests finalList;
int cnt = 0;
for (it = this->TestList.begin(); it != this->TestList.end(); it++) {
cnt++;
// if this test is not in our list of tests to run, then skip it.
if ((!this->TestsToRun.empty() &&
std::find(this->TestsToRun.begin(), this->TestsToRun.end(), cnt) ==
this->TestsToRun.end())) {
continue;
}
it->Index = cnt;
finalList.push_back(*it);
}
UpdateForFixtures(finalList);
// Save the total number of tests before exclusions
this->TotalNumberOfTests = this->TestList.size();
// Set the TestList to the list of failed tests to rerun
this->TestList = finalList;
this->UpdateMaxTestNameWidth();
}
void cmCTestTestHandler::UpdateForFixtures(ListOfTests& tests) const
{
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Updating test list for fixtures" << std::endl,
this->Quiet);
// Prepare some maps to help us find setup and cleanup tests for
// any given fixture
typedef std::set<ListOfTests::const_iterator> TestIteratorSet;
typedef std::map<std::string, TestIteratorSet> FixtureDependencies;
FixtureDependencies fixtureSetups;
FixtureDependencies fixtureDeps;
for (ListOfTests::const_iterator it = this->TestList.begin();
it != this->TestList.end(); ++it) {
const cmCTestTestProperties& p = *it;
const std::set<std::string>& setups = p.FixturesSetup;
for (std::set<std::string>::const_iterator depsIt = setups.begin();
depsIt != setups.end(); ++depsIt) {
fixtureSetups[*depsIt].insert(it);
fixtureDeps[*depsIt].insert(it);
}
const std::set<std::string>& cleanups = p.FixturesCleanup;
for (std::set<std::string>::const_iterator depsIt = cleanups.begin();
depsIt != cleanups.end(); ++depsIt) {
fixtureDeps[*depsIt].insert(it);
}
}
// Prepare fast lookup of tests already included in our list of tests
std::set<std::string> addedTests;
for (ListOfTests::const_iterator it = tests.begin(); it != tests.end();
++it) {
const cmCTestTestProperties& p = *it;
addedTests.insert(p.Name);
}
// This is a lookup of fixture name to a list of indices into the
// final tests array for tests which require that fixture. It is
// needed at the end to populate dependencies of the cleanup tests
// in our final list of tests.
std::map<std::string, std::vector<size_t> > fixtureRequirements;
// Use integer index for iteration because we append to
// the tests vector as we go
size_t fixtureTestsAdded = 0;
std::set<std::string> addedFixtures;
for (size_t i = 0; i < tests.size(); ++i) {
if (tests[i].FixturesRequired.empty()) {
continue;
}
// Must copy the set of fixtures because we may invalidate
// the tests array by appending to it
const std::set<std::string> fixtures = tests[i].FixturesRequired;
for (std::set<std::string>::const_iterator fixturesIt = fixtures.begin();
fixturesIt != fixtures.end(); ++fixturesIt) {
const std::string& requiredFixtureName = *fixturesIt;
if (requiredFixtureName.empty()) {
continue;
}
fixtureRequirements[requiredFixtureName].push_back(i);
// Add dependencies to this test for all of the setup tests
// associated with the required fixture. If any of those setup
// tests fail, this test should not run. We make the fixture's
// cleanup tests depend on this test case later.
FixtureDependencies::const_iterator setupIt =
fixtureSetups.find(requiredFixtureName);
if (setupIt != fixtureSetups.end()) {
for (TestIteratorSet::const_iterator sIt = setupIt->second.begin();
sIt != setupIt->second.end(); ++sIt) {
const std::string& setupTestName = (**sIt).Name;
tests[i].RequireSuccessDepends.insert(setupTestName);
if (std::find(tests[i].Depends.begin(), tests[i].Depends.end(),
setupTestName) == tests[i].Depends.end()) {
tests[i].Depends.push_back(setupTestName);
}
}
}
// Append any fixture setup/cleanup tests to our test list if they
// are not already in it (they could have been in the original
// set of tests passed to us at the outset or have already been
// added from a previously checked test). A fixture isn't required
// to have setup/cleanup tests.
if (!addedFixtures.insert(requiredFixtureName).second) {
// Already added this fixture
continue;
}
FixtureDependencies::const_iterator fixtureIt =
fixtureDeps.find(requiredFixtureName);
if (fixtureIt == fixtureDeps.end()) {
// No setup or cleanup tests for this fixture
continue;
}
const TestIteratorSet& testIters = fixtureIt->second;
for (TestIteratorSet::const_iterator depsIt = testIters.begin();
depsIt != testIters.end(); ++depsIt) {
ListOfTests::const_iterator lotIt = *depsIt;
const cmCTestTestProperties& p = *lotIt;
if (!addedTests.insert(p.Name).second) {
// Already have p in our test list
continue;
}
// This is a test not yet in our list, so add it and
// update its index to reflect where it was in the original
// full list of all tests (needed to track individual tests
// across ctest runs for re-run failed, etc.)
tests.push_back(p);
tests.back().Index =
1 + static_cast<int>(std::distance(this->TestList.begin(), lotIt));
++fixtureTestsAdded;
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Added test "
<< p.Name << " required by fixture "
<< requiredFixtureName << std::endl,
this->Quiet);
}
}
}
// Now that we have the final list of tests, we can update all cleanup
// tests to depend on those tests which require that fixture
for (ListOfTests::iterator tIt = tests.begin(); tIt != tests.end(); ++tIt) {
cmCTestTestProperties& p = *tIt;
const std::set<std::string>& cleanups = p.FixturesCleanup;
for (std::set<std::string>::const_iterator fIt = cleanups.begin();
fIt != cleanups.end(); ++fIt) {
const std::string& fixture = *fIt;
std::map<std::string, std::vector<size_t> >::const_iterator cIt =
fixtureRequirements.find(fixture);
if (cIt == fixtureRequirements.end()) {
// No test cases require the fixture this cleanup test is for.
// This cleanup test must have been part of the original test
// list passed in (which is not an error)
continue;
}
const std::vector<size_t>& indices = cIt->second;
for (std::vector<size_t>::const_iterator indexIt = indices.begin();
indexIt != indices.end(); ++indexIt) {
const std::string& reqTestName = tests[*indexIt].Name;
if (std::find(p.Depends.begin(), p.Depends.end(), reqTestName) ==
p.Depends.end()) {
p.Depends.push_back(reqTestName);
}
}
}
}
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Added "
<< fixtureTestsAdded
<< " tests to meet fixture requirements" << std::endl,
this->Quiet);
}
void cmCTestTestHandler::UpdateMaxTestNameWidth()
{
std::string::size_type max = this->CTest->GetMaxTestNameWidth();
for (cmCTestTestHandler::ListOfTests::iterator it = this->TestList.begin();
it != this->TestList.end(); it++) {
cmCTestTestProperties& p = *it;
if (max < p.Name.size()) {
max = p.Name.size();
}
}
if (static_cast<std::string::size_type>(
this->CTest->GetMaxTestNameWidth()) != max) {
2009-07-19 21:40:38 +04:00
this->CTest->SetMaxTestNameWidth(static_cast<int>(max));
}
2008-07-03 17:31:33 +04:00
}
2011-03-11 16:04:58 +03:00
bool cmCTestTestHandler::GetValue(const char* tag, int& value,
std::istream& fin)
2008-07-03 17:31:33 +04:00
{
std::string line;
2008-07-04 18:10:30 +04:00
bool ret = true;
2008-07-03 17:31:33 +04:00
cmSystemTools::GetLineFromStream(fin, line);
if (line == tag) {
2008-07-03 17:31:33 +04:00
fin >> value;
2008-07-04 18:10:30 +04:00
ret = cmSystemTools::GetLineFromStream(fin, line); // read blank line
} else {
cmCTestLog(this->CTest, ERROR_MESSAGE, "parse error: missing tag: "
<< tag << " found [" << line << "]" << std::endl);
2008-07-04 18:10:30 +04:00
ret = false;
}
2008-07-04 18:10:30 +04:00
return ret;
2008-07-03 17:31:33 +04:00
}
bool cmCTestTestHandler::GetValue(const char* tag, double& value,
std::istream& fin)
2008-07-03 17:31:33 +04:00
{
std::string line;
cmSystemTools::GetLineFromStream(fin, line);
2008-07-04 18:10:30 +04:00
bool ret = true;
if (line == tag) {
2008-07-03 17:31:33 +04:00
fin >> value;
2008-07-04 18:10:30 +04:00
ret = cmSystemTools::GetLineFromStream(fin, line); // read blank line
} else {
cmCTestLog(this->CTest, ERROR_MESSAGE, "parse error: missing tag: "
<< tag << " found [" << line << "]" << std::endl);
2008-07-04 18:10:30 +04:00
ret = false;
}
2008-07-04 18:10:30 +04:00
return ret;
2008-07-03 17:31:33 +04:00
}
bool cmCTestTestHandler::GetValue(const char* tag, bool& value,
std::istream& fin)
2008-07-03 17:31:33 +04:00
{
std::string line;
cmSystemTools::GetLineFromStream(fin, line);
2008-07-04 18:10:30 +04:00
bool ret = true;
if (line == tag) {
#ifdef __HAIKU__
int tmp = 0;
fin >> tmp;
value = false;
if (tmp) {
value = true;
}
#else
2008-07-03 17:31:33 +04:00
fin >> value;
#endif
2008-07-04 18:10:30 +04:00
ret = cmSystemTools::GetLineFromStream(fin, line); // read blank line
} else {
cmCTestLog(this->CTest, ERROR_MESSAGE, "parse error: missing tag: "
<< tag << " found [" << line << "]" << std::endl);
2008-07-04 18:10:30 +04:00
ret = false;
}
2008-07-04 18:10:30 +04:00
return ret;
2008-07-03 17:31:33 +04:00
}
bool cmCTestTestHandler::GetValue(const char* tag, size_t& value,
std::istream& fin)
2008-07-03 17:31:33 +04:00
{
std::string line;
cmSystemTools::GetLineFromStream(fin, line);
2008-07-04 18:10:30 +04:00
bool ret = true;
if (line == tag) {
2008-07-03 17:31:33 +04:00
fin >> value;
2008-07-04 18:10:30 +04:00
ret = cmSystemTools::GetLineFromStream(fin, line); // read blank line
} else {
cmCTestLog(this->CTest, ERROR_MESSAGE, "parse error: missing tag: "
<< tag << " found [" << line << "]" << std::endl);
2008-07-04 18:10:30 +04:00
ret = false;
}
2008-07-04 18:10:30 +04:00
return ret;
2008-07-03 17:31:33 +04:00
}
bool cmCTestTestHandler::GetValue(const char* tag, std::string& value,
std::istream& fin)
2008-07-03 17:31:33 +04:00
{
std::string line;
cmSystemTools::GetLineFromStream(fin, line);
2008-07-04 18:10:30 +04:00
bool ret = true;
if (line == tag) {
ret = cmSystemTools::GetLineFromStream(fin, value);
} else {
cmCTestLog(this->CTest, ERROR_MESSAGE, "parse error: missing tag: "
<< tag << " found [" << line << "]" << std::endl);
2008-07-04 18:10:30 +04:00
ret = false;
}
2008-07-04 18:10:30 +04:00
return ret;
2008-07-03 17:31:33 +04:00
}
void cmCTestTestHandler::ProcessDirectory(std::vector<std::string>& passed,
std::vector<std::string>& failed)
2008-07-03 17:31:33 +04:00
{
this->ComputeTestList();
2009-08-31 18:28:39 +04:00
this->StartTest = this->CTest->CurrentTime();
this->StartTestTime = static_cast<unsigned int>(cmSystemTools::GetTime());
double elapsed_time_start = cmSystemTools::GetTime();
cmCTestMultiProcessHandler* parallel = this->CTest->GetBatchJobs()
? new cmCTestBatchTestHandler
: new cmCTestMultiProcessHandler;
parallel->SetCTest(this->CTest);
parallel->SetParallelLevel(this->CTest->GetParallelLevel());
parallel->SetTestHandler(this);
2015-02-17 22:23:56 +03:00
parallel->SetQuiet(this->Quiet);
if (this->TestLoad > 0) {
parallel->SetTestLoad(this->TestLoad);
} else {
parallel->SetTestLoad(this->CTest->GetTestLoad());
}
*this->LogFile
<< "Start testing: " << this->CTest->CurrentTime() << std::endl
<< "----------------------------------------------------------"
<< std::endl;
cmCTestMultiProcessHandler::TestMap tests;
cmCTestMultiProcessHandler::PropertiesMap properties;
2011-03-11 16:04:58 +03:00
bool randomSchedule = this->CTest->GetScheduleType() == "Random";
if (randomSchedule) {
2016-06-27 23:44:16 +03:00
srand((unsigned)time(CM_NULLPTR));
}
2008-07-03 17:31:33 +04:00
for (ListOfTests::iterator it = this->TestList.begin();
it != this->TestList.end(); ++it) {
2008-07-03 17:31:33 +04:00
cmCTestTestProperties& p = *it;
cmCTestMultiProcessHandler::TestSet depends;
if (randomSchedule) {
p.Cost = static_cast<float>(rand());
}
if (p.Timeout == 0 && this->CTest->GetGlobalTimeout() != 0) {
p.Timeout = this->CTest->GetGlobalTimeout();
}
if (!p.Depends.empty()) {
for (std::vector<std::string>::iterator i = p.Depends.begin();
i != p.Depends.end(); ++i) {
for (ListOfTests::iterator it2 = this->TestList.begin();
it2 != this->TestList.end(); ++it2) {
if (it2->Name == *i) {
2008-07-03 17:31:33 +04:00
depends.insert(it2->Index);
break; // break out of test loop as name can only match 1
}
}
}
}
2008-07-03 17:31:33 +04:00
tests[it->Index] = depends;
properties[it->Index] = &*it;
}
parallel->SetTests(tests, properties);
parallel->SetPassFailVectors(&passed, &failed);
2008-07-03 17:31:33 +04:00
this->TestResults.clear();
parallel->SetTestResults(&this->TestResults);
if (this->CTest->ShouldPrintLabels()) {
parallel->PrintLabels();
} else if (this->CTest->GetShowOnly()) {
parallel->PrintTestList();
} else {
parallel->RunTests();
}
delete parallel;
2009-08-31 18:28:39 +04:00
this->EndTest = this->CTest->CurrentTime();
this->EndTestTime = static_cast<unsigned int>(cmSystemTools::GetTime());
this->ElapsedTestingTime = cmSystemTools::GetTime() - elapsed_time_start;
*this->LogFile << "End testing: " << this->CTest->CurrentTime() << std::endl;
2004-09-09 16:41:05 +04:00
}
void cmCTestTestHandler::GenerateTestCommand(
std::vector<std::string>& /*unused*/, int /*unused*/)
2004-09-09 16:41:05 +04:00
{
}
void cmCTestTestHandler::GenerateDartOutput(cmXMLWriter& xml)
2004-09-09 16:41:05 +04:00
{
if (!this->CTest->GetProduceXML()) {
2004-09-09 16:41:05 +04:00
return;
}
2004-09-09 16:41:05 +04:00
this->CTest->StartXML(xml, this->AppendXML);
xml.StartElement("Testing");
xml.Element("StartDateTime", this->StartTest);
xml.Element("StartTestTime", this->StartTestTime);
xml.StartElement("TestList");
2006-03-10 23:03:09 +03:00
cmCTestTestHandler::TestResultsVector::size_type cc;
for (cc = 0; cc < this->TestResults.size(); cc++) {
cmCTestTestResult* result = &this->TestResults[cc];
2006-03-10 23:03:09 +03:00
std::string testPath = result->Path + "/" + result->Name;
xml.Element("Test", this->CTest->GetShortPathToFile(testPath.c_str()));
}
xml.EndElement(); // TestList
for (cc = 0; cc < this->TestResults.size(); cc++) {
cmCTestTestResult* result = &this->TestResults[cc];
this->WriteTestResultHeader(xml, result);
xml.StartElement("Results");
if (result->Status != cmCTestTestHandler::NOT_RUN) {
if (result->Status != cmCTestTestHandler::COMPLETED ||
result->ReturnValue) {
xml.StartElement("NamedMeasurement");
xml.Attribute("type", "text/string");
xml.Attribute("name", "Exit Code");
xml.Element("Value", this->GetTestStatus(result->Status));
xml.EndElement(); // NamedMeasurement
xml.StartElement("NamedMeasurement");
xml.Attribute("type", "text/string");
xml.Attribute("name", "Exit Value");
xml.Element("Value", result->ReturnValue);
xml.EndElement(); // NamedMeasurement
}
this->GenerateRegressionImages(xml, result->DartString);
xml.StartElement("NamedMeasurement");
xml.Attribute("type", "numeric/double");
xml.Attribute("name", "Execution Time");
xml.Element("Value", result->ExecutionTime);
xml.EndElement(); // NamedMeasurement
if (!result->Reason.empty()) {
const char* reasonType = "Pass Reason";
if (result->Status != cmCTestTestHandler::COMPLETED &&
result->Status != cmCTestTestHandler::NOT_RUN) {
reasonType = "Fail Reason";
}
xml.StartElement("NamedMeasurement");
xml.Attribute("type", "text/string");
xml.Attribute("name", reasonType);
xml.Element("Value", result->Reason);
xml.EndElement(); // NamedMeasurement
}
xml.StartElement("NamedMeasurement");
xml.Attribute("type", "text/string");
xml.Attribute("name", "Completion Status");
xml.Element("Value", result->CompletionStatus);
xml.EndElement(); // NamedMeasurement
}
xml.StartElement("NamedMeasurement");
xml.Attribute("type", "text/string");
xml.Attribute("name", "Command Line");
xml.Element("Value", result->FullCommandLine);
xml.EndElement(); // NamedMeasurement
std::map<std::string, std::string>::iterator measureIt;
for (measureIt = result->Properties->Measurements.begin();
measureIt != result->Properties->Measurements.end(); ++measureIt) {
xml.StartElement("NamedMeasurement");
xml.Attribute("type", "text/string");
xml.Attribute("name", measureIt->first);
xml.Element("Value", measureIt->second);
xml.EndElement(); // NamedMeasurement
}
xml.StartElement("Measurement");
xml.StartElement("Value");
if (result->CompressOutput) {
xml.Attribute("encoding", "base64");
xml.Attribute("compression", "gzip");
}
xml.Content(result->Output);
xml.EndElement(); // Value
xml.EndElement(); // Measurement
xml.EndElement(); // Results
this->AttachFiles(xml, result);
this->WriteTestResultFooter(xml, result);
}
xml.Element("EndDateTime", this->EndTest);
xml.Element("EndTestTime", this->EndTestTime);
xml.Element("ElapsedMinutes",
static_cast<int>(this->ElapsedTestingTime / 6) / 10.0);
xml.EndElement(); // Testing
this->CTest->EndXML(xml);
2004-09-09 16:41:05 +04:00
}
void cmCTestTestHandler::WriteTestResultHeader(cmXMLWriter& xml,
cmCTestTestResult* result)
{
xml.StartElement("Test");
if (result->Status == cmCTestTestHandler::COMPLETED) {
xml.Attribute("Status", "passed");
} else if (result->Status == cmCTestTestHandler::NOT_RUN) {
xml.Attribute("Status", "notrun");
} else {
xml.Attribute("Status", "failed");
}
std::string testPath = result->Path + "/" + result->Name;
xml.Element("Name", result->Name);
xml.Element("Path", this->CTest->GetShortPathToFile(result->Path.c_str()));
xml.Element("FullName", this->CTest->GetShortPathToFile(testPath.c_str()));
xml.Element("FullCommandLine", result->FullCommandLine);
}
void cmCTestTestHandler::WriteTestResultFooter(cmXMLWriter& xml,
cmCTestTestResult* result)
{
if (!result->Properties->Labels.empty()) {
xml.StartElement("Labels");
std::vector<std::string> const& labels = result->Properties->Labels;
for (std::vector<std::string>::const_iterator li = labels.begin();
li != labels.end(); ++li) {
xml.Element("Label", *li);
}
xml.EndElement(); // Labels
}
xml.EndElement(); // Test
}
void cmCTestTestHandler::AttachFiles(cmXMLWriter& xml,
cmCTestTestResult* result)
{
if (result->Status != cmCTestTestHandler::COMPLETED &&
!result->Properties->AttachOnFail.empty()) {
result->Properties->AttachedFiles.insert(
result->Properties->AttachedFiles.end(),
result->Properties->AttachOnFail.begin(),
result->Properties->AttachOnFail.end());
}
for (std::vector<std::string>::const_iterator file =
result->Properties->AttachedFiles.begin();
file != result->Properties->AttachedFiles.end(); ++file) {
const std::string& base64 = this->CTest->Base64GzipEncodeFile(*file);
std::string fname = cmSystemTools::GetFilenameName(*file);
xml.StartElement("NamedMeasurement");
xml.Attribute("name", "Attached File");
xml.Attribute("encoding", "base64");
xml.Attribute("compression", "tar/gzip");
xml.Attribute("filename", fname);
xml.Attribute("type", "file");
xml.Element("Value", base64);
xml.EndElement(); // NamedMeasurement
}
}
int cmCTestTestHandler::ExecuteCommands(std::vector<std::string>& vec)
2004-09-09 16:41:05 +04:00
{
std::vector<std::string>::iterator it;
for (it = vec.begin(); it != vec.end(); ++it) {
2004-09-09 16:41:05 +04:00
int retVal = 0;
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Run command: " << *it << std::endl, this->Quiet);
2016-06-27 23:44:16 +03:00
if (!cmSystemTools::RunSingleCommand(it->c_str(), CM_NULLPTR, CM_NULLPTR,
&retVal, CM_NULLPTR,
cmSystemTools::OUTPUT_MERGE
/*this->Verbose*/) ||
retVal != 0) {
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Problem running command: " << *it << std::endl);
2004-09-09 16:41:05 +04:00
return 0;
}
}
2004-09-09 16:41:05 +04:00
return 1;
}
// Find the appropriate executable to run for a test
std::string cmCTestTestHandler::FindTheExecutable(const char* exe)
2004-09-09 16:41:05 +04:00
{
std::string resConfig;
std::vector<std::string> extraPaths;
std::vector<std::string> failedPaths;
if (strcmp(exe, "NOT_AVAILABLE") == 0) {
return exe;
}
return cmCTestTestHandler::FindExecutable(this->CTest, exe, resConfig,
extraPaths, failedPaths);
}
2004-09-09 16:41:05 +04:00
// add additional configurations to the search path
void cmCTestTestHandler::AddConfigurations(
cmCTest* ctest, std::vector<std::string>& attempted,
std::vector<std::string>& attemptedConfigs, std::string filepath,
std::string& filename)
{
std::string tempPath;
if (!filepath.empty() && filepath[filepath.size() - 1] != '/') {
filepath += "/";
}
tempPath = filepath + filename;
attempted.push_back(tempPath);
attemptedConfigs.push_back("");
2011-03-11 16:04:58 +03:00
if (!ctest->GetConfigType().empty()) {
tempPath = filepath;
tempPath += ctest->GetConfigType();
tempPath += "/";
tempPath += filename;
attempted.push_back(tempPath);
attemptedConfigs.push_back(ctest->GetConfigType());
2012-11-07 20:13:09 +04:00
// If the file is an OSX bundle then the configtype
// will be at the start of the path
tempPath = ctest->GetConfigType();
tempPath += "/";
tempPath += filepath;
tempPath += filename;
attempted.push_back(tempPath);
attemptedConfigs.push_back(ctest->GetConfigType());
} else {
2012-11-07 20:13:09 +04:00
// no config specified - try some options...
tempPath = filepath;
tempPath += "Release/";
tempPath += filename;
attempted.push_back(tempPath);
attemptedConfigs.push_back("Release");
tempPath = filepath;
tempPath += "Debug/";
tempPath += filename;
attempted.push_back(tempPath);
attemptedConfigs.push_back("Debug");
tempPath = filepath;
tempPath += "MinSizeRel/";
tempPath += filename;
attempted.push_back(tempPath);
attemptedConfigs.push_back("MinSizeRel");
tempPath = filepath;
tempPath += "RelWithDebInfo/";
tempPath += filename;
2011-03-11 16:04:58 +03:00
attempted.push_back(tempPath);
attemptedConfigs.push_back("RelWithDebInfo");
tempPath = filepath;
tempPath += "Deployment/";
tempPath += filename;
attempted.push_back(tempPath);
attemptedConfigs.push_back("Deployment");
tempPath = filepath;
tempPath += "Development/";
tempPath += filename;
attempted.push_back(tempPath);
attemptedConfigs.push_back("Deployment");
}
}
// Find the appropriate executable to run for a test
std::string cmCTestTestHandler::FindExecutable(
cmCTest* ctest, const char* testCommand, std::string& resultingConfig,
std::vector<std::string>& extraPaths, std::vector<std::string>& failed)
{
// now run the compiled test if we can find it
std::vector<std::string> attempted;
std::vector<std::string> attemptedConfigs;
std::string tempPath;
std::string filepath = cmSystemTools::GetFilenamePath(testCommand);
std::string filename = cmSystemTools::GetFilenameName(testCommand);
cmCTestTestHandler::AddConfigurations(ctest, attempted, attemptedConfigs,
filepath, filename);
// even if a fullpath was specified also try it relative to the current
// directory
if (!filepath.empty() && filepath[0] == '/') {
std::string localfilepath = filepath.substr(1, filepath.size() - 1);
cmCTestTestHandler::AddConfigurations(ctest, attempted, attemptedConfigs,
localfilepath, filename);
}
2011-03-11 16:04:58 +03:00
// if extraPaths are provided and we were not passed a full path, try them,
// try any extra paths
if (filepath.empty()) {
for (unsigned int i = 0; i < extraPaths.size(); ++i) {
std::string filepathExtra =
cmSystemTools::GetFilenamePath(extraPaths[i]);
std::string filenameExtra =
cmSystemTools::GetFilenameName(extraPaths[i]);
cmCTestTestHandler::AddConfigurations(ctest, attempted, attemptedConfigs,
filepathExtra, filenameExtra);
2011-03-11 16:04:58 +03:00
}
}
2011-03-11 16:04:58 +03:00
// store the final location in fullPath
std::string fullPath;
2004-09-09 16:41:05 +04:00
// now look in the paths we specified above
for (unsigned int ai = 0; ai < attempted.size() && fullPath.empty(); ++ai) {
// first check without exe extension
if (cmSystemTools::FileExists(attempted[ai].c_str()) &&
!cmSystemTools::FileIsDirectory(attempted[ai])) {
fullPath = cmSystemTools::CollapseFullPath(attempted[ai]);
resultingConfig = attemptedConfigs[ai];
}
// then try with the exe extension
else {
failed.push_back(attempted[ai]);
tempPath = attempted[ai];
tempPath += cmSystemTools::GetExecutableExtension();
if (cmSystemTools::FileExists(tempPath.c_str()) &&
!cmSystemTools::FileIsDirectory(tempPath)) {
fullPath = cmSystemTools::CollapseFullPath(tempPath);
resultingConfig = attemptedConfigs[ai];
} else {
failed.push_back(tempPath);
2004-09-09 16:41:05 +04:00
}
}
}
2011-03-11 16:04:58 +03:00
// if everything else failed, check the users path, but only if a full path
// wasn't specified
if (fullPath.empty() && filepath.empty()) {
std::string path = cmSystemTools::FindProgram(filename.c_str());
if (path != "") {
resultingConfig = "";
2004-09-09 16:41:05 +04:00
return path;
}
}
if (fullPath.empty()) {
cmCTestLog(ctest, HANDLER_OUTPUT, "Could not find executable "
<< testCommand << "\n"
<< "Looked in the following places:\n");
for (std::vector<std::string>::iterator i = failed.begin();
i != failed.end(); ++i) {
cmCTestLog(ctest, HANDLER_OUTPUT, *i << "\n");
}
}
2011-03-11 16:04:58 +03:00
2004-09-09 16:41:05 +04:00
return fullPath;
}
void cmCTestTestHandler::GetListOfTests()
2004-09-09 16:41:05 +04:00
{
if (!this->IncludeLabelRegExp.empty()) {
this->IncludeLabelRegularExpression.compile(
this->IncludeLabelRegExp.c_str());
}
if (!this->ExcludeLabelRegExp.empty()) {
this->ExcludeLabelRegularExpression.compile(
this->ExcludeLabelRegExp.c_str());
}
if (!this->IncludeRegExp.empty()) {
2006-03-10 23:03:09 +03:00
this->IncludeTestsRegularExpression.compile(this->IncludeRegExp.c_str());
}
if (!this->ExcludeRegExp.empty()) {
2006-03-10 23:03:09 +03:00
this->ExcludeTestsRegularExpression.compile(this->ExcludeRegExp.c_str());
}
2015-02-17 22:23:56 +03:00
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Constructing a list of tests" << std::endl, this->Quiet);
cmake cm;
cm.SetHomeDirectory("");
cm.SetHomeOutputDirectory("");
cm.GetCurrentSnapshot().SetDefaultDefinitions();
cmGlobalGenerator gg(&cm);
CM_AUTO_PTR<cmMakefile> mf(new cmMakefile(&gg, cm.GetCurrentSnapshot()));
2006-03-09 19:17:10 +03:00
mf->AddDefinition("CTEST_CONFIGURATION_TYPE",
this->CTest->GetConfigType().c_str());
// Add handler for ADD_TEST
cmCTestAddTestCommand* newCom1 = new cmCTestAddTestCommand;
2006-03-10 23:03:09 +03:00
newCom1->TestHandler = this;
2015-04-11 13:52:14 +03:00
cm.GetState()->AddCommand(newCom1);
2008-01-19 23:09:36 +03:00
// Add handler for SUBDIRS
cmCTestSubdirCommand* newCom2 = new cmCTestSubdirCommand;
2006-03-10 23:03:09 +03:00
newCom2->TestHandler = this;
2015-04-11 13:52:14 +03:00
cm.GetState()->AddCommand(newCom2);
2008-01-19 23:09:36 +03:00
// Add handler for ADD_SUBDIRECTORY
cmCTestAddSubdirectoryCommand* newCom3 = new cmCTestAddSubdirectoryCommand;
2006-03-10 23:03:09 +03:00
newCom3->TestHandler = this;
2015-04-11 13:52:14 +03:00
cm.GetState()->AddCommand(newCom3);
2008-01-19 23:09:36 +03:00
// Add handler for SET_SOURCE_FILES_PROPERTIES
cmCTestSetTestsPropertiesCommand* newCom4 =
new cmCTestSetTestsPropertiesCommand;
2008-01-19 23:09:36 +03:00
newCom4->TestHandler = this;
2015-04-11 13:52:14 +03:00
cm.GetState()->AddCommand(newCom4);
2008-01-19 23:09:36 +03:00
2005-06-20 16:59:33 +04:00
const char* testFilename;
if (cmSystemTools::FileExists("CTestTestfile.cmake")) {
// does the CTestTestfile.cmake exist ?
testFilename = "CTestTestfile.cmake";
} else if (cmSystemTools::FileExists("DartTestfile.txt")) {
// does the DartTestfile.txt exist ?
testFilename = "DartTestfile.txt";
} else {
2004-09-09 16:41:05 +04:00
return;
}
2004-09-09 16:41:05 +04:00
if (!mf->ReadListFile(testFilename)) {
2004-09-09 16:41:05 +04:00
return;
}
if (cmSystemTools::GetErrorOccuredFlag()) {
return;
}
2015-02-17 22:23:56 +03:00
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Done constructing a list of tests" << std::endl,
this->Quiet);
2004-09-09 16:41:05 +04:00
}
void cmCTestTestHandler::UseIncludeRegExp()
{
2006-03-10 23:03:09 +03:00
this->UseIncludeRegExpFlag = true;
2004-09-09 16:41:05 +04:00
}
void cmCTestTestHandler::UseExcludeRegExp()
{
2006-03-10 23:03:09 +03:00
this->UseExcludeRegExpFlag = true;
this->UseExcludeRegExpFirst = !this->UseIncludeRegExpFlag;
2004-09-09 16:41:05 +04:00
}
2006-03-09 19:17:10 +03:00
2004-09-09 16:41:05 +04:00
const char* cmCTestTestHandler::GetTestStatus(int status)
{
static const char statuses[][100] = {
"Not Run", "Timeout", "SEGFAULT", "ILLEGAL", "INTERRUPT",
"NUMERICAL", "OTHER_FAULT", "Failed", "BAD_COMMAND", "Completed"
2004-09-09 16:41:05 +04:00
};
if (status < cmCTestTestHandler::NOT_RUN ||
status > cmCTestTestHandler::COMPLETED) {
2004-09-09 16:41:05 +04:00
return "No Status";
}
2004-09-09 16:41:05 +04:00
return statuses[status];
}
2008-07-07 03:58:38 +04:00
void cmCTestTestHandler::ExpandTestsToRunInformation(size_t numTests)
2004-09-09 16:41:05 +04:00
{
if (this->TestsToRunString.empty()) {
2004-09-09 16:41:05 +04:00
return;
}
2006-03-09 19:17:10 +03:00
2004-09-09 16:41:05 +04:00
int start;
int end = -1;
double stride = -1;
std::string::size_type pos = 0;
std::string::size_type pos2;
// read start
if (GetNextNumber(this->TestsToRunString, start, pos, pos2)) {
2004-09-09 16:41:05 +04:00
// read end
if (GetNextNumber(this->TestsToRunString, end, pos, pos2)) {
2004-09-09 16:41:05 +04:00
// read stride
if (GetNextRealNumber(this->TestsToRunString, stride, pos, pos2)) {
int val = 0;
2004-09-09 16:41:05 +04:00
// now read specific numbers
while (GetNextNumber(this->TestsToRunString, val, pos, pos2)) {
2006-03-10 23:03:09 +03:00
this->TestsToRun.push_back(val);
2004-09-09 16:41:05 +04:00
}
this->TestsToRun.push_back(val);
2004-09-09 16:41:05 +04:00
}
}
}
2004-09-09 16:41:05 +04:00
// if start is not specified then we assume we start at 1
if (start == -1) {
2004-09-09 16:41:05 +04:00
start = 1;
}
2004-09-09 16:41:05 +04:00
// if end isnot specified then we assume we end with the last test
if (end == -1) {
2008-07-07 06:06:08 +04:00
end = static_cast<int>(numTests);
}
2006-03-09 19:17:10 +03:00
2004-09-09 16:41:05 +04:00
// if the stride wasn't specified then it defaults to 1
if (stride == -1) {
2004-09-09 16:41:05 +04:00
stride = 1;
}
2004-09-09 16:41:05 +04:00
// if we have a range then add it
if (end != -1 && start != -1 && stride > 0) {
2004-09-09 16:41:05 +04:00
int i = 0;
while (i * stride + start <= end) {
this->TestsToRun.push_back(static_cast<int>(i * stride + start));
2004-09-09 16:41:05 +04:00
++i;
}
}
2004-09-09 16:41:05 +04:00
// sort the array
2006-03-10 23:03:09 +03:00
std::sort(this->TestsToRun.begin(), this->TestsToRun.end(),
std::less<int>());
2004-09-09 16:41:05 +04:00
// remove duplicates
2006-03-09 19:17:10 +03:00
std::vector<int>::iterator new_end =
2006-03-10 23:03:09 +03:00
std::unique(this->TestsToRun.begin(), this->TestsToRun.end());
this->TestsToRun.erase(new_end, this->TestsToRun.end());
2004-09-09 16:41:05 +04:00
}
void cmCTestTestHandler::ExpandTestsToRunInformationForRerunFailed()
{
std::string dirName = this->CTest->GetBinaryDir() + "/Testing/Temporary";
cmsys::Directory directory;
if (directory.Load(dirName) == 0) {
cmCTestLog(this->CTest, ERROR_MESSAGE, "Unable to read the contents of "
<< dirName << std::endl);
return;
}
int numFiles =
static_cast<int>(cmsys::Directory::GetNumberOfFilesInDirectory(dirName));
std::string pattern = "LastTestsFailed";
std::string logName = "";
for (int i = 0; i < numFiles; ++i) {
std::string fileName = directory.GetFile(i);
// bcc crashes if we attempt a normal substring comparison,
// hence the following workaround
std::string fileNameSubstring = fileName.substr(0, pattern.length());
if (fileNameSubstring.compare(pattern) != 0) {
continue;
}
if (logName == "") {
logName = fileName;
} else {
// if multiple matching logs were found we use the most recently
// modified one.
int res;
cmSystemTools::FileTimeCompare(logName, fileName, &res);
if (res == -1) {
logName = fileName;
}
}
}
std::string lastTestsFailedLog =
this->CTest->GetBinaryDir() + "/Testing/Temporary/" + logName;
if (!cmSystemTools::FileExists(lastTestsFailedLog.c_str())) {
if (!this->CTest->GetShowOnly() && !this->CTest->ShouldPrintLabels()) {
cmCTestLog(this->CTest, ERROR_MESSAGE, lastTestsFailedLog
<< " does not exist!" << std::endl);
}
return;
}
// parse the list of tests to rerun from LastTestsFailed.log
cmsys::ifstream ifs(lastTestsFailedLog.c_str());
if (ifs) {
std::string line;
std::string::size_type pos;
while (cmSystemTools::GetLineFromStream(ifs, line)) {
pos = line.find(':', 0);
if (pos == line.npos) {
continue;
}
int val = atoi(line.substr(0, pos).c_str());
this->TestsToRun.push_back(val);
}
ifs.close();
} else if (!this->CTest->GetShowOnly() &&
!this->CTest->ShouldPrintLabels()) {
cmCTestLog(this->CTest, ERROR_MESSAGE, "Problem reading file: "
<< lastTestsFailedLog
<< " while generating list of previously failed tests."
<< std::endl);
}
}
2005-01-27 23:54:47 +03:00
// Just for convenience
2004-09-09 16:41:05 +04:00
#define SPACE_REGEX "[ \t\r\n]"
void cmCTestTestHandler::GenerateRegressionImages(cmXMLWriter& xml,
const std::string& dart)
2004-09-09 16:41:05 +04:00
{
cmsys::RegularExpression twoattributes(
"<DartMeasurement" SPACE_REGEX
"*(name|type|encoding|compression)=\"([^\"]*)\"" SPACE_REGEX
"*(name|type|encoding|compression)=\"([^\"]*)\"" SPACE_REGEX
"*>([^<]*)</DartMeasurement>");
2004-09-09 16:41:05 +04:00
cmsys::RegularExpression threeattributes(
"<DartMeasurement" SPACE_REGEX
"*(name|type|encoding|compression)=\"([^\"]*)\"" SPACE_REGEX
"*(name|type|encoding|compression)=\"([^\"]*)\"" SPACE_REGEX
"*(name|type|encoding|compression)=\"([^\"]*)\"" SPACE_REGEX
"*>([^<]*)</DartMeasurement>");
2004-09-09 16:41:05 +04:00
cmsys::RegularExpression fourattributes(
"<DartMeasurement" SPACE_REGEX
"*(name|type|encoding|compression)=\"([^\"]*)\"" SPACE_REGEX
"*(name|type|encoding|compression)=\"([^\"]*)\"" SPACE_REGEX
"*(name|type|encoding|compression)=\"([^\"]*)\"" SPACE_REGEX
"*(name|type|encoding|compression)=\"([^\"]*)\"" SPACE_REGEX
"*>([^<]*)</DartMeasurement>");
2008-06-17 22:03:49 +04:00
cmsys::RegularExpression cdatastart(
"<DartMeasurement" SPACE_REGEX
"*(name|type|encoding|compression)=\"([^\"]*)\"" SPACE_REGEX
"*(name|type|encoding|compression)=\"([^\"]*)\"" SPACE_REGEX
"*>" SPACE_REGEX "*<!\\[CDATA\\[");
cmsys::RegularExpression cdataend("]]>" SPACE_REGEX "*</DartMeasurement>");
2004-09-09 16:41:05 +04:00
cmsys::RegularExpression measurementfile(
"<DartMeasurementFile" SPACE_REGEX
"*(name|type|encoding|compression)=\"([^\"]*)\"" SPACE_REGEX
"*(name|type|encoding|compression)=\"([^\"]*)\"" SPACE_REGEX
"*>([^<]*)</DartMeasurementFile>");
2004-09-09 16:41:05 +04:00
bool done = false;
std::string cxml = dart;
while (!done) {
if (twoattributes.find(cxml)) {
xml.StartElement("NamedMeasurement");
xml.Attribute(twoattributes.match(1).c_str(), twoattributes.match(2));
xml.Attribute(twoattributes.match(3).c_str(), twoattributes.match(4));
xml.Element("Value", twoattributes.match(5));
xml.EndElement();
2006-03-09 19:17:10 +03:00
cxml.erase(twoattributes.start(),
twoattributes.end() - twoattributes.start());
} else if (threeattributes.find(cxml)) {
xml.StartElement("NamedMeasurement");
xml.Attribute(threeattributes.match(1).c_str(),
threeattributes.match(2));
xml.Attribute(threeattributes.match(3).c_str(),
threeattributes.match(4));
xml.Attribute(threeattributes.match(5).c_str(),
threeattributes.match(6));
xml.Element("Value", twoattributes.match(7));
xml.EndElement();
2006-03-09 19:17:10 +03:00
cxml.erase(threeattributes.start(),
threeattributes.end() - threeattributes.start());
} else if (fourattributes.find(cxml)) {
xml.StartElement("NamedMeasurement");
xml.Attribute(fourattributes.match(1).c_str(), fourattributes.match(2));
xml.Attribute(fourattributes.match(3).c_str(), fourattributes.match(4));
xml.Attribute(fourattributes.match(5).c_str(), fourattributes.match(6));
xml.Attribute(fourattributes.match(7).c_str(), fourattributes.match(8));
xml.Element("Value", twoattributes.match(9));
xml.EndElement();
2006-03-09 19:17:10 +03:00
cxml.erase(fourattributes.start(),
fourattributes.end() - fourattributes.start());
} else if (cdatastart.find(cxml) && cdataend.find(cxml)) {
xml.StartElement("NamedMeasurement");
xml.Attribute(cdatastart.match(1).c_str(), cdatastart.match(2));
xml.Attribute(cdatastart.match(3).c_str(), cdatastart.match(4));
xml.StartElement("Value");
xml.CData(
cxml.substr(cdatastart.end(), cdataend.start() - cdatastart.end()));
xml.EndElement(); // Value
xml.EndElement(); // NamedMeasurement
cxml.erase(cdatastart.start(), cdataend.end() - cdatastart.start());
} else if (measurementfile.find(cxml)) {
2006-03-09 19:17:10 +03:00
const std::string& filename =
2004-09-09 16:41:05 +04:00
cmCTest::CleanString(measurementfile.match(5));
if (cmSystemTools::FileExists(filename.c_str())) {
2014-11-23 13:05:50 +03:00
long len = cmSystemTools::FileLength(filename);
if (len == 0) {
2004-09-09 16:41:05 +04:00
std::string k1 = measurementfile.match(1);
std::string v1 = measurementfile.match(2);
std::string k2 = measurementfile.match(3);
std::string v2 = measurementfile.match(4);
if (cmSystemTools::LowerCase(k1) == "type") {
2004-09-09 16:41:05 +04:00
v1 = "text/string";
}
if (cmSystemTools::LowerCase(k2) == "type") {
2004-09-09 16:41:05 +04:00
v2 = "text/string";
}
2006-03-09 19:17:10 +03:00
xml.StartElement("NamedMeasurement");
xml.Attribute(k1.c_str(), v1);
xml.Attribute(k2.c_str(), v2);
xml.Attribute("encoding", "none");
xml.Element("Value", "Image " + filename + " is empty");
xml.EndElement();
} else {
cmsys::ifstream ifs(filename.c_str(), std::ios::in
2004-09-09 16:41:05 +04:00
#ifdef _WIN32
| std::ios::binary
2004-09-09 16:41:05 +04:00
#endif
);
unsigned char* file_buffer = new unsigned char[len + 1];
2004-09-09 16:41:05 +04:00
ifs.read(reinterpret_cast<char*>(file_buffer), len);
unsigned char* encoded_buffer = new unsigned char[static_cast<int>(
static_cast<double>(len) * 1.5 + 5.0)];
2006-03-09 19:17:10 +03:00
size_t rlen =
cmsysBase64_Encode(file_buffer, len, encoded_buffer, 1);
2006-03-09 19:17:10 +03:00
xml.StartElement("NamedMeasurement");
xml.Attribute(measurementfile.match(1).c_str(),
measurementfile.match(2));
xml.Attribute(measurementfile.match(3).c_str(),
measurementfile.match(4));
xml.Attribute("encoding", "base64");
std::ostringstream ostr;
for (size_t cc = 0; cc < rlen; cc++) {
2004-09-09 16:41:05 +04:00
ostr << encoded_buffer[cc];
if (cc % 60 == 0 && cc) {
2004-09-09 16:41:05 +04:00
ostr << std::endl;
}
}
xml.Element("Value", ostr.str());
xml.EndElement(); // NamedMeasurement
delete[] file_buffer;
delete[] encoded_buffer;
2004-09-09 16:41:05 +04:00
}
} else {
2004-09-09 16:41:05 +04:00
int idx = 4;
if (measurementfile.match(1) == "name") {
2004-09-09 16:41:05 +04:00
idx = 2;
}
xml.StartElement("NamedMeasurement");
xml.Attribute("name", measurementfile.match(idx));
xml.Attribute("text", "text/string");
xml.Element("Value", "File " + filename + " not found");
xml.EndElement();
cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "File \""
<< filename << "\" not found." << std::endl,
this->Quiet);
2004-09-09 16:41:05 +04:00
}
cxml.erase(measurementfile.start(),
measurementfile.end() - measurementfile.start());
} else {
2004-09-09 16:41:05 +04:00
done = true;
}
}
2004-09-09 16:41:05 +04:00
}
void cmCTestTestHandler::SetIncludeRegExp(const char* arg)
2004-09-09 16:41:05 +04:00
{
2006-03-10 23:03:09 +03:00
this->IncludeRegExp = arg;
2004-09-09 16:41:05 +04:00
}
void cmCTestTestHandler::SetExcludeRegExp(const char* arg)
2004-09-09 16:41:05 +04:00
{
2006-03-10 23:03:09 +03:00
this->ExcludeRegExp = arg;
2004-09-09 16:41:05 +04:00
}
void cmCTestTestHandler::SetTestsToRunInformation(const char* in)
{
if (!in) {
return;
}
2004-09-09 16:41:05 +04:00
this->TestsToRunString = in;
2006-03-09 19:17:10 +03:00
// if the argument is a file, then read it and use the contents as the
// string
if (cmSystemTools::FileExists(in)) {
cmsys::ifstream fin(in);
2004-09-09 16:41:05 +04:00
unsigned long filelen = cmSystemTools::FileLength(in);
char* buff = new char[filelen + 1];
2004-09-09 16:41:05 +04:00
fin.getline(buff, filelen);
buff[fin.gcount()] = 0;
this->TestsToRunString = buff;
delete[] buff;
}
2004-09-09 16:41:05 +04:00
}
bool cmCTestTestHandler::CleanTestOutput(std::string& output, size_t length)
{
if (!length || length >= output.size() ||
output.find("CTEST_FULL_OUTPUT") != output.npos) {
return true;
}
// Truncate at given length but do not break in the middle of a multi-byte
// UTF-8 encoding.
char const* const begin = output.c_str();
char const* const end = begin + output.size();
char const* const truncate = begin + length;
char const* current = begin;
while (current < truncate) {
unsigned int ch;
if (const char* next = cm_utf8_decode_character(current, end, &ch)) {
if (next > truncate) {
break;
}
current = next;
} else // Bad byte will be handled by cmXMLWriter.
{
++current;
}
}
output = output.substr(0, current - begin);
// Append truncation message.
std::ostringstream msg;
msg << "...\n"
"The rest of the test output was removed since it exceeds the "
"threshold "
"of "
<< length << " bytes.\n";
output += msg.str();
return true;
}
2006-03-09 19:17:10 +03:00
bool cmCTestTestHandler::SetTestsProperties(
const std::vector<std::string>& args)
{
std::vector<std::string>::const_iterator it;
std::vector<std::string> tests;
bool found = false;
for (it = args.begin(); it != args.end(); ++it) {
if (*it == "PROPERTIES") {
found = true;
break;
}
tests.push_back(*it);
}
if (!found) {
return false;
}
++it; // skip PROPERTIES
for (; it != args.end(); ++it) {
std::string key = *it;
++it;
if (it == args.end()) {
break;
}
std::string val = *it;
std::vector<std::string>::const_iterator tit;
for (tit = tests.begin(); tit != tests.end(); ++tit) {
2006-03-10 23:03:09 +03:00
cmCTestTestHandler::ListOfTests::iterator rtit;
for (rtit = this->TestList.begin(); rtit != this->TestList.end();
++rtit) {
if (*tit == rtit->Name) {
if (key == "WILL_FAIL") {
2006-03-10 23:03:09 +03:00
rtit->WillFail = cmSystemTools::IsOn(val.c_str());
}
if (key == "ATTACHED_FILES") {
cmSystemTools::ExpandListArgument(val, rtit->AttachedFiles);
}
if (key == "ATTACHED_FILES_ON_FAIL") {
cmSystemTools::ExpandListArgument(val, rtit->AttachOnFail);
}
if (key == "RESOURCE_LOCK") {
2010-03-02 23:34:37 +03:00
std::vector<std::string> lval;
cmSystemTools::ExpandListArgument(val, lval);
2010-03-02 23:34:37 +03:00
rtit->LockedResources.insert(lval.begin(), lval.end());
}
if (key == "FIXTURES_SETUP") {
std::vector<std::string> lval;
cmSystemTools::ExpandListArgument(val, lval);
rtit->FixturesSetup.insert(lval.begin(), lval.end());
}
if (key == "FIXTURES_CLEANUP") {
std::vector<std::string> lval;
cmSystemTools::ExpandListArgument(val, lval);
rtit->FixturesCleanup.insert(lval.begin(), lval.end());
}
if (key == "FIXTURES_REQUIRED") {
std::vector<std::string> lval;
cmSystemTools::ExpandListArgument(val, lval);
rtit->FixturesRequired.insert(lval.begin(), lval.end());
}
if (key == "TIMEOUT") {
2007-01-25 19:16:16 +03:00
rtit->Timeout = atof(val.c_str());
rtit->ExplicitTimeout = true;
}
if (key == "COST") {
2009-10-02 23:30:01 +04:00
rtit->Cost = static_cast<float>(atof(val.c_str()));
}
if (key == "REQUIRED_FILES") {
cmSystemTools::ExpandListArgument(val, rtit->RequiredFiles);
}
if (key == "RUN_SERIAL") {
rtit->RunSerial = cmSystemTools::IsOn(val.c_str());
}
if (key == "FAIL_REGULAR_EXPRESSION") {
std::vector<std::string> lval;
cmSystemTools::ExpandListArgument(val, lval);
std::vector<std::string>::iterator crit;
for (crit = lval.begin(); crit != lval.end(); ++crit) {
2006-03-10 23:03:09 +03:00
rtit->ErrorRegularExpressions.push_back(
std::pair<cmsys::RegularExpression, std::string>(
cmsys::RegularExpression(crit->c_str()),
std::string(*crit)));
}
}
if (key == "PROCESSORS") {
rtit->Processors = atoi(val.c_str());
if (rtit->Processors < 1) {
rtit->Processors = 1;
}
}
if (key == "SKIP_RETURN_CODE") {
rtit->SkipReturnCode = atoi(val.c_str());
if (rtit->SkipReturnCode < 0 || rtit->SkipReturnCode > 255) {
rtit->SkipReturnCode = -1;
}
}
if (key == "DEPENDS") {
cmSystemTools::ExpandListArgument(val, rtit->Depends);
}
if (key == "ENVIRONMENT") {
cmSystemTools::ExpandListArgument(val, rtit->Environment);
}
if (key == "LABELS") {
cmSystemTools::ExpandListArgument(val, rtit->Labels);
}
if (key == "MEASUREMENT") {
size_t pos = val.find_first_of('=');
if (pos != val.npos) {
std::string mKey = val.substr(0, pos);
const char* mVal = val.c_str() + pos + 1;
rtit->Measurements[mKey] = mVal;
} else {
rtit->Measurements[val] = "1";
}
}
if (key == "PASS_REGULAR_EXPRESSION") {
std::vector<std::string> lval;
cmSystemTools::ExpandListArgument(val, lval);
std::vector<std::string>::iterator crit;
for (crit = lval.begin(); crit != lval.end(); ++crit) {
2006-03-10 23:03:09 +03:00
rtit->RequiredRegularExpressions.push_back(
std::pair<cmsys::RegularExpression, std::string>(
cmsys::RegularExpression(crit->c_str()),
std::string(*crit)));
}
}
if (key == "WORKING_DIRECTORY") {
rtit->Directory = val;
}
if (key == "TIMEOUT_AFTER_MATCH") {
std::vector<std::string> propArgs;
cmSystemTools::ExpandListArgument(val, propArgs);
if (propArgs.size() != 2) {
cmCTestLog(this->CTest, WARNING,
"TIMEOUT_AFTER_MATCH expects two arguments, found "
<< propArgs.size() << std::endl);
} else {
rtit->AlternateTimeout = atof(propArgs[0].c_str());
std::vector<std::string> lval;
cmSystemTools::ExpandListArgument(propArgs[1], lval);
std::vector<std::string>::iterator crit;
for (crit = lval.begin(); crit != lval.end(); ++crit) {
rtit->TimeoutRegularExpressions.push_back(
std::pair<cmsys::RegularExpression, std::string>(
cmsys::RegularExpression(crit->c_str()),
std::string(*crit)));
}
}
}
}
}
}
}
return true;
}
bool cmCTestTestHandler::AddTest(const std::vector<std::string>& args)
{
const std::string& testname = args[0];
2015-02-17 22:23:56 +03:00
cmCTestOptionalLog(this->CTest, DEBUG, "Add test: " << args[0] << std::endl,
this->Quiet);
if (this->UseExcludeRegExpFlag && this->UseExcludeRegExpFirst &&
this->ExcludeTestsRegularExpression.find(testname.c_str())) {
return true;
}
if (this->MemCheck) {
std::vector<std::string>::iterator it;
bool found = false;
for (it = this->CustomTestsIgnore.begin();
it != this->CustomTestsIgnore.end(); ++it) {
if (*it == testname) {
found = true;
break;
}
}
if (found) {
2015-02-17 22:23:56 +03:00
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Ignore memcheck: " << *it << std::endl, this->Quiet);
return true;
}
} else {
std::vector<std::string>::iterator it;
bool found = false;
for (it = this->CustomTestsIgnore.begin();
it != this->CustomTestsIgnore.end(); ++it) {
if (*it == testname) {
found = true;
break;
}
}
if (found) {
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Ignore test: " << *it << std::endl, this->Quiet);
return true;
}
}
cmCTestTestProperties test;
2006-03-10 23:03:09 +03:00
test.Name = testname;
test.Args = args;
test.Directory = cmSystemTools::GetCurrentWorkingDirectory();
cmCTestOptionalLog(this->CTest, DEBUG,
"Set test directory: " << test.Directory << std::endl,
this->Quiet);
2011-03-11 16:04:58 +03:00
2006-03-10 23:03:09 +03:00
test.IsInBasedOnREOptions = true;
test.WillFail = false;
test.RunSerial = false;
2007-01-25 19:16:16 +03:00
test.Timeout = 0;
test.ExplicitTimeout = false;
test.Cost = 0;
test.Processors = 1;
test.SkipReturnCode = -1;
test.PreviousRuns = 0;
2006-03-10 23:03:09 +03:00
if (this->UseIncludeRegExpFlag &&
!this->IncludeTestsRegularExpression.find(testname.c_str())) {
2006-03-10 23:03:09 +03:00
test.IsInBasedOnREOptions = false;
} else if (this->UseExcludeRegExpFlag && !this->UseExcludeRegExpFirst &&
this->ExcludeTestsRegularExpression.find(testname.c_str())) {
2006-03-10 23:03:09 +03:00
test.IsInBasedOnREOptions = false;
}
2006-03-10 23:03:09 +03:00
this->TestList.push_back(test);
return true;
}