From e539cf9f7c39b2c25b5566925e0c43a39f2ee868 Mon Sep 17 00:00:00 2001
From: Bill Hoffman <bill.hoffman@kitware.com>
Date: Wed, 28 Apr 2004 12:31:18 -0400
Subject: [PATCH] ENH: make test driver more flexible by using a configured
 file instead of generating all the code. fixes bug 28

---
 Source/cmCreateTestSourceList.cxx | 183 +++++-------------------------
 Source/cmCreateTestSourceList.h   |   6 +-
 Templates/TestDriver.cxx.in       | 180 +++++++++++++++++++++++++++++
 3 files changed, 213 insertions(+), 156 deletions(-)
 create mode 100644 Templates/TestDriver.cxx.in

diff --git a/Source/cmCreateTestSourceList.cxx b/Source/cmCreateTestSourceList.cxx
index ef4f4a0222..51931baea0 100644
--- a/Source/cmCreateTestSourceList.cxx
+++ b/Source/cmCreateTestSourceList.cxx
@@ -42,7 +42,9 @@ bool cmCreateTestSourceList::InitialPass(std::vector<std::string> const& args)
         this->SetError("incorrect arguments to EXTRA_INCLUDE");
         return false;
         }
-      extraInclude = *i;
+      extraInclude = "#include \"";
+      extraInclude += *i;
+      extraInclude += "\"\n";
       }
     else if(*i == "FUNCTION")
       {
@@ -53,6 +55,7 @@ bool cmCreateTestSourceList::InitialPass(std::vector<std::string> const& args)
         return false;
         }
       function = *i;
+      function += "(&ac, &av);\n";
       }
     else
       {
@@ -87,36 +90,11 @@ bool cmCreateTestSourceList::InitialPass(std::vector<std::string> const& args)
     this->SetError(err.c_str());
     return false;
     }
-
+  std::string configFile = 
+    m_Makefile->GetDefinition("CMAKE_ROOT");
+  configFile += "/Templates/TestDriver.cxx.in";
   // Create the test driver file
-
-  fout << 
-    "#include <ctype.h>\n"
-    "#include <stdio.h>\n"
-    "#include <string.h>\n"
-    "#include <stdlib.h>\n"
-    "\n";
-  fout <<
-    "#if defined(_MSC_VER) && defined(_DEBUG)\n"
-    "/* MSVC debug hook to prevent dialogs when running from DART.  */\n"
-    "# include <crtdbg.h>\n"
-    "static int TestDriverDebugReport(int type, char* message, int* retVal)\n"
-    "{\n"
-    "  (void)type; (void)retVal;\n"
-    "  fprintf(stderr, message);\n"
-    "  exit(1);\n"
-    "}\n"
-    "#endif\n";
-  if(extraInclude.size())
-    {
-    fout << "#include \"" << extraInclude << "\"\n";
-    }
   
-  fout <<
-    "\n"
-    "/* Forward declare test functions. */\n"
-    "\n";
-
   std::vector<std::string>::const_iterator testsBegin = i;
   std::vector<std::string> tests_func_name;
 
@@ -126,7 +104,7 @@ bool cmCreateTestSourceList::InitialPass(std::vector<std::string> const& args)
   // list. 
   // For the moment:
   //   - replace spaces ' ', ':' and '/' with underscores '_'
-
+  std::string forwardDeclareCode;
   for(i = testsBegin; i != tests.end(); ++i)
     {
     if(*i == "EXTRA_INCLUDE")
@@ -148,22 +126,12 @@ bool cmCreateTestSourceList::InitialPass(std::vector<std::string> const& args)
     cmSystemTools::ReplaceString(func_name, "/", "_");
     cmSystemTools::ReplaceString(func_name, ":", "_");
     tests_func_name.push_back(func_name);
-    fout << "int " << func_name << "(int, char*[]);\n";
+    forwardDeclareCode += "int ";
+    forwardDeclareCode += func_name;
+    forwardDeclareCode += "(int, char*[]);\n";
     }
-
-  fout << 
-    "\n"
-    "/* Create map.  */\n"
-    "\n"
-    "typedef int (*MainFuncPointer)(int , char*[]);\n"
-    "typedef struct\n"
-    "{\n"
-    "  const char* name;\n"
-    "  MainFuncPointer func;\n"
-    "} functionMapEntry;\n"
-    "\n"
-    "functionMapEntry cmakeGeneratedFunctionMapEntries[] = {\n";
-
+  
+  std::string functionMapCode;
   int numTests = 0;
   std::vector<std::string>::iterator j;
   for(i = testsBegin, j = tests_func_name.begin(); i != tests.end(); ++i, ++j)
@@ -178,122 +146,27 @@ bool cmCreateTestSourceList::InitialPass(std::vector<std::string> const& args)
       {
       func_name = cmSystemTools::GetFilenameWithoutLastExtension(*i);
       }
-    fout << 
-      "  {\n"
-      "    \"" << func_name << "\",\n"
-      "    " << *j << "\n"
+    functionMapCode += "  {\n"
+      "    \"";
+    functionMapCode += func_name;
+    functionMapCode += "\",\n"
+      "    ";
+    functionMapCode +=  *j;
+    functionMapCode += "\n"
       "  },\n";
     numTests++;
     }
-  // end with an empty struct
-  fout << "  {0,0}\n";
-  
-  fout << 
-    "};\n"
-    "\n"
-    "/* Allocate and create a lowercased copy of string\n"
-    "   (note that it has to be free'd manually) */\n"
-    "\n"
-    "char* lowercase(const char *string)\n"
-    "{\n"
-    "  char *new_string, *p;\n"
-    "  new_string = (char *)malloc(sizeof(char) * (size_t)(strlen(string) + 1));\n"
-    "  if (!new_string)\n"
-    "    {\n"
-    "    return 0;\n"
-    "    }\n"
-    "  strcpy(new_string, string);\n"
-    "  p = new_string;\n"
-    "  while (*p != 0)\n"
-    "    {\n"
-    "    *p = (char)tolower(*p);\n"
-    "    ++p;\n"
-    "    }\n"
-    "  return new_string;\n"
-    "}\n"
-    "\n"
-    "int main(int ac, char *av[])\n"
-    "{\n"
-    "  int i, NumTests, testNum, partial_match;\n"
-    "  char *arg, *test_name;\n"
-    "  \n"
-    "#if defined(_MSC_VER) && defined(_DEBUG)\n"
-    "  /* If running from DART, put in debug hook.  */\n"
-    "  if(getenv(\"DART_TEST_FROM_DART\"))\n"
-    "    {\n"
-    "    _CrtSetReportHook(TestDriverDebugReport);\n"
-    "    }\n"
-    "#endif\n"
-    "  \n"
-    "  NumTests = " << numTests << ";\n"
-    "  \n"
-    "  /* If no test name was given */\n";
+  if(extraInclude.size())
+    {
+    m_Makefile->AddDefinition("CMAKE_TESTDRIVER_EXTRA_INCLUDES", extraInclude.c_str());
+    }
   if(function.size())
     {
-    fout << "  /* process command line with user function.  */\n"
-         << "  " << function << "(&ac, &av);\n";
+    m_Makefile->AddDefinition("CMAKE_TESTDRIVER_ARGVC_FUNCTION", function.c_str());
     }
-  
-  fout <<
-    "  if (ac < 2)\n"
-    "    {\n"
-    "    /* Ask for a test.  */\n"
-    "    printf(\"Available tests:\\n\");\n"
-    "    for (i =0; i < NumTests; ++i)\n"
-    "      {\n"
-    "      printf(\"%3d. %s\\n\", i, cmakeGeneratedFunctionMapEntries[i].name);\n"
-    "      }\n"
-    "    printf(\"To run a test, enter the test number: \");\n"
-    "    testNum = 0;\n"
-    "    scanf(\"%d\", &testNum);\n"
-    "    if (testNum >= NumTests)\n"
-    "      {\n"
-    "      printf(\"%3d is an invalid test number.\\n\", testNum);\n"
-    "      return -1;\n"
-    "      }\n"
-    "    return (*cmakeGeneratedFunctionMapEntries[testNum].func)(ac-1, av+1);\n"
-    "    }\n"
-    "  \n"
-    "  /* If partial match is requested.  */\n"
-    "  partial_match = (strcmp(av[1], \"-R\") == 0) ? 1 : 0;\n"
-    "  if (partial_match && ac < 3)\n"
-    "    {\n"
-    "    printf(\"-R needs an additional parameter.\\n\");\n"
-    "    return -1;\n"
-    "    }\n"
-    "  \n"
-    "  arg = lowercase(av[1 + partial_match]);\n"
-    "  for (i =0; i < NumTests; ++i)\n"
-    "    {\n"
-    "    test_name = lowercase(cmakeGeneratedFunctionMapEntries[i].name);\n"
-    "    if (partial_match && strstr(test_name, arg) != NULL)\n"
-    "      {\n"
-    "      free(arg);\n"
-    "      free(test_name);\n"
-    "      return (*cmakeGeneratedFunctionMapEntries[i].func)(ac - 2, av + 2);\n"
-    "      }\n"
-    "    else if (!partial_match && strcmp(test_name, arg) == 0)\n"
-    "      {\n"
-    "      free(arg);\n"
-    "      free(test_name);\n"
-    "      return (*cmakeGeneratedFunctionMapEntries[i].func)(ac - 1, av + 1);\n"
-    "      }\n"
-    "    free(test_name);\n"
-    "    }\n"
-    "  free(arg);\n"
-    "  \n"
-    "  /* Nothing was run, display the test names.  */\n"
-    "  printf(\"Available tests:\\n\");\n"
-    "  for (i =0; i < NumTests; ++i)\n"
-    "    {\n"
-    "    printf(\"%3d. %s\\n\", i, cmakeGeneratedFunctionMapEntries[i].name);\n"
-    "    }\n"
-    "  printf(\"Failed: %s is an invalid test name.\\n\", av[1]);\n"
-    "  \n"
-    "  return -1;\n"
-    "}\n";
-
-  fout.close();
+  m_Makefile->AddDefinition("CMAKE_FORWARD_DECLARE_TESTS", forwardDeclareCode.c_str());
+  m_Makefile->AddDefinition("CMAKE_FUNCTION_TABLE_ENTIRES", functionMapCode.c_str());
+  m_Makefile->ConfigureFile(configFile.c_str(), driver.c_str(), false, true, false);
 
   // Create the source list
   cmSourceFile cfile;
diff --git a/Source/cmCreateTestSourceList.h b/Source/cmCreateTestSourceList.h
index 66d34a44ed..850c2bc3e3 100644
--- a/Source/cmCreateTestSourceList.h
+++ b/Source/cmCreateTestSourceList.h
@@ -84,7 +84,11 @@ public:
       "next argument is included into the generated file. If FUNCTION is "
       "specified, then the next argument is taken as a function name that "
       "is passed a pointer to ac and av.  This can be used to add extra "
-      "command line processing to each test. ";
+      "command line processing to each test. The cmake variable "
+      "CMAKE_TESTDRIVER_BEFORE_TESTMAIN can be set to have code that will be "
+      "placed directly before calling the test main function.   "
+      "CMAKE_TESTDRIVER_AFTER_TESTMAIN can be set to have code that will be "
+      "placed directly after the call to the test main function.";
     }
   
   cmTypeMacro(cmCreateTestSourceList, cmCommand);
diff --git a/Templates/TestDriver.cxx.in b/Templates/TestDriver.cxx.in
new file mode 100644
index 0000000000..6a3839f592
--- /dev/null
+++ b/Templates/TestDriver.cxx.in
@@ -0,0 +1,180 @@
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#if defined(_MSC_VER) && defined(_DEBUG)
+/* MSVC debug hook to prevent dialogs when running from DART.  */
+# include <crtdbg.h>
+static int TestDriverDebugReport(int type, char* message, int* retVal)
+{
+  (void)type; (void)retVal;
+  fprintf(stderr, message);
+  exit(1);
+  return 0;
+}
+#endif
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+# include <windows.h>
+static LONG __stdcall
+TestDriverUnhandledExceptionFilter(EXCEPTION_POINTERS* e)
+{
+  ExitProcess(e->ExceptionRecord->ExceptionCode);
+  return 0;
+}
+static void TestDriverEnableWindowsExceptionFilter()
+{
+  if(getenv("DART_TEST_FROM_DART"))
+    {
+    SetUnhandledExceptionFilter(&TestDriverUnhandledExceptionFilter);    
+    }
+}
+#else
+static void TestDriverEnableWindowsExceptionFilter()
+{
+}
+#endif
+@CMAKE_TESTDRIVER_EXTRA_INCLUDES@
+
+
+/* Forward declare test functions. */
+@CMAKE_FORWARD_DECLARE_TESTS@
+
+/* Create map.  */
+
+typedef int (*MainFuncPointer)(int , char*[]);
+typedef struct
+{
+  const char* name;
+  MainFuncPointer func;
+} functionMapEntry;
+
+functionMapEntry cmakeGeneratedFunctionMapEntries[] = {
+  @CMAKE_FUNCTION_TABLE_ENTIRES@
+  {0,0}
+};
+
+/* Allocate and create a lowercased copy of string
+   (note that it has to be free'd manually) */
+
+char* lowercase(const char *string)
+{
+  char *new_string, *p;
+  new_string = (char *)malloc(sizeof(char) * (size_t)(strlen(string) + 1));
+  if (!new_string)
+    {
+    return 0;
+    }
+  strcpy(new_string, string);
+  p = new_string;
+  while (*p != 0)
+    {
+    *p = (char)tolower(*p);
+    ++p;
+    }
+  return new_string;
+}
+
+int main(int ac, char *av[])
+{
+  int i, NumTests, testNum, partial_match;
+  char *arg, *test_name;
+  int count;
+  int result;
+  int testToRun = -1;
+
+  @CMAKE_TESTDRIVER_ARGVC_FUNCTION@
+    
+#if defined(_MSC_VER) && defined(_DEBUG)
+  /* If running from DART, put in debug hook.  */
+  if(getenv("DART_TEST_FROM_DART"))
+    {
+    _CrtSetReportHook(TestDriverDebugReport);
+    }
+#endif
+  TestDriverEnableWindowsExceptionFilter();
+  for(count =0; cmakeGeneratedFunctionMapEntries[count].name != 0; count++)
+    {
+    }
+  NumTests = count;
+  /* If no test name was given */
+  /* process command line with user function.  */
+  @CMAKE_TESTDRIVER_ARGVC_FUNCTION@
+  if (ac < 2)
+    {
+    /* Ask for a test.  */
+    printf("Available tests:\n");
+    for (i =0; i < NumTests; ++i)
+      {
+      printf("%3d. %s\n", i, cmakeGeneratedFunctionMapEntries[i].name);
+      }
+    printf("To run a test, enter the test number: ");
+    testNum = 0;
+    scanf("%d", &testNum);
+    if (testNum >= NumTests)
+      {
+      printf("%3d is an invalid test number.\n", testNum);
+      return -1;
+      }
+    testToRun = testNum;
+    ac--;
+    av++;
+    }
+  partial_match = 0;
+  arg = 0;
+  /* If partial match is requested.  */
+  if(testToRun == -1 && ac > 1)
+    {
+    partial_match = (strcmp(av[1], "-R") == 0) ? 1 : 0;
+    }
+  if (partial_match && ac < 3)
+    {
+    printf("-R needs an additional parameter.\n");
+    return -1;
+    }
+  if(testToRun == -1)
+    {
+    arg = lowercase(av[1 + partial_match]);
+    }
+  for (i =0; i < NumTests && testToRun == -1; ++i)
+    {
+    test_name = lowercase(cmakeGeneratedFunctionMapEntries[i].name);
+    if (partial_match && strstr(test_name, arg) != NULL)
+      {
+      testToRun = i;
+      ac -=2;
+      av += 2;
+      }
+    else if (!partial_match && strcmp(test_name, arg) == 0)
+      {
+      testToRun = i;
+      ac--;
+      av++;
+      }
+    free(test_name);
+    }
+  if(arg)
+    {
+    free(arg);
+    }
+  if(testToRun != -1)
+    {
+    int result;
+@CMAKE_TESTDRIVER_BEFORE_TESTMAIN@
+    result = (*cmakeGeneratedFunctionMapEntries[testToRun].func)(ac, av);
+@CMAKE_TESTDRIVER_AFTER_TESTMAIN@
+    return result;
+    }
+  
+  
+  /* Nothing was run, display the test names.  */
+  printf("Available tests:\n");
+  for (i =0; i < NumTests; ++i)
+    {
+    printf("%3d. %s\n", i, cmakeGeneratedFunctionMapEntries[i].name);
+    }
+  printf("Failed: %s is an invalid test name.\n", av[1]);
+  
+  return -1;
+}
-- 
GitLab