From e525649a4e9f2578c41593b56f9af4b4b7719984 Mon Sep 17 00:00:00 2001
From: Zach Mullen <zach.mullen@kitware.com>
Date: Thu, 3 Jun 2010 10:34:34 -0400
Subject: Checksums on CTest submit files, and retry timed out submissions.

---
 Modules/CTest.cmake                   |   9 +-
 Modules/DartConfiguration.tcl.in      |   4 +
 Source/CTest/cmCTestSubmitCommand.cxx |  27 ++++++
 Source/CTest/cmCTestSubmitCommand.h   |   6 ++
 Source/CTest/cmCTestSubmitHandler.cxx | 170 ++++++++++++++++++++++++++++++----
 Source/CTest/cmCTestSubmitHandler.h   |   1 +
 Tests/CTestTest3/test.cmake.in        |   2 +-
 7 files changed, 198 insertions(+), 21 deletions(-)

diff --git a/Modules/CTest.cmake b/Modules/CTest.cmake
index 2d0702e..bdaf911 100644
--- a/Modules/CTest.cmake
+++ b/Modules/CTest.cmake
@@ -163,6 +163,11 @@ IF(BUILD_TESTING)
   SET(DART_TESTING_TIMEOUT 1500 CACHE STRING 
     "Maximum time allowed before CTest will kill the test.")
 
+  SET(CTEST_SUBMIT_RETRY_DELAY 5 CACHE STRING
+    "How long to wait between timed-out CTest submissions.")
+  SET(CTEST_SUBMIT_RETRY_COUNT 3 CACHE STRING
+    "How many times to retry timed-out CTest submissions.")
+
   FIND_PROGRAM(MEMORYCHECK_COMMAND
     NAMES purify valgrind boundscheck
     PATHS
@@ -262,7 +267,9 @@ IF(BUILD_TESTING)
     SCPCOMMAND
     SLURM_SBATCH_COMMAND
     SLURM_SRUN_COMMAND
-    SITE 
+    SITE
+    CTEST_SUBMIT_RETRY_DELAY
+    CTEST_SUBMIT_RETRY_COUNT
     )
   #  BUILDNAME 
   IF(NOT RUN_FROM_DART)
diff --git a/Modules/DartConfiguration.tcl.in b/Modules/DartConfiguration.tcl.in
index 85b4138..799ff79 100644
--- a/Modules/DartConfiguration.tcl.in
+++ b/Modules/DartConfiguration.tcl.in
@@ -83,3 +83,7 @@ CurlOptions: @CTEST_CURL_OPTIONS@
 # warning, if you add new options here that have to do with submit,
 # you have to update cmCTestSubmitCommand.cxx
 
+# For CTest submissions that timeout, these options
+# specify behavior for retrying the submission
+CTestRetryTime: @CTEST_SUBMIT_RETRY_DELAY@
+CTestRetryCount: @CTEST_SUBMIT_RETRY_COUNT@
diff --git a/Source/CTest/cmCTestSubmitCommand.cxx b/Source/CTest/cmCTestSubmitCommand.cxx
index d1226da..6a45d58 100644
--- a/Source/CTest/cmCTestSubmitCommand.cxx
+++ b/Source/CTest/cmCTestSubmitCommand.cxx
@@ -147,6 +147,11 @@ cmCTestGenericHandler* cmCTestSubmitCommand::InitializeHandler()
     static_cast<cmCTestSubmitHandler*>(handler)->SelectParts(this->Parts);
     }
 
+  static_cast<cmCTestSubmitHandler*>(handler)->SetOption("RetryTime",
+    this->RetryDelay.c_str());
+  static_cast<cmCTestSubmitHandler*>(handler)->SetOption("RetryCount",
+    this->RetryCount.c_str());
+
   return handler;
 }
 
@@ -169,6 +174,18 @@ bool cmCTestSubmitCommand::CheckArgumentKeyword(std::string const& arg)
     return true;
     }
 
+  if(arg == "RETRY_COUNT")
+    {
+    this->ArgumentDoing = ArgumentDoingRetryCount;
+    return true;
+    }
+
+  if(arg == "RETRY_DELAY")
+    {
+    this->ArgumentDoing = ArgumentDoingRetryDelay;
+    return true;
+    }
+
   // Look for other arguments.
   return this->Superclass::CheckArgumentKeyword(arg);
 }
@@ -213,6 +230,16 @@ bool cmCTestSubmitCommand::CheckArgumentValue(std::string const& arg)
     return true;
     }
 
+  if(this->ArgumentDoing == ArgumentDoingRetryCount)
+    {
+    this->RetryCount = arg;
+    }
+
+  if(this->ArgumentDoing == ArgumentDoingRetryDelay)
+    {
+    this->RetryDelay = arg;
+    }
+
   // Look for other arguments.
   return this->Superclass::CheckArgumentValue(arg);
 }
diff --git a/Source/CTest/cmCTestSubmitCommand.h b/Source/CTest/cmCTestSubmitCommand.h
index ccaef7e..14dfa37 100644
--- a/Source/CTest/cmCTestSubmitCommand.h
+++ b/Source/CTest/cmCTestSubmitCommand.h
@@ -29,6 +29,8 @@ public:
     {
     this->PartsMentioned = false;
     this->FilesMentioned = false;
+    this->RetryCount = "";
+    this->RetryDelay = "";
     }
 
   /**
@@ -92,6 +94,8 @@ protected:
   {
     ArgumentDoingParts = Superclass::ArgumentDoingLast1,
     ArgumentDoingFiles,
+    ArgumentDoingRetryDelay,
+    ArgumentDoingRetryCount,
     ArgumentDoingLast2
   };
 
@@ -99,6 +103,8 @@ protected:
   std::set<cmCTest::Part> Parts;
   bool FilesMentioned;
   cmCTest::SetOfStrings Files;
+  std::string RetryCount;
+  std::string RetryDelay;
 };
 
 
diff --git a/Source/CTest/cmCTestSubmitHandler.cxx b/Source/CTest/cmCTestSubmitHandler.cxx
index 7b4f38b..fca05ac 100644
--- a/Source/CTest/cmCTestSubmitHandler.cxx
+++ b/Source/CTest/cmCTestSubmitHandler.cxx
@@ -15,6 +15,7 @@
 #include "cmVersion.h"
 #include "cmGeneratedFileStream.h"
 #include "cmCTest.h"
+#include "cmXMLParser.h"
 
 #include <cmsys/Process.h>
 #include <cmsys/Base64.h>
@@ -31,6 +32,80 @@
 
 typedef std::vector<char> cmCTestSubmitHandlerVectorOfChar;
 
+//----------------------------------------------------------------------------
+class cmCTestSubmitHandler::ResponseParser: public cmXMLParser
+{
+public:
+  ResponseParser() { this->Status = STATUS_OK; }
+  ~ResponseParser() {}
+
+public:
+
+  enum StatusType
+    {
+    STATUS_OK,
+    STATUS_WARNING,
+    STATUS_ERROR
+    };
+
+  StatusType Status;
+  std::string CDashVersion;
+  std::string Filename;
+  std::string MD5;
+  std::string Message;
+
+private:
+  std::string CurrentValue;
+  std::string CurrentTag;
+
+  virtual void StartElement(const char* name, const char** atts)
+    {
+    this->CurrentValue = "";
+    if(strcmp(name, "cdash") == 0)
+      {
+      this->CDashVersion = this->FindAttribute(atts, "version");
+      }
+    }
+
+  virtual void CharacterDataHandler(const char* data, int length)
+    {
+    this->CurrentValue.insert(this->CurrentValue.end(), data, data+length);
+    }
+
+  virtual void EndElement(const char* name)
+    {
+    if(strcmp(name, "status") == 0)
+      {
+      this->CurrentValue = cmSystemTools::UpperCase(this->CurrentValue);
+      if(this->CurrentValue == "OK" || this->CurrentValue == "SUCCESS")
+        {
+        this->Status = STATUS_OK;
+        }
+      else if(this->CurrentValue == "WARNING")
+        {
+        this->Status = STATUS_WARNING;
+        }
+      else
+        {
+        this->Status = STATUS_ERROR;
+        }
+      }
+    else if(strcmp(name, "filename") == 0)
+      {
+      this->Filename = this->CurrentValue;
+      }
+    else if(strcmp(name, "md5") == 0)
+      {
+      this->MD5 = this->CurrentValue;
+      }
+    else if(strcmp(name, "message") == 0)
+      {
+      this->Message = this->CurrentValue;
+      }
+    }
+};
+
+
 static size_t
 cmCTestSubmitHandlerWriteMemoryCallback(void *ptr, size_t size, size_t nmemb,
   void *data)
@@ -367,6 +442,13 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP(const cmStdString& localprefix,
         = url + ((url.find("?",0) == cmStdString::npos) ? "?" : "&")
         + "FileName=" + ofile;
 
+      char md5[33];
+      cmSystemTools::ComputeFileMD5(local_file.c_str(), md5);
+      md5[32] = 0;
+      std::stringstream md5string;
+      md5string << "&MD5=" << md5;
+      upload_as += md5string.str();
+
       struct stat st;
       if ( ::stat(local_file.c_str(), &st) )
         {
@@ -382,7 +464,6 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP(const cmStdString& localprefix,
         << local_file.c_str() << " to "
         << upload_as.c_str() << " Size: " << st.st_size << std::endl);
 
-
       // specify target
       ::curl_easy_setopt(curl,CURLOPT_URL, upload_as.c_str());
 
@@ -411,6 +492,47 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP(const cmStdString& localprefix,
       // Now run off and do what you've been told!
       res = ::curl_easy_perform(curl);
 
+      // If we time out or operation is too slow, wait and retry
+      if(res == CURLE_OPERATION_TIMEOUTED)
+        {
+        std::string retryTime = this->GetOption("RetryTime") == NULL ?
+          "" : this->GetOption("RetryTime");
+        std::string retryCount = this->GetOption("RetryCount") == NULL ?
+          "" : this->GetOption("RetryCount");
+
+        int time = retryTime == "" ? atoi(this->CTest->GetCTestConfiguration(
+          "CTestRetryTime").c_str()) : atoi(retryTime.c_str());
+        int count = retryCount == "" ? atoi(this->CTest->GetCTestConfiguration(
+          "CTestRetryCount").c_str()) : atoi(retryCount.c_str());
+
+        for(int i = 0; i < count; i++)
+          {
+          cmCTestLog(this->CTest, HANDLER_OUTPUT,
+            "   Connection timed out, waiting " << time << " seconds...\n");
+
+          double stop = cmSystemTools::GetTime() + time;
+          while(cmSystemTools::GetTime() < stop) {} //wait <time> seconds
+
+          cmCTestLog(this->CTest, HANDLER_OUTPUT,
+            "   Retry submission: Attempt " << (i + 1) << " of "
+            << count << std::endl);
+
+          ::fclose(ftpfile);
+          ftpfile = ::fopen(local_file.c_str(), "rb");
+          ::curl_easy_setopt(curl, CURLOPT_INFILE, ftpfile);
+
+          chunk.clear();
+          chunkDebug.clear();
+
+          res = ::curl_easy_perform(curl);
+
+          if(res != CURLE_OPERATION_TIMEDOUT)
+            {
+            break;
+            }
+          }
+        }
+
       if ( chunk.size() > 0 )
         {
         cmCTestLog(this->CTest, DEBUG, "CURL output: ["
@@ -467,29 +589,39 @@ void cmCTestSubmitHandler
 ::ParseResponse(cmCTestSubmitHandlerVectorOfChar chunk)
 {
   std::string output = "";
+  output.append(chunk.begin(), chunk.end());
 
-  for(cmCTestSubmitHandlerVectorOfChar::iterator i = chunk.begin();
-      i != chunk.end(); ++i)
-    {
-    output += *i;
-    }
-  output = cmSystemTools::UpperCase(output);
-  
-  if(output.find("WARNING") != std::string::npos)
-    {
-    this->HasWarnings = true;
-    }
-  if(output.find("ERROR") != std::string::npos)
+  if(output.find("<cdash") != output.npos)
     {
-    this->HasErrors = true;
+    ResponseParser parser;
+    parser.Parse(output.c_str());
+
+    if(parser.Status != ResponseParser::STATUS_OK)
+      {
+      this->HasErrors = true;
+      cmCTestLog(this->CTest, HANDLER_OUTPUT, "   Submission failed: " <<
+        parser.Message << std::endl);
+      return;
+      }
     }
-  
-  if(this->HasWarnings || this->HasErrors)
+  else
     {
-    cmCTestLog(this->CTest, HANDLER_OUTPUT, "   Server Response:\n" <<
-          cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "\n");
+    output = cmSystemTools::UpperCase(output);
+    if(output.find("WARNING") != std::string::npos)
+      {
+      this->HasWarnings = true;
+      }
+    if(output.find("ERROR") != std::string::npos)
+      {
+      this->HasErrors = true;
+      }
+
+    if(this->HasWarnings || this->HasErrors)
+      {
+      cmCTestLog(this->CTest, HANDLER_OUTPUT, "   Server Response:\n" <<
+            cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "\n");
+      }
     }
-  
 }
 
 //----------------------------------------------------------------------------
diff --git a/Source/CTest/cmCTestSubmitHandler.h b/Source/CTest/cmCTestSubmitHandler.h
index 8b011ea..e7755b1 100644
--- a/Source/CTest/cmCTestSubmitHandler.h
+++ b/Source/CTest/cmCTestSubmitHandler.h
@@ -79,6 +79,7 @@ private:
 
   std::string GetSubmitResultsPrefix();
 
+  class         ResponseParser;
   cmStdString   HTTPProxy;
   int           HTTPProxyType;
   cmStdString   HTTPProxyAuth;
diff --git a/Tests/CTestTest3/test.cmake.in b/Tests/CTestTest3/test.cmake.in
index 1e8ea50..9819235 100644
--- a/Tests/CTestTest3/test.cmake.in
+++ b/Tests/CTestTest3/test.cmake.in
@@ -113,7 +113,7 @@ IF(svncommand)
   CTEST_TEST(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res PARALLEL_LEVEL 5 SCHEDULE_RANDOM ON)
   CTEST_MEMCHECK(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res PARALLEL_LEVEL 5)
   CTEST_COVERAGE(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res)
-  CTEST_SUBMIT(RETURN_VALUE res)
+  CTEST_SUBMIT(RETRY_COUNT 4 RETRY_DELAY 10 RETURN_VALUE res)
 
 ELSE(svncommand)
   MESSAGE("Cannot find SVN command: ${svncommand}")
-- 
cgit v0.12


From 082c87e5287e75e65a75d7588c40508f12e18632 Mon Sep 17 00:00:00 2001
From: Zach Mullen <zach.mullen@kitware.com>
Date: Thu, 3 Jun 2010 13:27:26 -0400
Subject: Cross-platform fixes for checksum/retry code

---
 Modules/DartConfiguration.tcl.in      |  4 ++--
 Source/CTest/cmCTestSubmitCommand.h   |  9 +++++++--
 Source/CTest/cmCTestSubmitHandler.cxx | 34 ++++++++++++++++++++--------------
 3 files changed, 29 insertions(+), 18 deletions(-)

diff --git a/Modules/DartConfiguration.tcl.in b/Modules/DartConfiguration.tcl.in
index 799ff79..cbb9a32 100644
--- a/Modules/DartConfiguration.tcl.in
+++ b/Modules/DartConfiguration.tcl.in
@@ -85,5 +85,5 @@ CurlOptions: @CTEST_CURL_OPTIONS@
 
 # For CTest submissions that timeout, these options
 # specify behavior for retrying the submission
-CTestRetryTime: @CTEST_SUBMIT_RETRY_DELAY@
-CTestRetryCount: @CTEST_SUBMIT_RETRY_COUNT@
+CTestSubmitRetryDelay: @CTEST_SUBMIT_RETRY_DELAY@
+CTestSubmitRetryCount: @CTEST_SUBMIT_RETRY_COUNT@
diff --git a/Source/CTest/cmCTestSubmitCommand.h b/Source/CTest/cmCTestSubmitCommand.h
index 14dfa37..5a80b45 100644
--- a/Source/CTest/cmCTestSubmitCommand.h
+++ b/Source/CTest/cmCTestSubmitCommand.h
@@ -63,7 +63,8 @@ public:
   virtual const char* GetFullDocumentation()
     {
     return
-      "  ctest_submit([PARTS ...] [FILES ...] [RETURN_VALUE res])\n"
+      "  ctest_submit([PARTS ...] [FILES ...] [RETRY_COUNT count] "
+      "               [RETRY_DELAY delay][RETURN_VALUE res])\n"
       "By default all available parts are submitted if no PARTS or FILES "
       "are specified.  "
       "The PARTS option lists a subset of parts to be submitted.  "
@@ -79,7 +80,11 @@ public:
       "  ExtraFiles = Files listed by CTEST_EXTRA_SUBMIT_FILES\n"
       "  Submit     = nothing\n"
       "The FILES option explicitly lists specific files to be submitted.  "
-      "Each individual file must exist at the time of the call.\n";
+      "Each individual file must exist at the time of the call.\n"
+      "The RETRY_DELAY option specifies how long in seconds to wait after "
+      "a timed-out submission before attempting to re-submit.\n"
+      "The RETRY_COUNT option specifies how many times to retry a timed-out "
+      "submission.\n";
     }
 
   cmTypeMacro(cmCTestSubmitCommand, cmCTestHandlerCommand);
diff --git a/Source/CTest/cmCTestSubmitHandler.cxx b/Source/CTest/cmCTestSubmitHandler.cxx
index fca05ac..fe00a82 100644
--- a/Source/CTest/cmCTestSubmitHandler.cxx
+++ b/Source/CTest/cmCTestSubmitHandler.cxx
@@ -55,12 +55,19 @@ public:
   std::string Message;
 
 private:
-  std::string CurrentValue;
-  std::string CurrentTag;
+
+  std::vector<char> CurrentValue;
+
+  std::string GetCurrentValue()
+    {
+    std::string val;
+    val.assign(&this->CurrentValue[0], this->CurrentValue.size());
+    return val;
+    }
 
   virtual void StartElement(const char* name, const char** atts)
     {
-    this->CurrentValue = "";
+    this->CurrentValue.clear();
     if(strcmp(name, "cdash") == 0)
       {
       this->CDashVersion = this->FindAttribute(atts, "version");
@@ -76,12 +83,12 @@ private:
     {
     if(strcmp(name, "status") == 0)
       {
-      this->CurrentValue = cmSystemTools::UpperCase(this->CurrentValue);
-      if(this->CurrentValue == "OK" || this->CurrentValue == "SUCCESS")
+      std::string status = cmSystemTools::UpperCase(this->GetCurrentValue());
+      if(status == "OK" || status == "SUCCESS")
         {
         this->Status = STATUS_OK;
         }
-      else if(this->CurrentValue == "WARNING")
+      else if(status == "WARNING")
         {
         this->Status = STATUS_WARNING;
         }
@@ -92,15 +99,15 @@ private:
       }
     else if(strcmp(name, "filename") == 0)
       {
-      this->Filename = this->CurrentValue;
+      this->Filename = this->GetCurrentValue();
       }
     else if(strcmp(name, "md5") == 0)
       {
-      this->MD5 = this->CurrentValue;
+      this->MD5 = this->GetCurrentValue();
       }
     else if(strcmp(name, "message") == 0)
       {
-      this->Message = this->CurrentValue;
+      this->Message = this->GetCurrentValue();
       }
     }
 };
@@ -445,9 +452,8 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP(const cmStdString& localprefix,
       char md5[33];
       cmSystemTools::ComputeFileMD5(local_file.c_str(), md5);
       md5[32] = 0;
-      std::stringstream md5string;
-      md5string << "&MD5=" << md5;
-      upload_as += md5string.str();
+      upload_as += "&MD5=";
+      upload_as += md5;
 
       struct stat st;
       if ( ::stat(local_file.c_str(), &st) )
@@ -501,9 +507,9 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP(const cmStdString& localprefix,
           "" : this->GetOption("RetryCount");
 
         int time = retryTime == "" ? atoi(this->CTest->GetCTestConfiguration(
-          "CTestRetryTime").c_str()) : atoi(retryTime.c_str());
+          "CTestSubmitRetryDelay").c_str()) : atoi(retryTime.c_str());
         int count = retryCount == "" ? atoi(this->CTest->GetCTestConfiguration(
-          "CTestRetryCount").c_str()) : atoi(retryCount.c_str());
+          "CTestSubmitRetryCount").c_str()) : atoi(retryCount.c_str());
 
         for(int i = 0; i < count; i++)
           {
-- 
cgit v0.12


From d6b71078da1bf75ab3031bae5c2952a6f0bd9d45 Mon Sep 17 00:00:00 2001
From: Zach Mullen <zach.mullen@kitware.com>
Date: Thu, 3 Jun 2010 13:52:48 -0400
Subject: Fix subscript out of range crash

---
 Source/CTest/cmCTestSubmitHandler.cxx | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/Source/CTest/cmCTestSubmitHandler.cxx b/Source/CTest/cmCTestSubmitHandler.cxx
index fe00a82..9bbb2e6 100644
--- a/Source/CTest/cmCTestSubmitHandler.cxx
+++ b/Source/CTest/cmCTestSubmitHandler.cxx
@@ -61,7 +61,10 @@ private:
   std::string GetCurrentValue()
     {
     std::string val;
-    val.assign(&this->CurrentValue[0], this->CurrentValue.size());
+    if(this->CurrentValue.size())
+      {
+      val.assign(&this->CurrentValue[0], this->CurrentValue.size());
+      }
     return val;
     }
 
-- 
cgit v0.12


From 86e81b53c196bfd29aa7d877d2fb9e71a6392cc1 Mon Sep 17 00:00:00 2001
From: Zach Mullen <zach.mullen@kitware.com>
Date: Sat, 5 Jun 2010 10:36:23 -0400
Subject: CTest should resubmit in the checksum failed case

---
 Source/CTest/cmCTestSubmitHandler.cxx | 43 +++++++++++++++++++++--------------
 1 file changed, 26 insertions(+), 17 deletions(-)

diff --git a/Source/CTest/cmCTestSubmitHandler.cxx b/Source/CTest/cmCTestSubmitHandler.cxx
index 9bbb2e6..21c005d 100644
--- a/Source/CTest/cmCTestSubmitHandler.cxx
+++ b/Source/CTest/cmCTestSubmitHandler.cxx
@@ -501,8 +501,22 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP(const cmStdString& localprefix,
       // Now run off and do what you've been told!
       res = ::curl_easy_perform(curl);
 
-      // If we time out or operation is too slow, wait and retry
-      if(res == CURLE_OPERATION_TIMEOUTED)
+      if ( chunk.size() > 0 )
+        {
+        cmCTestLog(this->CTest, DEBUG, "CURL output: ["
+          << cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "]"
+          << std::endl);
+        this->ParseResponse(chunk);
+        }
+      if ( chunkDebug.size() > 0 )
+        {
+        cmCTestLog(this->CTest, DEBUG, "CURL debug output: ["
+          << cmCTestLogWrite(&*chunkDebug.begin(), chunkDebug.size()) << "]"
+          << std::endl);
+        }
+
+      // If we time out or checksum fails, wait and retry
+      if(res == CURLE_OPERATION_TIMEDOUT || this->HasErrors)
         {
         std::string retryTime = this->GetOption("RetryTime") == NULL ?
           "" : this->GetOption("RetryTime");
@@ -532,30 +546,25 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP(const cmStdString& localprefix,
 
           chunk.clear();
           chunkDebug.clear();
+          this->HasErrors = false;
 
           res = ::curl_easy_perform(curl);
 
-          if(res != CURLE_OPERATION_TIMEDOUT)
+          if ( chunk.size() > 0 )
+            {
+            cmCTestLog(this->CTest, DEBUG, "CURL output: ["
+              << cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "]"
+              << std::endl);
+            this->ParseResponse(chunk);
+            }
+
+          if(res != CURLE_OPERATION_TIMEDOUT && !this->HasErrors)
             {
             break;
             }
           }
         }
 
-      if ( chunk.size() > 0 )
-        {
-        cmCTestLog(this->CTest, DEBUG, "CURL output: ["
-          << cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "]"
-          << std::endl);
-        this->ParseResponse(chunk);
-        }
-      if ( chunkDebug.size() > 0 )
-        {
-        cmCTestLog(this->CTest, DEBUG, "CURL debug output: ["
-          << cmCTestLogWrite(&*chunkDebug.begin(), chunkDebug.size()) << "]"
-          << std::endl);
-        }
-
       fclose(ftpfile);
       if ( res )
         {
-- 
cgit v0.12


From af5ef0c96982be431791ea35d6de8798ffe70254 Mon Sep 17 00:00:00 2001
From: Zach Mullen <zach.mullen@kitware.com>
Date: Thu, 10 Jun 2010 12:25:49 -0400
Subject: Testing for CTest checksum

---
 Source/CTest/cmCTestSubmitCommand.cxx | 12 ++++++-
 Source/CTest/cmCTestSubmitCommand.h   |  2 ++
 Source/CTest/cmCTestSubmitHandler.cxx | 60 ++++++++++++++++++++---------------
 Tests/CMakeLists.txt                  | 11 +++++++
 Tests/CTestTest3/test.cmake.in        |  2 +-
 Tests/CTestTestChecksum/test.cmake.in | 23 ++++++++++++++
 6 files changed, 82 insertions(+), 28 deletions(-)
 create mode 100644 Tests/CTestTestChecksum/test.cmake.in

diff --git a/Source/CTest/cmCTestSubmitCommand.cxx b/Source/CTest/cmCTestSubmitCommand.cxx
index 6a45d58..24974e3 100644
--- a/Source/CTest/cmCTestSubmitCommand.cxx
+++ b/Source/CTest/cmCTestSubmitCommand.cxx
@@ -147,10 +147,12 @@ cmCTestGenericHandler* cmCTestSubmitCommand::InitializeHandler()
     static_cast<cmCTestSubmitHandler*>(handler)->SelectParts(this->Parts);
     }
 
-  static_cast<cmCTestSubmitHandler*>(handler)->SetOption("RetryTime",
+  static_cast<cmCTestSubmitHandler*>(handler)->SetOption("RetryDelay",
     this->RetryDelay.c_str());
   static_cast<cmCTestSubmitHandler*>(handler)->SetOption("RetryCount",
     this->RetryCount.c_str());
+  static_cast<cmCTestSubmitHandler*>(handler)->SetOption("InternalTest",
+    this->InternalTest ? "ON" : "OFF");
 
   return handler;
 }
@@ -186,6 +188,12 @@ bool cmCTestSubmitCommand::CheckArgumentKeyword(std::string const& arg)
     return true;
     }
 
+  if(arg == "INTERNAL_TEST_CHECKSUM")
+    {
+    this->InternalTest = true;
+    return true;
+    }
+
   // Look for other arguments.
   return this->Superclass::CheckArgumentKeyword(arg);
 }
@@ -233,11 +241,13 @@ bool cmCTestSubmitCommand::CheckArgumentValue(std::string const& arg)
   if(this->ArgumentDoing == ArgumentDoingRetryCount)
     {
     this->RetryCount = arg;
+    return true;
     }
 
   if(this->ArgumentDoing == ArgumentDoingRetryDelay)
     {
     this->RetryDelay = arg;
+    return true;
     }
 
   // Look for other arguments.
diff --git a/Source/CTest/cmCTestSubmitCommand.h b/Source/CTest/cmCTestSubmitCommand.h
index 5a80b45..edc9c65 100644
--- a/Source/CTest/cmCTestSubmitCommand.h
+++ b/Source/CTest/cmCTestSubmitCommand.h
@@ -29,6 +29,7 @@ public:
     {
     this->PartsMentioned = false;
     this->FilesMentioned = false;
+    this->InternalTest = false;
     this->RetryCount = "";
     this->RetryDelay = "";
     }
@@ -107,6 +108,7 @@ protected:
   bool PartsMentioned;
   std::set<cmCTest::Part> Parts;
   bool FilesMentioned;
+  bool InternalTest;
   cmCTest::SetOfStrings Files;
   std::string RetryCount;
   std::string RetryDelay;
diff --git a/Source/CTest/cmCTestSubmitHandler.cxx b/Source/CTest/cmCTestSubmitHandler.cxx
index 21c005d..4ca382c 100644
--- a/Source/CTest/cmCTestSubmitHandler.cxx
+++ b/Source/CTest/cmCTestSubmitHandler.cxx
@@ -452,11 +452,19 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP(const cmStdString& localprefix,
         = url + ((url.find("?",0) == cmStdString::npos) ? "?" : "&")
         + "FileName=" + ofile;
 
-      char md5[33];
-      cmSystemTools::ComputeFileMD5(local_file.c_str(), md5);
-      md5[32] = 0;
       upload_as += "&MD5=";
-      upload_as += md5;
+
+      if(cmSystemTools::IsOn(this->GetOption("InternalTest")))
+        {
+        upload_as += "bad_md5sum";
+        }
+      else
+        {
+        char md5[33];
+        cmSystemTools::ComputeFileMD5(local_file.c_str(), md5);
+        md5[32] = 0;
+        upload_as += md5;
+        }
 
       struct stat st;
       if ( ::stat(local_file.c_str(), &st) )
@@ -518,23 +526,26 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP(const cmStdString& localprefix,
       // If we time out or checksum fails, wait and retry
       if(res == CURLE_OPERATION_TIMEDOUT || this->HasErrors)
         {
-        std::string retryTime = this->GetOption("RetryTime") == NULL ?
-          "" : this->GetOption("RetryTime");
+        std::string retryDelay = this->GetOption("RetryDelay") == NULL ?
+          "" : this->GetOption("RetryDelay");
         std::string retryCount = this->GetOption("RetryCount") == NULL ?
           "" : this->GetOption("RetryCount");
 
-        int time = retryTime == "" ? atoi(this->CTest->GetCTestConfiguration(
-          "CTestSubmitRetryDelay").c_str()) : atoi(retryTime.c_str());
+        int delay = retryDelay == "" ? atoi(this->CTest->GetCTestConfiguration(
+          "CTestSubmitRetryDelay").c_str()) : atoi(retryDelay.c_str());
         int count = retryCount == "" ? atoi(this->CTest->GetCTestConfiguration(
           "CTestSubmitRetryCount").c_str()) : atoi(retryCount.c_str());
 
         for(int i = 0; i < count; i++)
           {
           cmCTestLog(this->CTest, HANDLER_OUTPUT,
-            "   Connection timed out, waiting " << time << " seconds...\n");
+            "   Connection timed out, waiting " << delay << " seconds...\n");
 
-          double stop = cmSystemTools::GetTime() + time;
-          while(cmSystemTools::GetTime() < stop) {} //wait <time> seconds
+          double stop = cmSystemTools::GetTime() + delay;
+          while(cmSystemTools::GetTime() < stop)
+            {
+            cmSystemTools::Delay(100);
+            }
 
           cmCTestLog(this->CTest, HANDLER_OUTPUT,
             "   Retry submission: Attempt " << (i + 1) << " of "
@@ -622,23 +633,20 @@ void cmCTestSubmitHandler
       return;
       }
     }
-  else
+  output = cmSystemTools::UpperCase(output);
+  if(output.find("WARNING") != std::string::npos)
     {
-    output = cmSystemTools::UpperCase(output);
-    if(output.find("WARNING") != std::string::npos)
-      {
-      this->HasWarnings = true;
-      }
-    if(output.find("ERROR") != std::string::npos)
-      {
-      this->HasErrors = true;
-      }
+    this->HasWarnings = true;
+    }
+  if(output.find("ERROR") != std::string::npos)
+    {
+    this->HasErrors = true;
+    }
 
-    if(this->HasWarnings || this->HasErrors)
-      {
-      cmCTestLog(this->CTest, HANDLER_OUTPUT, "   Server Response:\n" <<
-            cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "\n");
-      }
+  if(this->HasWarnings || this->HasErrors)
+    {
+    cmCTestLog(this->CTest, HANDLER_OUTPUT, "   Server Response:\n" <<
+          cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "\n");
     }
 }
 
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt
index 330cf9f..c1cc8b8 100644
--- a/Tests/CMakeLists.txt
+++ b/Tests/CMakeLists.txt
@@ -1449,6 +1449,17 @@ ${CMake_BINARY_DIR}/bin/cmake -DVERSION=master -P ${CMake_SOURCE_DIR}/Utilities/
       --output-log "${CMake_BINARY_DIR}/Tests/CTestTest3/testOutput.log"
       )
 
+    CONFIGURE_FILE("${CMake_SOURCE_DIR}/Tests/CTestTestChecksum/test.cmake.in"
+      "${CMake_BINARY_DIR}/Tests/CTestTestChecksum/test.cmake" @ONLY
+      ESCAPE_QUOTES)
+    ADD_TEST(CTestTestChecksum ${CMAKE_CTEST_COMMAND}
+      -S "${CMake_BINARY_DIR}/Tests/CTestTestChecksum/test.cmake" -V
+      --output-log
+      "${CMake_BINARY_DIR}/Tests/CTestTestChecksum/testOutput.log"
+      )
+    SET_TESTS_PROPERTIES(CTestTestChecksum PROPERTIES PASS_REGULAR_EXPRESSION
+      "Submission failed: Checksum failed for file")
+
     # these tests take a long time, make sure they have it
     # if timeouts have not already been set
     GET_TEST_PROPERTY(CTestTest TIMEOUT PREVIOUS_TIMEOUT)
diff --git a/Tests/CTestTest3/test.cmake.in b/Tests/CTestTest3/test.cmake.in
index 9819235..1e8ea50 100644
--- a/Tests/CTestTest3/test.cmake.in
+++ b/Tests/CTestTest3/test.cmake.in
@@ -113,7 +113,7 @@ IF(svncommand)
   CTEST_TEST(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res PARALLEL_LEVEL 5 SCHEDULE_RANDOM ON)
   CTEST_MEMCHECK(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res PARALLEL_LEVEL 5)
   CTEST_COVERAGE(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res)
-  CTEST_SUBMIT(RETRY_COUNT 4 RETRY_DELAY 10 RETURN_VALUE res)
+  CTEST_SUBMIT(RETURN_VALUE res)
 
 ELSE(svncommand)
   MESSAGE("Cannot find SVN command: ${svncommand}")
diff --git a/Tests/CTestTestChecksum/test.cmake.in b/Tests/CTestTestChecksum/test.cmake.in
new file mode 100644
index 0000000..7ef8ab2
--- /dev/null
+++ b/Tests/CTestTestChecksum/test.cmake.in
@@ -0,0 +1,23 @@
+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@-Checksum")
+
+SET(CTEST_SOURCE_DIRECTORY              "@CMake_SOURCE_DIR@/Tests/CTestTestParallel")
+SET(CTEST_BINARY_DIRECTORY              "@CMake_BINARY_DIR@/Tests/CTestTestParallel")
+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_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 PARALLEL_LEVEL 4)
+CTEST_SUBMIT(RETRY_DELAY 3 RETRY_COUNT 2 INTERNAL_TEST_CHECKSUM RETURN_VALUE res)
-- 
cgit v0.12


From d0d1cdd71bb85b821be9b65be94819f3575b49ad Mon Sep 17 00:00:00 2001
From: Zach Mullen <zach.mullen@kitware.com>
Date: Thu, 10 Jun 2010 15:02:24 -0400
Subject: Mock checksum failure output for old CDash versions

---
 Source/CTest/cmCTestSubmitHandler.cxx | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/Source/CTest/cmCTestSubmitHandler.cxx b/Source/CTest/cmCTestSubmitHandler.cxx
index 4ca382c..bfe515d 100644
--- a/Source/CTest/cmCTestSubmitHandler.cxx
+++ b/Source/CTest/cmCTestSubmitHandler.cxx
@@ -509,6 +509,19 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP(const cmStdString& localprefix,
       // Now run off and do what you've been told!
       res = ::curl_easy_perform(curl);
 
+      if(cmSystemTools::IsOn(this->GetOption("InternalTest")) &&
+         cmSystemTools::VersionCompare(cmSystemTools::OP_LESS,
+         this->CTest->GetCDashVersion().c_str(), "1.7"))
+        {
+        // mock failure output for internal test case
+        std::string mock_output = "<cdash version=\"1.7.0\">\n"
+          "  <status>ERROR</status>\n"
+          "  <message>Checksum failed for file.</message>\n"
+          "</cdash>\n";
+        chunk.clear();
+        chunk.assign(mock_output.begin(), mock_output.end());
+        }
+
       if ( chunk.size() > 0 )
         {
         cmCTestLog(this->CTest, DEBUG, "CURL output: ["
-- 
cgit v0.12


From 87054972a703efac5137ab3d83f7f0dc9c10b9b3 Mon Sep 17 00:00:00 2001
From: Zach Mullen <zach.mullen@kitware.com>
Date: Tue, 22 Jun 2010 11:13:48 -0400
Subject: Checksum test should use CMAKE_TESTS_CDASH_SERVER

---
 Tests/CTestTestChecksum/test.cmake.in | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/Tests/CTestTestChecksum/test.cmake.in b/Tests/CTestTestChecksum/test.cmake.in
index 7ef8ab2..c3c41a5 100644
--- a/Tests/CTestTestChecksum/test.cmake.in
+++ b/Tests/CTestTestChecksum/test.cmake.in
@@ -20,4 +20,9 @@ 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 PARALLEL_LEVEL 4)
+
+SET(CTEST_DROP_METHOD "@protocol@")
+SET(CTEST_DROP_SITE "@server@")
+SET(CTEST_DROP_LOCATION "@path@/submit.php?project=PublicDashboard")
+
 CTEST_SUBMIT(RETRY_DELAY 3 RETRY_COUNT 2 INTERNAL_TEST_CHECKSUM RETURN_VALUE res)
-- 
cgit v0.12


From 46df0b44ac97859ab40dd0aa03ceca297a455587 Mon Sep 17 00:00:00 2001
From: David Cole <david.cole@kitware.com>
Date: Mon, 12 Jul 2010 16:48:38 -0400
Subject: Activate retry code on any curl submit failure.

Previously, we were only going into the retry block
for time out conditions. But a "could not connect"
response, or really any sort of curl failure, is
also a condition where we should retry the submit
if the user has requested a retry.
---
 Source/CTest/cmCTestSubmitHandler.cxx | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/Source/CTest/cmCTestSubmitHandler.cxx b/Source/CTest/cmCTestSubmitHandler.cxx
index bfe515d..1e18259 100644
--- a/Source/CTest/cmCTestSubmitHandler.cxx
+++ b/Source/CTest/cmCTestSubmitHandler.cxx
@@ -536,8 +536,9 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP(const cmStdString& localprefix,
           << std::endl);
         }
 
-      // If we time out or checksum fails, wait and retry
-      if(res == CURLE_OPERATION_TIMEDOUT || this->HasErrors)
+      // If curl failed for any reason, or checksum fails, wait and retry
+      //
+      if(res != CURLE_OK || this->HasErrors)
         {
         std::string retryDelay = this->GetOption("RetryDelay") == NULL ?
           "" : this->GetOption("RetryDelay");
@@ -552,7 +553,7 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP(const cmStdString& localprefix,
         for(int i = 0; i < count; i++)
           {
           cmCTestLog(this->CTest, HANDLER_OUTPUT,
-            "   Connection timed out, waiting " << delay << " seconds...\n");
+            "   Submit failed, waiting " << delay << " seconds...\n");
 
           double stop = cmSystemTools::GetTime() + delay;
           while(cmSystemTools::GetTime() < stop)
@@ -582,7 +583,7 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP(const cmStdString& localprefix,
             this->ParseResponse(chunk);
             }
 
-          if(res != CURLE_OPERATION_TIMEDOUT && !this->HasErrors)
+          if(res == CURLE_OK && !this->HasErrors)
             {
             break;
             }
-- 
cgit v0.12