Browse Source

Add the --stop-time argument

Unit test and script hook for STOP_TIME
Zach Mullen 15 years ago
parent
commit
0ba9d04117

+ 52 - 2
Source/CTest/cmCTestRunTest.cxx

@@ -14,6 +14,7 @@
 #include "cmCTestMemCheckHandler.h"
 #include "cmCTest.h"
 #include "cmSystemTools.h"
+#include "cm_curl.h"
 
 #include <cm_zlib.h>
 #include <cmsys/Base64.h>
@@ -441,7 +442,7 @@ bool cmCTestRunTest::StartTest(size_t total)
     }
   this->StartTime = this->CTest->CurrentTime();
 
-  return this->CreateProcess(this->TestProperties->Timeout,
+  return this->ForkProcess(this->ResolveTimeout(),
                              &this->TestProperties->Environment);
 }
 
@@ -518,7 +519,56 @@ void cmCTestRunTest::DartProcessing()
 }
 
 //----------------------------------------------------------------------
-bool cmCTestRunTest::CreateProcess(double testTimeOut,
+double cmCTestRunTest::ResolveTimeout()
+{
+  double timeout = this->TestProperties->Timeout;
+
+  if(this->CTest->GetStopTime() == "")
+    {
+    return timeout;
+    }
+  struct tm* lctime;
+  time_t current_time = time(0);
+  lctime = gmtime(&current_time);
+  int gm_hour = lctime->tm_hour;
+  lctime = localtime(&current_time);
+  int local_hour = lctime->tm_hour;
+
+  int timezone = (local_hour - gm_hour) * 100;
+  char buf[1024];
+  // add todays year day and month to the time in str because
+  // curl_getdate no longer assumes the day is today
+  sprintf(buf, "%d%02d%02d %s %+05i",
+          lctime->tm_year + 1900,
+          lctime->tm_mon + 1,
+          lctime->tm_mday,
+          this->CTest->GetStopTime().c_str(),
+          timezone);
+
+  time_t stop_time = curl_getdate(buf, &current_time);
+  if(stop_time == -1)
+    {
+    return timeout;
+    }
+
+  //the stop time refers to the next day
+  if(this->CTest->NextDayStopTime)
+    {
+    stop_time += 24*60*60;
+    }
+  double stop_timeout = stop_time - current_time;
+
+  if(stop_timeout <= 0)
+    {
+    cmCTestLog(this->CTest, ERROR_MESSAGE, "The stop time has been passed. "
+      "Exiting ctest." << std::endl);
+    exit(-1);
+    }
+  return timeout == 0 ? stop_timeout : min(timeout, stop_timeout);
+}
+
+//----------------------------------------------------------------------
+bool cmCTestRunTest::ForkProcess(double testTimeOut,
                      std::vector<std::string>* environment)
 {
   this->TestProcess = new cmProcess;

+ 3 - 1
Source/CTest/cmCTestRunTest.h

@@ -59,7 +59,9 @@ public:
 private:
   void DartProcessing();
   void ExeNotFound(std::string exe);
-  bool CreateProcess(double testTimeOut,
+  // Figures out a final timeout which is min(STOP_TIME, NOW+TIMEOUT)
+  double ResolveTimeout();
+  bool ForkProcess(double testTimeOut,
                      std::vector<std::string>* environment);
   void WriteLogOutputTop(size_t completed, size_t total);
   //Run post processing of the process output for MemCheck

+ 5 - 0
Source/CTest/cmCTestTestCommand.cxx

@@ -25,6 +25,7 @@ cmCTestTestCommand::cmCTestTestCommand()
   this->Arguments[ctt_INCLUDE_LABEL] = "INCLUDE_LABEL";
   this->Arguments[ctt_PARALLEL_LEVEL] = "PARALLEL_LEVEL";
   this->Arguments[ctt_SCHEDULE_RANDOM] = "SCHEDULE_RANDOM";
+  this->Arguments[ctt_STOP_TIME] = "STOP_TIME";
   this->Arguments[ctt_LAST] = 0;
   this->Last = ctt_LAST;
 }
@@ -98,6 +99,10 @@ cmCTestGenericHandler* cmCTestTestCommand::InitializeHandler()
     handler->SetOption("ScheduleRandom",
                        this->Values[ctt_SCHEDULE_RANDOM]);
     }
+  if(this->Values[ctt_STOP_TIME])
+    {
+    this->CTest->SetStopTime(this->Values[ctt_STOP_TIME]);
+    }
   return handler;
 }
 

+ 5 - 2
Source/CTest/cmCTestTestCommand.h

@@ -62,7 +62,8 @@ public:
       "             [EXCLUDE_LABEL exclude regex] \n"
       "             [INCLUDE_LABEL label regex] \n"
       "             [PARALLEL_LEVEL level] \n"
-      "             [SCHEDULE_RANDOM on]) \n"
+      "             [SCHEDULE_RANDOM on] \n"
+      "             [STOP_TIME time of day]) \n"
       "Tests the given build directory and stores results in Test.xml. The "
       "second argument is a variable that will hold value. Optionally, "
       "you can specify the starting test number START, the ending test number "
@@ -73,7 +74,8 @@ public:
       "property LABEL. PARALLEL_LEVEL should be set to a positive number "
       "representing the number of tests to be run in parallel. "
       "SCHEDULE_RANDOM will launch tests in a random order, and is "
-      "typically used to detect implicit test dependencies."
+      "typically used to detect implicit test dependencies. STOP_TIME is the "
+      "time of day at which the tests should all stop running."
       "\n"
       CTEST_COMMAND_APPEND_OPTION_DOCS;
     }
@@ -96,6 +98,7 @@ protected:
     ctt_INCLUDE_LABEL,
     ctt_PARALLEL_LEVEL,
     ctt_SCHEDULE_RANDOM,
+    ctt_STOP_TIME,
     ctt_LAST
   };
 };

+ 42 - 0
Source/cmCTest.cxx

@@ -314,6 +314,8 @@ cmCTest::cmCTest()
   this->CompressXMLFiles       = false;
   this->CTestConfigFile        = "";
   this->ScheduleType           = "";
+  this->StopTime               = "";
+  this->NextDayStopTime        = false;
   this->OutputLogFile          = 0;
   this->OutputLogFileLastTag   = -1;
   this->SuppressUpdatingCTestConfiguration = false;
@@ -1881,6 +1883,12 @@ void cmCTest::HandleCommandLineArguments(size_t &i,
     double timeout = (double)atof(args[i].c_str());
     this->GlobalTimeout = timeout;
     }
+
+  if(this->CheckArgument(arg, "--stop-time") && i < args.size() - 1)
+    {
+    i++;
+    this->SetStopTime(args[i]);
+    }
   
   if(this->CheckArgument(arg, "-C", "--build-config") &&
      i < args.size() - 1)
@@ -2334,6 +2342,13 @@ void cmCTest::SetNotesFiles(const char* notes)
   this->NotesFiles = notes;
 }
 
+//----------------------------------------------------------------------
+void cmCTest::SetStopTime(std::string time)
+{
+  this->StopTime = time;
+  this->DetermineNextDayStop();
+}
+
 //----------------------------------------------------------------------
 int cmCTest::ReadCustomConfigurationFileTree(const char* dir, cmMakefile* mf)
 {
@@ -2535,6 +2550,33 @@ void cmCTest::EmptyCTestConfiguration()
   this->CTestConfiguration.clear();
 }
 
+//----------------------------------------------------------------------
+void cmCTest::DetermineNextDayStop()
+{
+  struct tm* lctime;
+  time_t current_time = time(0);
+  lctime = gmtime(&current_time);
+  int gm_hour = lctime->tm_hour;
+  lctime = localtime(&current_time);
+  int local_hour = lctime->tm_hour;
+
+  int timezone = (local_hour - gm_hour) * 100;
+  char buf[1024];
+  sprintf(buf, "%d%02d%02d %s %+05i",
+          lctime->tm_year + 1900,
+          lctime->tm_mon + 1,
+          lctime->tm_mday,
+          this->StopTime.c_str(),
+          timezone);
+
+  time_t stop_time = curl_getdate(buf, &current_time);
+
+  if(stop_time < current_time)
+    {
+    this->NextDayStopTime = true;
+    }
+}
+
 //----------------------------------------------------------------------
 void cmCTest::SetCTestConfiguration(const char *name, const char* value)
 {

+ 7 - 0
Source/cmCTest.h

@@ -214,6 +214,9 @@ public:
 
   std::string GetCDashVersion();
 
+  std::string GetStopTime() { return this->StopTime; }
+  void SetStopTime(std::string time);
+
   //Used for parallel ctest job scheduling
   std::string GetScheduleType() { return this->ScheduleType; }
   void SetScheduleType(std::string type) { this->ScheduleType = type; }
@@ -403,6 +406,8 @@ public:
 private:
   std::string ConfigType;
   std::string ScheduleType;
+  std::string StopTime;
+  bool NextDayStopTime;
   bool Verbose;
   bool ExtraVerbose;
   bool ProduceXML;
@@ -420,6 +425,8 @@ private:
 
   int GenerateNotesFile(const char* files);
 
+  void DetermineNextDayStop();
+
   // these are helper classes
   typedef std::map<cmStdString,cmCTestGenericHandler*> t_TestingHandlers;
   t_TestingHandlers TestingHandlers;

+ 4 - 0
Source/ctest.cxx

@@ -218,6 +218,10 @@ static const char * cmDocumentationOptions[][3] =
   {"--timeout <seconds>", "Set a global timeout on all tests.",
    "This option will set a global timeout on all tests that do not already "
    "have a timeout set on them."},
+  {"--stop-time <time>", "Set a time at which all tests should stop running.",
+   "Set a real time of day at which all tests should timeout. Example: "
+   "7:00:00 -0400. Any time format understood by the curl date parser is "
+   "accepted. Local time is assumed if no timezone is specified."},
   {"--http1.0", "Submit using HTTP 1.0.",
   "This option will force CTest to use HTTP 1.0 to submit files to the "
   "dashboard, instead of HTTP 1.1."},

+ 16 - 1
Tests/CMakeLists.txt

@@ -1304,7 +1304,22 @@ ${CMake_BINARY_DIR}/bin/cmake -DVERSION=master -P ${CMake_SOURCE_DIR}/Utilities/
     )
   SET_TESTS_PROPERTIES(CTestTestScheduler PROPERTIES
     PASS_REGULAR_EXPRESSION "Start 1.*Start 2.*Start 3.*Start 4.*Start 5.*Start 5.*Start 4.*Start 3.*Start 2.*Start 1")
-  
+
+  CONFIGURE_FILE(
+    "${CMake_SOURCE_DIR}/Tests/CTestTestStopTime/test.cmake.in"
+    "${CMake_BINARY_DIR}/Tests/CTestTestStopTime/test.cmake"
+    @ONLY ESCAPE_QUOTES)
+  CONFIGURE_FILE(
+    "${CMake_SOURCE_DIR}/Tests/CTestTestStopTime/GetDate.cmake"
+    "${CMake_BINARY_DIR}/Tests/CTestTestStopTime/GetDate.cmake"
+    COPYONLY)
+  ADD_TEST(CTestTestStopTime ${CMAKE_CTEST_COMMAND}
+    -S "${CMake_BINARY_DIR}/Tests/CTestTestStopTime/test.cmake" -V
+    --output-log "${CMake_BINARY_DIR}/Tests/CTestTestStopTime/testOutput.log"
+    )
+  SET_TESTS_PROPERTIES(CTestTestStopTime PROPERTIES
+    PASS_REGULAR_EXPRESSION "The stop time has been passed")
+
   CONFIGURE_FILE(
     "${CMake_SOURCE_DIR}/Tests/CTestTestSubdir/test.cmake.in"
     "${CMake_BINARY_DIR}/Tests/CTestTestSubdir/test.cmake"

+ 11 - 0
Tests/CTestTestStopTime/CMakeLists.txt

@@ -0,0 +1,11 @@
+cmake_minimum_required (VERSION 2.6)
+PROJECT(CTestTestStopTime)
+INCLUDE(CTest)
+
+ADD_EXECUTABLE (Sleep sleep.c)
+
+ADD_TEST (TestSleep Sleep 30)
+ADD_TEST (ShouldNotRun Sleep 30)
+
+SET_TESTS_PROPERTIES(ShouldNotRun PROPERTIES DEPENDS TestSleep)
+SET_TESTS_PROPERTIES(ShouldNotRun PROPERTIES WILL_FAIL ON)

+ 7 - 0
Tests/CTestTestStopTime/CTestConfig.cmake

@@ -0,0 +1,7 @@
+set (CTEST_PROJECT_NAME "CTestTestStopTime")
+set (CTEST_NIGHTLY_START_TIME "21:00:00 EDT")
+set (CTEST_DART_SERVER_VERSION "2")
+set(CTEST_DROP_METHOD "http")
+set(CTEST_DROP_SITE "www.cdash.org")
+set(CTEST_DROP_LOCATION "/CDash/submit.php?project=PublicDashboard")
+set(CTEST_DROP_SITE_CDASH TRUE)

+ 219 - 0
Tests/CTestTestStopTime/GetDate.cmake

@@ -0,0 +1,219 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.2)
+
+MACRO(GET_DATE)
+  #
+  # All macro arguments are optional.
+  #   If there's an ARGV0, use it as GD_PREFIX. Default = 'GD_'
+  #   If there's an ARGV1, use it as ${GD_PREFIX}VERBOSE. Default = '0'
+  #
+  # If the date can be retrieved and parsed successfully, this macro
+  # will set the following CMake variables:
+  #
+  #   GD_PREFIX
+  #   ${GD_PREFIX}PREFIX (if '${GD_PREFIX}' is not 'GD_'...!)
+  #   ${GD_PREFIX}VERBOSE
+  #
+  #   ${GD_PREFIX}CMD
+  #   ${GD_PREFIX}ARGS
+  #   ${GD_PREFIX}OV
+  #   ${GD_PREFIX}RV
+  #
+  #   ${GD_PREFIX}REGEX
+  #   ${GD_PREFIX}YEAR
+  #   ${GD_PREFIX}MONTH
+  #   ${GD_PREFIX}DAY
+  #   ${GD_PREFIX}HOUR
+  #   ${GD_PREFIX}MINUTE
+  #   ${GD_PREFIX}SECOND
+  #   ${GD_PREFIX}FRACTIONAL_SECOND
+  #   ${GD_PREFIX}DAY_OF_WEEK
+  #
+  # Caller can then use these variables to construct names based on
+  # date and time stamps...
+  #
+
+  # If there's an ARGV0, use it as GD_PREFIX:
+  #
+  SET(GD_PREFIX "GD_")
+  IF(NOT "${ARGV0}" STREQUAL "")
+    SET(GD_PREFIX "${ARGV0}")
+  ENDIF(NOT "${ARGV0}" STREQUAL "")
+  IF(NOT "${GD_PREFIX}" STREQUAL "GD_")
+    SET(${GD_PREFIX}PREFIX "${GD_PREFIX}")
+  ENDIF(NOT "${GD_PREFIX}" STREQUAL "GD_")
+
+  # If there's an ARGV1, use it as ${GD_PREFIX}VERBOSE:
+  #
+  SET(${GD_PREFIX}VERBOSE "0")
+  IF(NOT "${ARGV1}" STREQUAL "")
+    SET(${GD_PREFIX}VERBOSE "${ARGV1}")
+  ENDIF(NOT "${ARGV1}" STREQUAL "")
+
+  # Retrieve the current date and time in the format:
+  #
+  # Thu 01/12/2006  8:55:12.01
+  # dow mm/dd/YYYY HH:MM:SS.ssssss
+  #
+  # Use "echo %DATE% %TIME%" on Windows.
+  # Otherwise, try "date" as implemented on most Unix flavors.
+  #
+  IF(WIN32)
+    #
+    # Use "cmd" shell with %DATE% and %TIME% support...
+    # May need adjustment in different locales or for custom date/time formats
+    # set in the Windows Control Panel.
+    #
+    SET(${GD_PREFIX}CMD "cmd")
+    SET(${GD_PREFIX}ARGS "/c echo %DATE% %TIME%")
+  ELSE(WIN32)
+    #
+    # Match the format returned by default in US English Windows:
+    #
+    SET(${GD_PREFIX}CMD "date")
+    SET(${GD_PREFIX}ARGS "\"+%a %m/%d/%Y %H:%M:%S.00\"")
+  ENDIF(WIN32)
+
+  EXEC_PROGRAM("${${GD_PREFIX}CMD}" "." ARGS "${${GD_PREFIX}ARGS}"
+    OUTPUT_VARIABLE ${GD_PREFIX}OV RETURN_VALUE ${GD_PREFIX}RV
+    )
+
+  IF(${GD_PREFIX}VERBOSE)
+    MESSAGE(STATUS "")
+    MESSAGE(STATUS "<GET_DATE>")
+    MESSAGE(STATUS "")
+    MESSAGE(STATUS "GD_PREFIX='${GD_PREFIX}'")
+    IF(NOT "${GD_PREFIX}" STREQUAL "GD_")
+      MESSAGE(STATUS "${GD_PREFIX}PREFIX='${${GD_PREFIX}PREFIX}'")
+    ENDIF(NOT "${GD_PREFIX}" STREQUAL "GD_")
+    MESSAGE(STATUS "${GD_PREFIX}VERBOSE='${${GD_PREFIX}VERBOSE}'")
+    MESSAGE(STATUS "")
+    MESSAGE(STATUS "${GD_PREFIX}CMD='${${GD_PREFIX}CMD}'")
+    MESSAGE(STATUS "${GD_PREFIX}ARGS='${${GD_PREFIX}ARGS}'")
+    MESSAGE(STATUS "${GD_PREFIX}OV='${${GD_PREFIX}OV}'")
+    MESSAGE(STATUS "${GD_PREFIX}RV='${${GD_PREFIX}RV}'")
+    MESSAGE(STATUS "")
+  ENDIF(${GD_PREFIX}VERBOSE)
+
+  IF("${${GD_PREFIX}RV}" STREQUAL "0")
+    #
+    # Extract eight individual components by matching a regex with paren groupings.
+    # Use the replace functionality and \\1 thru \\8 to extract components.
+    #
+    SET(${GD_PREFIX}REGEX "([^ ]+) +([^/]+)/([^/]+)/([^ ]+) +([^:]+):([^:]+):([^\\.]+)\\.(.*)")
+
+    STRING(REGEX REPLACE "${${GD_PREFIX}REGEX}" "\\1" ${GD_PREFIX}DAY_OF_WEEK "${${GD_PREFIX}OV}")
+    STRING(REGEX REPLACE "${${GD_PREFIX}REGEX}" "\\2" ${GD_PREFIX}MONTH "${${GD_PREFIX}OV}")
+    STRING(REGEX REPLACE "${${GD_PREFIX}REGEX}" "\\3" ${GD_PREFIX}DAY "${${GD_PREFIX}OV}")
+    STRING(REGEX REPLACE "${${GD_PREFIX}REGEX}" "\\4" ${GD_PREFIX}YEAR "${${GD_PREFIX}OV}")
+    STRING(REGEX REPLACE "${${GD_PREFIX}REGEX}" "\\5" ${GD_PREFIX}HOUR "${${GD_PREFIX}OV}")
+    STRING(REGEX REPLACE "${${GD_PREFIX}REGEX}" "\\6" ${GD_PREFIX}MINUTE "${${GD_PREFIX}OV}")
+    STRING(REGEX REPLACE "${${GD_PREFIX}REGEX}" "\\7" ${GD_PREFIX}SECOND "${${GD_PREFIX}OV}")
+    STRING(REGEX REPLACE "${${GD_PREFIX}REGEX}" "\\8" ${GD_PREFIX}FRACTIONAL_SECOND "${${GD_PREFIX}OV}")
+
+    #
+    # Verify that extracted components don't have anything obviously
+    # wrong with them... Emit warnings if something looks suspicious...
+    #
+
+    # Expecting a four digit year:
+    #
+    IF(NOT "${${GD_PREFIX}YEAR}" MATCHES "^[0-9][0-9][0-9][0-9]$")
+      MESSAGE(STATUS "WARNING: Extracted ${GD_PREFIX}YEAR='${${GD_PREFIX}YEAR}' is not a four digit number...")
+    ENDIF(NOT "${${GD_PREFIX}YEAR}" MATCHES "^[0-9][0-9][0-9][0-9]$")
+
+    # Expecting month to be <= 12:
+    #
+    IF(${${GD_PREFIX}MONTH} GREATER 12)
+      MESSAGE(STATUS "WARNING: Extracted ${GD_PREFIX}MONTH='${${GD_PREFIX}MONTH}' is greater than 12!")
+    ENDIF(${${GD_PREFIX}MONTH} GREATER 12)
+
+    # Expecting day to be <= 31:
+    #
+    IF(${${GD_PREFIX}DAY} GREATER 31)
+      MESSAGE(STATUS "WARNING: Extracted ${GD_PREFIX}DAY='${${GD_PREFIX}DAY}' is greater than 31!")
+    ENDIF(${${GD_PREFIX}DAY} GREATER 31)
+
+    # Expecting hour to be <= 23:
+    #
+    IF(${${GD_PREFIX}HOUR} GREATER 23)
+      MESSAGE(STATUS "WARNING: Extracted ${GD_PREFIX}HOUR='${${GD_PREFIX}HOUR}' is greater than 23!")
+    ENDIF(${${GD_PREFIX}HOUR} GREATER 23)
+
+    # Expecting minute to be <= 59:
+    #
+    IF(${${GD_PREFIX}MINUTE} GREATER 59)
+      MESSAGE(STATUS "WARNING: Extracted ${GD_PREFIX}MINUTE='${${GD_PREFIX}MINUTE}' is greater than 59!")
+    ENDIF(${${GD_PREFIX}MINUTE} GREATER 59)
+
+    # Expecting second to be <= 59:
+    #
+    IF(${${GD_PREFIX}SECOND} GREATER 59)
+      MESSAGE(STATUS "WARNING: Extracted ${GD_PREFIX}SECOND='${${GD_PREFIX}SECOND}' is greater than 59!")
+    ENDIF(${${GD_PREFIX}SECOND} GREATER 59)
+
+    # If individual components are single digit,
+    # prepend a leading zero:
+    #
+    IF("${${GD_PREFIX}YEAR}" MATCHES "^[0-9]$")
+      SET(${GD_PREFIX}YEAR "0${${GD_PREFIX}YEAR}")
+    ENDIF("${${GD_PREFIX}YEAR}" MATCHES "^[0-9]$")
+    IF("${${GD_PREFIX}MONTH}" MATCHES "^[0-9]$")
+      SET(${GD_PREFIX}MONTH "0${${GD_PREFIX}MONTH}")
+    ENDIF("${${GD_PREFIX}MONTH}" MATCHES "^[0-9]$")
+    IF("${${GD_PREFIX}DAY}" MATCHES "^[0-9]$")
+      SET(${GD_PREFIX}DAY "0${${GD_PREFIX}DAY}")
+    ENDIF("${${GD_PREFIX}DAY}" MATCHES "^[0-9]$")
+    IF("${${GD_PREFIX}HOUR}" MATCHES "^[0-9]$")
+      SET(${GD_PREFIX}HOUR "0${${GD_PREFIX}HOUR}")
+    ENDIF("${${GD_PREFIX}HOUR}" MATCHES "^[0-9]$")
+    IF("${${GD_PREFIX}MINUTE}" MATCHES "^[0-9]$")
+      SET(${GD_PREFIX}MINUTE "0${${GD_PREFIX}MINUTE}")
+    ENDIF("${${GD_PREFIX}MINUTE}" MATCHES "^[0-9]$")
+    IF("${${GD_PREFIX}SECOND}" MATCHES "^[0-9]$")
+      SET(${GD_PREFIX}SECOND "0${${GD_PREFIX}SECOND}")
+    ENDIF("${${GD_PREFIX}SECOND}" MATCHES "^[0-9]$")
+
+    IF(${GD_PREFIX}VERBOSE)
+      MESSAGE(STATUS "${GD_PREFIX}REGEX='${${GD_PREFIX}REGEX}'")
+      MESSAGE(STATUS "${GD_PREFIX}YEAR='${${GD_PREFIX}YEAR}'")
+      MESSAGE(STATUS "${GD_PREFIX}MONTH='${${GD_PREFIX}MONTH}'")
+      MESSAGE(STATUS "${GD_PREFIX}DAY='${${GD_PREFIX}DAY}'")
+      MESSAGE(STATUS "${GD_PREFIX}HOUR='${${GD_PREFIX}HOUR}'")
+      MESSAGE(STATUS "${GD_PREFIX}MINUTE='${${GD_PREFIX}MINUTE}'")
+      MESSAGE(STATUS "${GD_PREFIX}SECOND='${${GD_PREFIX}SECOND}'")
+      MESSAGE(STATUS "${GD_PREFIX}FRACTIONAL_SECOND='${${GD_PREFIX}FRACTIONAL_SECOND}'")
+      MESSAGE(STATUS "${GD_PREFIX}DAY_OF_WEEK='${${GD_PREFIX}DAY_OF_WEEK}'")
+      MESSAGE(STATUS "")
+      MESSAGE(STATUS "Counters that change...")
+      MESSAGE(STATUS "")
+      MESSAGE(STATUS "...very very quickly : ${${GD_PREFIX}YEAR}${${GD_PREFIX}MONTH}${${GD_PREFIX}DAY}${${GD_PREFIX}HOUR}${${GD_PREFIX}MINUTE}${${GD_PREFIX}SECOND}${${GD_PREFIX}FRACTIONAL_SECOND}")
+      MESSAGE(STATUS "        every second : ${${GD_PREFIX}YEAR}${${GD_PREFIX}MONTH}${${GD_PREFIX}DAY}${${GD_PREFIX}HOUR}${${GD_PREFIX}MINUTE}${${GD_PREFIX}SECOND}")
+      MESSAGE(STATUS "               daily : ${${GD_PREFIX}YEAR}${${GD_PREFIX}MONTH}${${GD_PREFIX}DAY}")
+      MESSAGE(STATUS "             monthly : ${${GD_PREFIX}YEAR}${${GD_PREFIX}MONTH}")
+      MESSAGE(STATUS "            annually : ${${GD_PREFIX}YEAR}")
+      MESSAGE(STATUS "")
+    ENDIF(${GD_PREFIX}VERBOSE)
+  ELSE("${${GD_PREFIX}RV}" STREQUAL "0")
+    MESSAGE(SEND_ERROR "ERROR: MACRO(GET_DATE) failed. ${GD_PREFIX}CMD='${${GD_PREFIX}CMD}' ${GD_PREFIX}ARGS='${${GD_PREFIX}ARGS}' ${GD_PREFIX}OV='${${GD_PREFIX}OV}' ${GD_PREFIX}RV='${${GD_PREFIX}RV}'")
+  ENDIF("${${GD_PREFIX}RV}" STREQUAL "0")
+
+  IF(${GD_PREFIX}VERBOSE)
+    MESSAGE(STATUS "</GET_DATE>")
+    MESSAGE(STATUS "")
+  ENDIF(${GD_PREFIX}VERBOSE)
+ENDMACRO(GET_DATE)
+
+MACRO(ADD_SECONDS sec)
+  set(new_min ${${GD_PREFIX}MINUTE})
+  set(new_hr ${${GD_PREFIX}HOUR})
+  math(EXPR new_sec "${sec} + ${${GD_PREFIX}SECOND}")
+  while(${new_sec} GREATER 60 OR ${new_sec} EQUAL 60)
+    math(EXPR new_sec "${new_sec} - 60")
+    math(EXPR new_min "${${GD_PREFIX}MINUTE} + 1")
+  endwhile()
+  while(${new_min} GREATER 60 OR ${new_min} EQUAL 60)
+    math(EXPR new_min "${new_min} - 60")
+    math(EXPR new_hr "${${GD_PREFIX}HOUR} + 1")
+  endwhile()
+  math(EXPR new_hr "${new_hr} % 24")
+ENDMACRO(ADD_SECONDS)

+ 21 - 0
Tests/CTestTestStopTime/sleep.c

@@ -0,0 +1,21 @@
+#if defined(_WIN32)
+# include <windows.h>
+#else
+# include <unistd.h>
+#endif
+
+/* sleeps for n seconds, where n is the argument to the program */
+int main(int argc, char** argv)
+{
+  int time;
+  if(argc > 1)
+    {
+    time = atoi(argv[1]);
+    }
+#if defined(_WIN32)
+  Sleep(time * 1000);
+#else
+  sleep(time);
+#endif
+  return 0;
+}

+ 30 - 0
Tests/CTestTestStopTime/test.cmake.in

@@ -0,0 +1,30 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.1)
+
+# Settings:
+SET(CTEST_DASHBOARD_ROOT                "@CMake_BINARY_DIR@/Tests/CTestTest")
+SET(CTEST_SITE                          "@SITE@")
+SET(CTEST_BUILD_NAME                    "CTestTest-@BUILDNAME@-StopTime")
+
+SET(CTEST_SOURCE_DIRECTORY              "@CMake_SOURCE_DIR@/Tests/CTestTestStopTime")
+SET(CTEST_BINARY_DIRECTORY              "@CMake_BINARY_DIR@/Tests/CTestTestStopTime")
+SET(CTEST_CVS_COMMAND                   "@CVSCOMMAND@")
+SET(CTEST_CMAKE_GENERATOR               "@CMAKE_TEST_GENERATOR@")
+SET(CTEST_BUILD_CONFIGURATION           "$ENV{CMAKE_CONFIG_TYPE}")
+SET(CTEST_MEMORYCHECK_COMMAND           "@MEMORYCHECK_COMMAND@")
+SET(CTEST_MEMORYCHECK_SUPPRESSIONS_FILE "@MEMORYCHECK_SUPPRESSIONS_FILE@")
+SET(CTEST_MEMORYCHECK_COMMAND_OPTIONS   "@MEMORYCHECK_COMMAND_OPTIONS@")
+SET(CTEST_COVERAGE_COMMAND              "@COVERAGE_COMMAND@")
+SET(CTEST_NOTES_FILES                   "${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}")
+
+#CTEST_EMPTY_BINARY_DIRECTORY(${CTEST_BINARY_DIRECTORY})
+
+INCLUDE("${CTEST_BINARY_DIRECTORY}/GetDate.cmake")
+
+GET_DATE()
+ADD_SECONDS(15)
+
+CTEST_START(Experimental)
+CTEST_CONFIGURE(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res)
+CTEST_BUILD(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res)
+CTEST_TEST(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res STOP_TIME "${new_hr}:${new_min}:${new_sec}")
+#CTEST_SUBMIT()