mirror of
https://github.com/Kitware/CMake.git
synced 2026-01-01 11:22:21 -06:00
ctest: Optionally terminate tests with a custom signal on timeout
CTest normally terminates test processes on timeout using `SIGKILL`.
Offer tests a chance to exit gracefully, on platforms supporting POSIX
signals, by setting `TIMEOUT_SIGNAL_{NAME,GRACE_PERIOD}` properties.
Fixes: #17288
This commit is contained in:
@@ -522,6 +522,8 @@ Properties on Tests
|
||||
/prop_test/SKIP_RETURN_CODE
|
||||
/prop_test/TIMEOUT
|
||||
/prop_test/TIMEOUT_AFTER_MATCH
|
||||
/prop_test/TIMEOUT_SIGNAL_GRACE_PERIOD
|
||||
/prop_test/TIMEOUT_SIGNAL_NAME
|
||||
/prop_test/WILL_FAIL
|
||||
/prop_test/WORKING_DIRECTORY
|
||||
|
||||
|
||||
@@ -10,3 +10,6 @@ setting takes precedence over :variable:`CTEST_TEST_TIMEOUT`.
|
||||
|
||||
An explicit ``0`` value means the test has no timeout, except as
|
||||
necessary to honor :option:`ctest --stop-time`.
|
||||
|
||||
See also :prop_test:`TIMEOUT_AFTER_MATCH` and
|
||||
:prop_test:`TIMEOUT_SIGNAL_NAME`.
|
||||
|
||||
@@ -39,3 +39,5 @@ If the required resource can be controlled by CTest you should use
|
||||
:prop_test:`RESOURCE_LOCK` instead of ``TIMEOUT_AFTER_MATCH``.
|
||||
This property should be used when only the test itself can determine
|
||||
when its required resources are available.
|
||||
|
||||
See also :prop_test:`TIMEOUT_SIGNAL_NAME`.
|
||||
|
||||
14
Help/prop_test/TIMEOUT_SIGNAL_GRACE_PERIOD.rst
Normal file
14
Help/prop_test/TIMEOUT_SIGNAL_GRACE_PERIOD.rst
Normal file
@@ -0,0 +1,14 @@
|
||||
TIMEOUT_SIGNAL_GRACE_PERIOD
|
||||
---------------------------
|
||||
|
||||
.. versionadded:: 3.27
|
||||
|
||||
If the :prop_test:`TIMEOUT_SIGNAL_NAME` test property is set, this property
|
||||
specifies the number of seconds to wait for a test process to terminate after
|
||||
sending the custom signal. Otherwise, this property has no meaning.
|
||||
|
||||
The grace period may be any real value greater than ``0.0``, but not greater
|
||||
than ``60.0``. If this property is not set, the default is ``1.0`` second.
|
||||
|
||||
This is available only on platforms supporting POSIX signals.
|
||||
It is not available on Windows.
|
||||
41
Help/prop_test/TIMEOUT_SIGNAL_NAME.rst
Normal file
41
Help/prop_test/TIMEOUT_SIGNAL_NAME.rst
Normal file
@@ -0,0 +1,41 @@
|
||||
TIMEOUT_SIGNAL_NAME
|
||||
-------------------
|
||||
|
||||
.. versionadded:: 3.27
|
||||
|
||||
Specify a custom signal to send to a test process when its timeout is reached.
|
||||
This is available only on platforms supporting POSIX signals.
|
||||
It is not available on Windows.
|
||||
|
||||
The name must be one of the following:
|
||||
|
||||
``SIGINT``
|
||||
Interrupt.
|
||||
|
||||
``SIGQUIT``
|
||||
Quit.
|
||||
|
||||
``SIGTERM``
|
||||
Terminate.
|
||||
|
||||
``SIGUSR1``
|
||||
User defined signal 1.
|
||||
|
||||
``SIGUSR2``
|
||||
User defined signal 2.
|
||||
|
||||
The custom signal is sent to the test process to give it a chance
|
||||
to exit gracefully during a grace period:
|
||||
|
||||
* If the test process created any children, it is responsible for
|
||||
terminating them too.
|
||||
|
||||
* The grace period length is determined by the
|
||||
:prop_test:`TIMEOUT_SIGNAL_GRACE_PERIOD` test property.
|
||||
|
||||
* If the test process does not terminate before the grace period ends,
|
||||
:manual:`ctest(1)` will force termination of its entire process tree
|
||||
via ``SIGSTOP`` and ``SIGKILL``.
|
||||
|
||||
See also :variable:`CTEST_TEST_TIMEOUT`,
|
||||
:prop_test:`TIMEOUT`, and :prop_test:`TIMEOUT_AFTER_MATCH`.
|
||||
7
Help/release/dev/ctest-timeout-signal.rst
Normal file
7
Help/release/dev/ctest-timeout-signal.rst
Normal file
@@ -0,0 +1,7 @@
|
||||
ctest-timeout-signal
|
||||
--------------------
|
||||
|
||||
* The :prop_test:`TIMEOUT_SIGNAL_NAME` and
|
||||
:prop_test:`TIMEOUT_SIGNAL_GRACE_PERIOD` test properties were added
|
||||
to specify a POSIX signal to send to a test process when its timeout
|
||||
is reached.
|
||||
@@ -181,6 +181,11 @@ cmCTestRunTest::EndTestResult cmCTestRunTest::EndTest(size_t completed,
|
||||
}
|
||||
} else if (res == cmProcess::State::Expired) {
|
||||
outputStream << "***Timeout ";
|
||||
if (this->TestProperties->TimeoutSignal &&
|
||||
this->TestProcess->GetTerminationStyle() ==
|
||||
cmProcess::Termination::Custom) {
|
||||
outputStream << "(" << this->TestProperties->TimeoutSignal->Name << ") ";
|
||||
}
|
||||
this->TestResult.Status = cmCTestTestHandler::TIMEOUT;
|
||||
outputTestErrorsToConsole =
|
||||
this->CTest->GetOutputTestOutputOnTestFailure();
|
||||
@@ -540,6 +545,19 @@ bool cmCTestRunTest::StartTest(size_t completed, size_t total)
|
||||
this->TestResult.Name = this->TestProperties->Name;
|
||||
this->TestResult.Path = this->TestProperties->Directory;
|
||||
|
||||
// Reject invalid test properties.
|
||||
if (this->TestProperties->Error) {
|
||||
std::string const& msg = *this->TestProperties->Error;
|
||||
*this->TestHandler->LogFile << msg << std::endl;
|
||||
cmCTestLog(this->CTest, HANDLER_OUTPUT, msg << std::endl);
|
||||
this->TestResult.CompletionStatus = "Invalid Test Properties";
|
||||
this->TestResult.Status = cmCTestTestHandler::NOT_RUN;
|
||||
this->TestResult.Output = msg;
|
||||
this->TestResult.FullCommandLine.clear();
|
||||
this->TestResult.Environment.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return immediately if test is disabled
|
||||
if (this->TestProperties->Disabled) {
|
||||
this->TestResult.CompletionStatus = "Disabled";
|
||||
|
||||
@@ -17,6 +17,10 @@
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
#ifndef _WIN32
|
||||
# include <csignal>
|
||||
#endif
|
||||
|
||||
#include <cm/memory>
|
||||
#include <cm/string_view>
|
||||
#include <cmext/algorithm>
|
||||
@@ -2171,6 +2175,16 @@ void cmCTestTestHandler::CleanTestOutput(std::string& output, size_t length,
|
||||
}
|
||||
}
|
||||
|
||||
void cmCTestTestHandler::cmCTestTestProperties::AppendError(
|
||||
cm::string_view err)
|
||||
{
|
||||
if (this->Error) {
|
||||
*this->Error = cmStrCat(*this->Error, '\n', err);
|
||||
} else {
|
||||
this->Error = err;
|
||||
}
|
||||
}
|
||||
|
||||
bool cmCTestTestHandler::SetTestsProperties(
|
||||
const std::vector<std::string>& args)
|
||||
{
|
||||
@@ -2247,6 +2261,53 @@ bool cmCTestTestHandler::SetTestsProperties(
|
||||
rt.FixturesRequired.insert(lval.begin(), lval.end());
|
||||
} else if (key == "TIMEOUT"_s) {
|
||||
rt.Timeout = cmDuration(atof(val.c_str()));
|
||||
} else if (key == "TIMEOUT_SIGNAL_NAME"_s) {
|
||||
#ifdef _WIN32
|
||||
rt.AppendError("TIMEOUT_SIGNAL_NAME is not supported on Windows.");
|
||||
#else
|
||||
std::string const& signalName = val;
|
||||
Signal s;
|
||||
if (signalName == "SIGINT"_s) {
|
||||
s.Number = SIGINT;
|
||||
} else if (signalName == "SIGQUIT"_s) {
|
||||
s.Number = SIGQUIT;
|
||||
} else if (signalName == "SIGTERM"_s) {
|
||||
s.Number = SIGTERM;
|
||||
} else if (signalName == "SIGUSR1"_s) {
|
||||
s.Number = SIGUSR1;
|
||||
} else if (signalName == "SIGUSR2"_s) {
|
||||
s.Number = SIGUSR2;
|
||||
}
|
||||
if (s.Number) {
|
||||
s.Name = signalName;
|
||||
rt.TimeoutSignal = std::move(s);
|
||||
} else {
|
||||
rt.AppendError(cmStrCat("TIMEOUT_SIGNAL_NAME \"", signalName,
|
||||
"\" not supported on this platform."));
|
||||
}
|
||||
#endif
|
||||
} else if (key == "TIMEOUT_SIGNAL_GRACE_PERIOD"_s) {
|
||||
#ifdef _WIN32
|
||||
rt.AppendError(
|
||||
"TIMEOUT_SIGNAL_GRACE_PERIOD is not supported on Windows.");
|
||||
#else
|
||||
std::string const& gracePeriod = val;
|
||||
static cmDuration minGracePeriod{ 0 };
|
||||
static cmDuration maxGracePeriod{ 60 };
|
||||
cmDuration gp = cmDuration(atof(gracePeriod.c_str()));
|
||||
if (gp <= minGracePeriod) {
|
||||
rt.AppendError(cmStrCat("TIMEOUT_SIGNAL_GRACE_PERIOD \"",
|
||||
gracePeriod, "\" is not greater than \"",
|
||||
minGracePeriod.count(), "\" seconds."));
|
||||
} else if (gp > maxGracePeriod) {
|
||||
rt.AppendError(cmStrCat("TIMEOUT_SIGNAL_GRACE_PERIOD \"",
|
||||
gracePeriod,
|
||||
"\" is not less than the maximum of \"",
|
||||
maxGracePeriod.count(), "\" seconds."));
|
||||
} else {
|
||||
rt.TimeoutGracePeriod = gp;
|
||||
}
|
||||
#endif
|
||||
} else if (key == "COST"_s) {
|
||||
rt.Cost = static_cast<float>(atof(val.c_str()));
|
||||
} else if (key == "REQUIRED_FILES"_s) {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include <cm/optional>
|
||||
#include <cm/string_view>
|
||||
|
||||
#include "cmsys/RegularExpression.hxx"
|
||||
|
||||
@@ -119,8 +120,16 @@ public:
|
||||
bool operator!=(const cmCTestTestResourceRequirement& other) const;
|
||||
};
|
||||
|
||||
struct Signal
|
||||
{
|
||||
int Number = 0;
|
||||
std::string Name;
|
||||
};
|
||||
|
||||
struct cmCTestTestProperties
|
||||
{
|
||||
void AppendError(cm::string_view err);
|
||||
cm::optional<std::string> Error;
|
||||
std::string Name;
|
||||
std::string Directory;
|
||||
std::vector<std::string> Args;
|
||||
@@ -144,6 +153,8 @@ public:
|
||||
int PreviousRuns = 0;
|
||||
bool RunSerial = false;
|
||||
cm::optional<cmDuration> Timeout;
|
||||
cm::optional<Signal> TimeoutSignal;
|
||||
cm::optional<cmDuration> TimeoutGracePeriod;
|
||||
cmDuration AlternateTimeout;
|
||||
int Index = 0;
|
||||
// Requested number of process slots
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include "cmCTest.h"
|
||||
#include "cmCTestRunTest.h"
|
||||
#include "cmCTestTestHandler.h"
|
||||
#include "cmGetPipes.h"
|
||||
#include "cmStringAlgorithms.h"
|
||||
#if defined(_WIN32)
|
||||
@@ -274,7 +275,29 @@ void cmProcess::OnTimeoutCB(uv_timer_t* timer)
|
||||
|
||||
void cmProcess::OnTimeout()
|
||||
{
|
||||
bool const wasExecuting = this->ProcessState == cmProcess::State::Executing;
|
||||
this->ProcessState = cmProcess::State::Expired;
|
||||
|
||||
// If the test process is still executing normally, and we timed out because
|
||||
// the test timeout was reached, send the custom timeout signal, if any.
|
||||
if (wasExecuting && this->TimeoutReason_ == TimeoutReason::Normal) {
|
||||
cmCTestTestHandler::cmCTestTestProperties* p =
|
||||
this->Runner->GetTestProperties();
|
||||
if (p->TimeoutSignal) {
|
||||
this->TerminationStyle = Termination::Custom;
|
||||
uv_process_kill(this->Process, p->TimeoutSignal->Number);
|
||||
if (p->TimeoutGracePeriod) {
|
||||
this->Timeout = *p->TimeoutGracePeriod;
|
||||
} else {
|
||||
static const cmDuration defaultGracePeriod{ 1.0 };
|
||||
this->Timeout = defaultGracePeriod;
|
||||
}
|
||||
this->StartTimer();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this->TerminationStyle = Termination::Forced;
|
||||
bool const was_still_reading = !this->ReadHandleClosed;
|
||||
if (!this->ReadHandleClosed) {
|
||||
this->ReadHandleClosed = true;
|
||||
|
||||
@@ -85,6 +85,14 @@ public:
|
||||
return std::move(this->Runner);
|
||||
}
|
||||
|
||||
enum class Termination
|
||||
{
|
||||
Normal,
|
||||
Custom,
|
||||
Forced,
|
||||
};
|
||||
Termination GetTerminationStyle() const { return this->TerminationStyle; }
|
||||
|
||||
private:
|
||||
cm::optional<cmDuration> Timeout;
|
||||
TimeoutReason TimeoutReason_ = TimeoutReason::Normal;
|
||||
@@ -137,4 +145,5 @@ private:
|
||||
std::vector<const char*> ProcessArgs;
|
||||
int Id;
|
||||
int64_t ExitValue;
|
||||
Termination TerminationStyle = Termination::Normal;
|
||||
};
|
||||
|
||||
@@ -479,3 +479,32 @@ set_tests_properties(test5 PROPERTIES SKIP_REGULAR_EXPRESSION \"please skip\")
|
||||
run_cmake_command(output-junit ${CMAKE_CTEST_COMMAND} --output-junit "${RunCMake_TEST_BINARY_DIR}/junit.xml")
|
||||
endfunction()
|
||||
run_output_junit()
|
||||
|
||||
if(WIN32)
|
||||
block()
|
||||
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/TimeoutSignalWindows)
|
||||
set(RunCMake_TEST_NO_CLEAN 1)
|
||||
file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
|
||||
file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/CTestTestfile.cmake" "
|
||||
add_test(test1 \"${CMAKE_COMMAND}\" -E true)
|
||||
set_tests_properties(test1 PROPERTIES TIMEOUT_SIGNAL_NAME SIGUSR1)
|
||||
set_tests_properties(test1 PROPERTIES TIMEOUT_SIGNAL_GRACE_PERIOD 1)
|
||||
")
|
||||
run_cmake_command(TimeoutSignalWindows ${CMAKE_CTEST_COMMAND})
|
||||
endblock()
|
||||
else()
|
||||
block()
|
||||
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/TimeoutSignalBad)
|
||||
set(RunCMake_TEST_NO_CLEAN 1)
|
||||
file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
|
||||
file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/CTestTestfile.cmake" "
|
||||
add_test(test1 \"${CMAKE_COMMAND}\" -E true)
|
||||
set_tests_properties(test1 PROPERTIES TIMEOUT_SIGNAL_NAME NOTASIG)
|
||||
set_tests_properties(test1 PROPERTIES TIMEOUT_SIGNAL_GRACE_PERIOD 0)
|
||||
set_tests_properties(test1 PROPERTIES TIMEOUT_SIGNAL_GRACE_PERIOD 1000)
|
||||
")
|
||||
run_cmake_command(TimeoutSignalBad ${CMAKE_CTEST_COMMAND})
|
||||
endblock()
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
[^0]
|
||||
@@ -0,0 +1 @@
|
||||
Errors while running CTest
|
||||
@@ -0,0 +1,5 @@
|
||||
Start 1: test1
|
||||
TIMEOUT_SIGNAL_NAME "NOTASIG" not supported on this platform\.
|
||||
TIMEOUT_SIGNAL_GRACE_PERIOD "0" is not greater than "0" seconds\.
|
||||
TIMEOUT_SIGNAL_GRACE_PERIOD "1000" is not less than the maximum of "60" seconds\.
|
||||
1/1 Test #1: test1 ............................\*\*\*Not Run +[0-9.]+ sec
|
||||
@@ -0,0 +1 @@
|
||||
[^0]
|
||||
@@ -0,0 +1 @@
|
||||
Errors while running CTest
|
||||
@@ -0,0 +1,4 @@
|
||||
Start 1: test1
|
||||
TIMEOUT_SIGNAL_NAME is not supported on Windows\.
|
||||
TIMEOUT_SIGNAL_GRACE_PERIOD is not supported on Windows\.
|
||||
1/1 Test #1: test1 ............................\*\*\*Not Run +[0-9.]+ sec
|
||||
@@ -2,6 +2,10 @@ cmake_minimum_required(VERSION 3.16)
|
||||
project(CTestTest@CASE_NAME@ C)
|
||||
include(CTest)
|
||||
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "SunPro" AND CMAKE_C_COMPILER_VERSION VERSION_LESS 5.14)
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
endif()
|
||||
|
||||
add_executable(TestTimeout TestTimeout.c)
|
||||
|
||||
if(NOT DEFINED TIMEOUT)
|
||||
|
||||
@@ -13,12 +13,68 @@ endfunction()
|
||||
|
||||
run_ctest_timeout(Basic)
|
||||
|
||||
if(UNIX)
|
||||
if(WIN32)
|
||||
string(CONCAT CASE_CMAKELISTS_SUFFIX_CODE [[
|
||||
set_tests_properties(TestTimeout PROPERTIES
|
||||
TIMEOUT_SIGNAL_NAME SIGUSR1
|
||||
TIMEOUT_SIGNAL_GRACE_PERIOD 1.2
|
||||
)
|
||||
]])
|
||||
run_ctest_timeout(SignalWindows)
|
||||
unset(CASE_CMAKELISTS_SUFFIX_CODE)
|
||||
|
||||
else()
|
||||
string(CONCAT CASE_CMAKELISTS_SUFFIX_CODE [[
|
||||
target_compile_definitions(TestTimeout PRIVATE FORK)
|
||||
]])
|
||||
run_ctest_timeout(Fork)
|
||||
unset(CASE_CMAKELISTS_SUFFIX_CODE)
|
||||
|
||||
string(CONCAT CASE_CMAKELISTS_SUFFIX_CODE [[
|
||||
target_compile_definitions(TestTimeout PRIVATE SIGNAL)
|
||||
set_tests_properties(TestTimeout PROPERTIES
|
||||
TIMEOUT_SIGNAL_NAME SIGUSR1
|
||||
TIMEOUT_SIGNAL_GRACE_PERIOD 1.2
|
||||
)
|
||||
]])
|
||||
run_ctest_timeout(Signal)
|
||||
unset(CASE_CMAKELISTS_SUFFIX_CODE)
|
||||
|
||||
string(CONCAT CASE_CMAKELISTS_SUFFIX_CODE [[
|
||||
target_compile_definitions(TestTimeout PRIVATE SIGNAL SIGNAL_IGNORE=1)
|
||||
set_tests_properties(TestTimeout PROPERTIES
|
||||
TIMEOUT_SIGNAL_NAME SIGUSR1
|
||||
# Use default TIMEOUT_SIGNAL_GRACE_PERIOD of 1.
|
||||
)
|
||||
]])
|
||||
run_ctest_timeout(SignalIgnore)
|
||||
unset(CASE_CMAKELISTS_SUFFIX_CODE)
|
||||
|
||||
string(CONCAT CASE_CMAKELISTS_SUFFIX_CODE [[
|
||||
set_tests_properties(TestTimeout PROPERTIES
|
||||
TIMEOUT_SIGNAL_NAME NOTASIG
|
||||
)
|
||||
]])
|
||||
run_ctest_timeout(SignalUnknown)
|
||||
unset(CASE_CMAKELISTS_SUFFIX_CODE)
|
||||
|
||||
string(CONCAT CASE_CMAKELISTS_SUFFIX_CODE [[
|
||||
set_tests_properties(TestTimeout PROPERTIES
|
||||
TIMEOUT_SIGNAL_NAME SIGUSR1
|
||||
TIMEOUT_SIGNAL_GRACE_PERIOD -1
|
||||
)
|
||||
]])
|
||||
run_ctest_timeout(SignalGraceLow)
|
||||
unset(CASE_CMAKELISTS_SUFFIX_CODE)
|
||||
|
||||
string(CONCAT CASE_CMAKELISTS_SUFFIX_CODE [[
|
||||
set_tests_properties(TestTimeout PROPERTIES
|
||||
TIMEOUT_SIGNAL_NAME SIGUSR1
|
||||
TIMEOUT_SIGNAL_GRACE_PERIOD 1000
|
||||
)
|
||||
]])
|
||||
run_ctest_timeout(SignalGraceHigh)
|
||||
unset(CASE_CMAKELISTS_SUFFIX_CODE)
|
||||
endif()
|
||||
|
||||
block()
|
||||
|
||||
11
Tests/RunCMake/CTestTimeout/Signal-check.cmake
Normal file
11
Tests/RunCMake/CTestTimeout/Signal-check.cmake
Normal file
@@ -0,0 +1,11 @@
|
||||
file(GLOB test_xml "${RunCMake_TEST_BINARY_DIR}/Testing/*/Test.xml")
|
||||
if(NOT test_xml)
|
||||
set(RunCMake_TEST_FAILED "Test.xml not found.")
|
||||
return()
|
||||
endif()
|
||||
|
||||
file(READ "${test_xml}" test_xml_content)
|
||||
if(NOT test_xml_content MATCHES "SIGUSR1")
|
||||
set(RunCMake_TEST_FAILED "Test output does not mention SIGUSR1.")
|
||||
return()
|
||||
endif()
|
||||
6
Tests/RunCMake/CTestTimeout/Signal-stdout.txt
Normal file
6
Tests/RunCMake/CTestTimeout/Signal-stdout.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
Test project [^
|
||||
]*/Tests/RunCMake/CTestTimeout/Signal-build
|
||||
Start 1: TestTimeout
|
||||
1/1 Test #1: TestTimeout ......................\*\*\*Timeout \(SIGUSR1\) +[1-9][0-9.]* sec
|
||||
+
|
||||
0% tests passed, 1 tests failed out of 1
|
||||
11
Tests/RunCMake/CTestTimeout/SignalGraceHigh-check.cmake
Normal file
11
Tests/RunCMake/CTestTimeout/SignalGraceHigh-check.cmake
Normal file
@@ -0,0 +1,11 @@
|
||||
file(GLOB test_xml "${RunCMake_TEST_BINARY_DIR}/Testing/*/Test.xml")
|
||||
if(NOT test_xml)
|
||||
set(RunCMake_TEST_FAILED "Test.xml not found.")
|
||||
return()
|
||||
endif()
|
||||
|
||||
file(READ "${test_xml}" test_xml_content)
|
||||
if(NOT test_xml_content MATCHES "TIMEOUT_SIGNAL_GRACE_PERIOD \"1000\" is not less than the maximum of \"60\" seconds\\.")
|
||||
set(RunCMake_TEST_FAILED "Test output does not have expected error message.")
|
||||
return()
|
||||
endif()
|
||||
3
Tests/RunCMake/CTestTimeout/SignalGraceHigh-stdout.txt
Normal file
3
Tests/RunCMake/CTestTimeout/SignalGraceHigh-stdout.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
Start 1: TestTimeout
|
||||
TIMEOUT_SIGNAL_GRACE_PERIOD "1000" is not less than the maximum of "60" seconds\.
|
||||
1/1 Test #1: TestTimeout ......................\*\*\*Not Run +[0-9.]+ sec
|
||||
11
Tests/RunCMake/CTestTimeout/SignalGraceLow-check.cmake
Normal file
11
Tests/RunCMake/CTestTimeout/SignalGraceLow-check.cmake
Normal file
@@ -0,0 +1,11 @@
|
||||
file(GLOB test_xml "${RunCMake_TEST_BINARY_DIR}/Testing/*/Test.xml")
|
||||
if(NOT test_xml)
|
||||
set(RunCMake_TEST_FAILED "Test.xml not found.")
|
||||
return()
|
||||
endif()
|
||||
|
||||
file(READ "${test_xml}" test_xml_content)
|
||||
if(NOT test_xml_content MATCHES "TIMEOUT_SIGNAL_GRACE_PERIOD \"-1\" is not greater than \"0\" seconds\\.")
|
||||
set(RunCMake_TEST_FAILED "Test output does not have expected error message.")
|
||||
return()
|
||||
endif()
|
||||
3
Tests/RunCMake/CTestTimeout/SignalGraceLow-stdout.txt
Normal file
3
Tests/RunCMake/CTestTimeout/SignalGraceLow-stdout.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
Start 1: TestTimeout
|
||||
TIMEOUT_SIGNAL_GRACE_PERIOD "-1" is not greater than "0" seconds.
|
||||
1/1 Test #1: TestTimeout ......................\*\*\*Not Run +[0-9.]+ sec
|
||||
11
Tests/RunCMake/CTestTimeout/SignalIgnore-check.cmake
Normal file
11
Tests/RunCMake/CTestTimeout/SignalIgnore-check.cmake
Normal file
@@ -0,0 +1,11 @@
|
||||
file(GLOB test_xml "${RunCMake_TEST_BINARY_DIR}/Testing/*/Test.xml")
|
||||
if(NOT test_xml)
|
||||
set(RunCMake_TEST_FAILED "Test.xml not found.")
|
||||
return()
|
||||
endif()
|
||||
|
||||
file(READ "${test_xml}" test_xml_content)
|
||||
if(NOT test_xml_content MATCHES "EINTR")
|
||||
set(RunCMake_TEST_FAILED "Test output does not mention EINTR.")
|
||||
return()
|
||||
endif()
|
||||
6
Tests/RunCMake/CTestTimeout/SignalIgnore-stdout.txt
Normal file
6
Tests/RunCMake/CTestTimeout/SignalIgnore-stdout.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
Test project [^
|
||||
]*/Tests/RunCMake/CTestTimeout/SignalIgnore-build
|
||||
Start 1: TestTimeout
|
||||
1/1 Test #1: TestTimeout ......................\*\*\*Timeout +[1-9][0-9.]* sec
|
||||
+
|
||||
0% tests passed, 1 tests failed out of 1
|
||||
11
Tests/RunCMake/CTestTimeout/SignalUnknown-check.cmake
Normal file
11
Tests/RunCMake/CTestTimeout/SignalUnknown-check.cmake
Normal file
@@ -0,0 +1,11 @@
|
||||
file(GLOB test_xml "${RunCMake_TEST_BINARY_DIR}/Testing/*/Test.xml")
|
||||
if(NOT test_xml)
|
||||
set(RunCMake_TEST_FAILED "Test.xml not found.")
|
||||
return()
|
||||
endif()
|
||||
|
||||
file(READ "${test_xml}" test_xml_content)
|
||||
if(NOT test_xml_content MATCHES "TIMEOUT_SIGNAL_NAME \"NOTASIG\" not supported on this platform\\.")
|
||||
set(RunCMake_TEST_FAILED "Test output does not have expected error message.")
|
||||
return()
|
||||
endif()
|
||||
3
Tests/RunCMake/CTestTimeout/SignalUnknown-stdout.txt
Normal file
3
Tests/RunCMake/CTestTimeout/SignalUnknown-stdout.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
Start 1: TestTimeout
|
||||
TIMEOUT_SIGNAL_NAME "NOTASIG" not supported on this platform\.
|
||||
1/1 Test #1: TestTimeout ......................\*\*\*Not Run +[0-9.]+ sec
|
||||
11
Tests/RunCMake/CTestTimeout/SignalWindows-check.cmake
Normal file
11
Tests/RunCMake/CTestTimeout/SignalWindows-check.cmake
Normal file
@@ -0,0 +1,11 @@
|
||||
file(GLOB test_xml "${RunCMake_TEST_BINARY_DIR}/Testing/*/Test.xml")
|
||||
if(NOT test_xml)
|
||||
set(RunCMake_TEST_FAILED "Test.xml not found.")
|
||||
return()
|
||||
endif()
|
||||
|
||||
file(READ "${test_xml}" test_xml_content)
|
||||
if(NOT test_xml_content MATCHES "TIMEOUT_SIGNAL_NAME is not supported on Windows\\.")
|
||||
set(RunCMake_TEST_FAILED "Test output does not have expected error message.")
|
||||
return()
|
||||
endif()
|
||||
4
Tests/RunCMake/CTestTimeout/SignalWindows-stdout.txt
Normal file
4
Tests/RunCMake/CTestTimeout/SignalWindows-stdout.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
Start 1: TestTimeout
|
||||
TIMEOUT_SIGNAL_GRACE_PERIOD is not supported on Windows\.
|
||||
TIMEOUT_SIGNAL_NAME is not supported on Windows\.
|
||||
1/1 Test #1: TestTimeout ......................\*\*\*Not Run +[0-9.]+ sec
|
||||
@@ -1,3 +1,8 @@
|
||||
#if !defined(_WIN32) && !defined(__APPLE__) && !defined(__OpenBSD__)
|
||||
/* NOLINTNEXTLINE(bugprone-reserved-identifier) */
|
||||
# define _XOPEN_SOURCE 600
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
# include <windows.h>
|
||||
#else
|
||||
@@ -7,6 +12,19 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef SIGNAL
|
||||
# include <errno.h>
|
||||
# include <signal.h>
|
||||
# include <string.h>
|
||||
|
||||
static unsigned int signal_count;
|
||||
static void signal_handler(int signum)
|
||||
{
|
||||
(void)signum;
|
||||
++signal_count;
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(void)
|
||||
{
|
||||
#ifdef FORK
|
||||
@@ -16,10 +34,39 @@ int main(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SIGNAL
|
||||
struct sigaction sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_handler = signal_handler;
|
||||
while ((sigaction(SIGUSR1, &sa, NULL) < 0) && (errno == EINTR))
|
||||
;
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
Sleep((TIMEOUT + 4) * 1000);
|
||||
#elif defined(SIGNAL_IGNORE)
|
||||
# if defined(__CYGWIN__) || defined(__sun__)
|
||||
# define ERRNO_IS_EINTR (errno == EINTR || errno == 0)
|
||||
# else
|
||||
# define ERRNO_IS_EINTR (errno == EINTR)
|
||||
# endif
|
||||
{
|
||||
unsigned int timeLeft = (TIMEOUT + 4 + SIGNAL_IGNORE);
|
||||
while ((timeLeft = sleep(timeLeft), timeLeft > 0 && ERRNO_IS_EINTR)) {
|
||||
printf("EINTR: timeLeft=%u\n", timeLeft);
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
#else
|
||||
sleep((TIMEOUT + 4));
|
||||
#endif
|
||||
|
||||
#ifdef SIGNAL
|
||||
if (signal_count > 0) {
|
||||
printf("SIGUSR1: count=%u\n", signal_count);
|
||||
fflush(stdout);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -102,6 +102,7 @@
|
||||
{ symbol: [ "std::enable_if<true, std::chrono::duration<long, std::ratio<1, 1000> > >::type", private, "\"cmConfigure.h\"", public ] },
|
||||
{ symbol: [ "__gnu_cxx::__enable_if<true, bool>::__type", private, "\"cmConfigure.h\"", public ] },
|
||||
{ symbol: [ "std::remove_reference<std::basic_string<char, std::char_traits<char>, std::allocator<char> > &>::type", private, "\"cmConfigure.h\"", public ] },
|
||||
{ symbol: [ "std::remove_reference<cmCTestTestHandler::Signal &>::type", private, "\"cmConfigure.h\"", public ] },
|
||||
{ symbol: [ "std::remove_reference<Defer &>::type", private, "\"cmConfigure.h\"", public ] },
|
||||
{ symbol: [ "std::remove_reference<dap::StoppedEvent &>::type", private, "\"cmConfigure.h\"", public ] },
|
||||
|
||||
|
||||
Reference in New Issue
Block a user