summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2019-10-29 14:21:38 -0400
committerBrad King <brad.king@kitware.com>2019-10-29 15:14:36 -0400
commit39ac8b4eb5c5ea99cf1053bd37e76d811f5122fc (patch)
tree284a7da62ff3285b1809618e822dd1c693ec03dd
parent80c2c9d14cf1c1a8f162e119bd00d5f483a94af2 (diff)
downloadcmake-39ac8b4eb5c5ea99cf1053bd37e76d811f5122fc.tar.gz
ctest: Add --repeat-after-timeout option
Add an option to re-run tests if they timeout. This will help tolerate sporadic timeouts on busy machines.
-rw-r--r--Help/manual/ctest.1.rst8
-rw-r--r--Help/release/dev/ctest-repeat-until-pass.rst5
-rw-r--r--Source/CTest/cmCTestRunTest.cxx13
-rw-r--r--Source/cmCTest.cxx22
-rw-r--r--Source/cmCTest.h1
-rw-r--r--Source/ctest.cxx2
-rw-r--r--Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake24
-rw-r--r--Tests/RunCMake/CTestCommandLine/repeat-after-timeout-bad1-result.txt1
-rw-r--r--Tests/RunCMake/CTestCommandLine/repeat-after-timeout-bad1-stderr.txt1
-rw-r--r--Tests/RunCMake/CTestCommandLine/repeat-after-timeout-bad2-result.txt1
-rw-r--r--Tests/RunCMake/CTestCommandLine/repeat-after-timeout-bad2-stderr.txt1
-rw-r--r--Tests/RunCMake/CTestCommandLine/repeat-after-timeout-cmake.cmake15
-rw-r--r--Tests/RunCMake/CTestCommandLine/repeat-after-timeout-ctest-stdout.txt15
-rw-r--r--Tests/RunCMake/CTestCommandLine/repeat-after-timeout-good-stderr.txt1
-rw-r--r--Tests/RunCMake/CTestCommandLine/repeat-until-fail-and-timeout-result.txt1
-rw-r--r--Tests/RunCMake/CTestCommandLine/repeat-until-fail-and-timeout-stderr.txt1
-rw-r--r--Tests/RunCMake/CTestCommandLine/test1-timeout.cmake14
17 files changed, 119 insertions, 7 deletions
diff --git a/Help/manual/ctest.1.rst b/Help/manual/ctest.1.rst
index 031b9a7636..0097cee148 100644
--- a/Help/manual/ctest.1.rst
+++ b/Help/manual/ctest.1.rst
@@ -268,9 +268,17 @@ Options
``--repeat-until-pass <n>``
Allow each test to run up to ``<n>`` times in order to pass.
+ Repeats tests if they fail for any reason.
This is useful in tolerating sporadic failures in test cases.
+``--repeat-after-timeout <n>``
+ Allow each test to run up to ``<n>`` times in order to pass.
+ Repeats tests only if they timeout.
+
+ This is useful in tolerating sporadic timeouts in test cases
+ on busy machines.
+
``--max-width <width>``
Set the max width for a test name to output.
diff --git a/Help/release/dev/ctest-repeat-until-pass.rst b/Help/release/dev/ctest-repeat-until-pass.rst
index 003cdf2aec..d177247181 100644
--- a/Help/release/dev/ctest-repeat-until-pass.rst
+++ b/Help/release/dev/ctest-repeat-until-pass.rst
@@ -1,5 +1,6 @@
ctest-repeat-until-pass
-----------------------
-* The :manual:`ctest(1)` tool learned a new ``--repeat-until-pass <n>``
- option to help tolerate sporadic test failures.
+* The :manual:`ctest(1)` tool learned new ``--repeat-until-pass <n>``
+ and ``--repeat-after-timeout <n>`` options to help tolerate sporadic
+ test failures.
diff --git a/Source/CTest/cmCTestRunTest.cxx b/Source/CTest/cmCTestRunTest.cxx
index ce9e13bbba..ba146532b6 100644
--- a/Source/CTest/cmCTestRunTest.cxx
+++ b/Source/CTest/cmCTestRunTest.cxx
@@ -345,7 +345,9 @@ bool cmCTestRunTest::NeedsToRerun()
if ((this->RerunMode == cmCTest::Rerun::UntilFail &&
this->TestResult.Status == cmCTestTestHandler::COMPLETED) ||
(this->RerunMode == cmCTest::Rerun::UntilPass &&
- this->TestResult.Status != cmCTestTestHandler::COMPLETED)) {
+ this->TestResult.Status != cmCTestTestHandler::COMPLETED) ||
+ (this->RerunMode == cmCTest::Rerun::AfterTimeout &&
+ this->TestResult.Status == cmCTestTestHandler::TIMEOUT)) {
this->RunAgain = true;
return true;
}
@@ -745,10 +747,11 @@ void cmCTestRunTest::WriteLogOutputTop(size_t completed, size_t total)
// then it will never print out the completed / total, same would
// got for run until pass. Trick is when this is called we don't
// yet know if we are passing or failing.
- if ((this->RerunMode != cmCTest::Rerun::UntilPass &&
- this->NumberOfRunsLeft == 1) ||
- (this->RerunMode == cmCTest::Rerun::UntilPass &&
- this->NumberOfRunsLeft == this->NumberOfRunsTotal) ||
+ bool const progressOnLast =
+ (this->RerunMode != cmCTest::Rerun::UntilPass &&
+ this->RerunMode != cmCTest::Rerun::AfterTimeout);
+ if ((progressOnLast && this->NumberOfRunsLeft == 1) ||
+ (!progressOnLast && this->NumberOfRunsLeft == this->NumberOfRunsTotal) ||
this->CTest->GetTestProgressOutput()) {
outputStream << std::setw(getNumWidth(total)) << completed << "/";
outputStream << std::setw(getNumWidth(total)) << total << " ";
diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx
index 7276d98fba..20445b0853 100644
--- a/Source/cmCTest.cxx
+++ b/Source/cmCTest.cxx
@@ -1884,6 +1884,28 @@ bool cmCTest::HandleCommandLineArguments(size_t& i,
}
}
+ if (this->CheckArgument(arg, "--repeat-after-timeout")) {
+ if (i >= args.size() - 1) {
+ errormsg = "'--repeat-after-timeout' requires an argument";
+ return false;
+ }
+ if (this->Impl->RerunMode != cmCTest::Rerun::Never) {
+ errormsg = "At most one '--repeat-*' option may be used.";
+ return false;
+ }
+ i++;
+ long repeat = 1;
+ if (!cmStrToLong(args[i], &repeat)) {
+ errormsg =
+ "'--repeat-after-timeout' given non-integer value '" + args[i] + "'";
+ return false;
+ }
+ this->Impl->RepeatTests = static_cast<int>(repeat);
+ if (repeat > 1) {
+ this->Impl->RerunMode = cmCTest::Rerun::AfterTimeout;
+ }
+ }
+
if (this->CheckArgument(arg, "--test-load") && i < args.size() - 1) {
i++;
unsigned long load;
diff --git a/Source/cmCTest.h b/Source/cmCTest.h
index c6b89281d7..bef0f8d368 100644
--- a/Source/cmCTest.h
+++ b/Source/cmCTest.h
@@ -438,6 +438,7 @@ public:
Never,
UntilFail,
UntilPass,
+ AfterTimeout,
};
Rerun GetRerunMode() const;
diff --git a/Source/ctest.cxx b/Source/ctest.cxx
index f716d7a941..2659e3071b 100644
--- a/Source/ctest.cxx
+++ b/Source/ctest.cxx
@@ -102,6 +102,8 @@ static const char* cmDocumentationOptions[][2] = {
"Require each test to run <n> times without failing in order to pass" },
{ "--repeat-until-pass <n>",
"Allow each test to run up to <n> times in order to pass" },
+ { "--repeat-after-timeout <n>",
+ "Allow each test to run up to <n> times if it times out" },
{ "--max-width <width>", "Set the max width for a test name to output" },
{ "--interactive-debug-mode [0|1]", "Set the interactive mode to 0 or 1." },
{ "--hardware-spec-file <file>", "Set the hardware spec file to use." },
diff --git a/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake
index 15a90fc02f..d7f41336e8 100644
--- a/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake
@@ -24,12 +24,25 @@ run_cmake_command(repeat-until-fail-good
${CMAKE_CTEST_COMMAND} --repeat-until-fail 2
)
+run_cmake_command(repeat-after-timeout-bad1
+ ${CMAKE_CTEST_COMMAND} --repeat-after-timeout
+ )
+run_cmake_command(repeat-after-timeout-bad2
+ ${CMAKE_CTEST_COMMAND} --repeat-after-timeout foo
+ )
+run_cmake_command(repeat-after-timeout-good
+ ${CMAKE_CTEST_COMMAND} --repeat-after-timeout 2
+ )
+
run_cmake_command(repeat-until-pass-and-fail
${CMAKE_CTEST_COMMAND} --repeat-until-pass 2 --repeat-until-fail 2
)
run_cmake_command(repeat-until-fail-and-pass
${CMAKE_CTEST_COMMAND} --repeat-until-fail 2 --repeat-until-pass 2
)
+run_cmake_command(repeat-until-fail-and-timeout
+ ${CMAKE_CTEST_COMMAND} --repeat-until-fail 2 --repeat-after-timeout 2
+ )
function(run_repeat_until_pass_tests)
# Use a single build tree for a few tests without cleaning.
@@ -42,6 +55,17 @@ function(run_repeat_until_pass_tests)
endfunction()
run_repeat_until_pass_tests()
+function(run_repeat_after_timeout_tests)
+ # Use a single build tree for a few tests without cleaning.
+ set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/repeat-after-timeout-build)
+ run_cmake(repeat-after-timeout-cmake)
+ set(RunCMake_TEST_NO_CLEAN 1)
+ run_cmake_command(repeat-after-timeout-ctest
+ ${CMAKE_CTEST_COMMAND} -C Debug --repeat-after-timeout 3
+ )
+endfunction()
+run_repeat_after_timeout_tests()
+
function(run_repeat_until_fail_tests)
# Use a single build tree for a few tests without cleaning.
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/repeat-until-fail-build)
diff --git a/Tests/RunCMake/CTestCommandLine/repeat-after-timeout-bad1-result.txt b/Tests/RunCMake/CTestCommandLine/repeat-after-timeout-bad1-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandLine/repeat-after-timeout-bad1-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestCommandLine/repeat-after-timeout-bad1-stderr.txt b/Tests/RunCMake/CTestCommandLine/repeat-after-timeout-bad1-stderr.txt
new file mode 100644
index 0000000000..aea92b8bb0
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandLine/repeat-after-timeout-bad1-stderr.txt
@@ -0,0 +1 @@
+^CMake Error: '--repeat-after-timeout' requires an argument$
diff --git a/Tests/RunCMake/CTestCommandLine/repeat-after-timeout-bad2-result.txt b/Tests/RunCMake/CTestCommandLine/repeat-after-timeout-bad2-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandLine/repeat-after-timeout-bad2-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestCommandLine/repeat-after-timeout-bad2-stderr.txt b/Tests/RunCMake/CTestCommandLine/repeat-after-timeout-bad2-stderr.txt
new file mode 100644
index 0000000000..c5db55b65b
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandLine/repeat-after-timeout-bad2-stderr.txt
@@ -0,0 +1 @@
+^CMake Error: '--repeat-after-timeout' given non-integer value 'foo'$
diff --git a/Tests/RunCMake/CTestCommandLine/repeat-after-timeout-cmake.cmake b/Tests/RunCMake/CTestCommandLine/repeat-after-timeout-cmake.cmake
new file mode 100644
index 0000000000..4dc9c3648f
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandLine/repeat-after-timeout-cmake.cmake
@@ -0,0 +1,15 @@
+enable_testing()
+
+set(TEST_OUTPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/test_output.txt")
+add_test(NAME initialization
+ COMMAND ${CMAKE_COMMAND}
+ "-DTEST_OUTPUT_FILE=${TEST_OUTPUT_FILE}"
+ -P "${CMAKE_CURRENT_SOURCE_DIR}/init.cmake")
+add_test(NAME test1
+ COMMAND ${CMAKE_COMMAND}
+ "-DTEST_OUTPUT_FILE=${TEST_OUTPUT_FILE}"
+ -P "${CMAKE_CURRENT_SOURCE_DIR}/test1-timeout.cmake")
+set_tests_properties(test1 PROPERTIES DEPENDS "initialization" TIMEOUT 2)
+
+add_test(hello ${CMAKE_COMMAND} -E echo hello)
+add_test(goodbye ${CMAKE_COMMAND} -E echo goodbye)
diff --git a/Tests/RunCMake/CTestCommandLine/repeat-after-timeout-ctest-stdout.txt b/Tests/RunCMake/CTestCommandLine/repeat-after-timeout-ctest-stdout.txt
new file mode 100644
index 0000000000..d0a54872a9
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandLine/repeat-after-timeout-ctest-stdout.txt
@@ -0,0 +1,15 @@
+^Test project .*/Tests/RunCMake/CTestCommandLine/repeat-after-timeout-build
+ Start 1: initialization
+1/4 Test #1: initialization ................... Passed +[0-9.]+ sec
+ Start 2: test1
+2/4 Test #2: test1 ............................\*\*\*Timeout +[0-9.]+ sec
+ Start 2: test1
+ Test #2: test1 ............................ Passed +[0-9.]+ sec
+ Start 3: hello
+3/4 Test #3: hello ............................ Passed +[0-9.]+ sec
+ Start 4: goodbye
+4/4 Test #4: goodbye .......................... Passed +[0-9.]+ sec
+
+100% tests passed, 0 tests failed out of 4
+
+Total Test time \(real\) = +[0-9.]+ sec$
diff --git a/Tests/RunCMake/CTestCommandLine/repeat-after-timeout-good-stderr.txt b/Tests/RunCMake/CTestCommandLine/repeat-after-timeout-good-stderr.txt
new file mode 100644
index 0000000000..a7c4b11f76
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandLine/repeat-after-timeout-good-stderr.txt
@@ -0,0 +1 @@
+^No tests were found!!!$
diff --git a/Tests/RunCMake/CTestCommandLine/repeat-until-fail-and-timeout-result.txt b/Tests/RunCMake/CTestCommandLine/repeat-until-fail-and-timeout-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandLine/repeat-until-fail-and-timeout-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestCommandLine/repeat-until-fail-and-timeout-stderr.txt b/Tests/RunCMake/CTestCommandLine/repeat-until-fail-and-timeout-stderr.txt
new file mode 100644
index 0000000000..15ee3a98ea
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandLine/repeat-until-fail-and-timeout-stderr.txt
@@ -0,0 +1 @@
+^CMake Error: At most one '--repeat-\*' option may be used\.$
diff --git a/Tests/RunCMake/CTestCommandLine/test1-timeout.cmake b/Tests/RunCMake/CTestCommandLine/test1-timeout.cmake
new file mode 100644
index 0000000000..fbf2cccd46
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandLine/test1-timeout.cmake
@@ -0,0 +1,14 @@
+# This is run by test test1 in repeat-after-timeout-cmake.cmake with cmake -P.
+# It reads the file TEST_OUTPUT_FILE and increments the number
+# found in the file by 1. Unless the number is 2, then the
+# code sends out a cmake error causing the test to not timeout only on
+# the second time it is run.
+message("TEST_OUTPUT_FILE = ${TEST_OUTPUT_FILE}")
+file(READ "${TEST_OUTPUT_FILE}" COUNT)
+message("COUNT= ${COUNT}")
+math(EXPR COUNT "${COUNT} + 1")
+file(WRITE "${TEST_OUTPUT_FILE}" "${COUNT}")
+if(NOT COUNT EQUAL 2)
+ message("this test times out except on the 2nd run")
+ execute_process(COMMAND ${CMAKE_COMMAND} -E sleep 10)
+endif()