summaryrefslogtreecommitdiff
path: root/Source/kwsys/testProcess.c
diff options
context:
space:
mode:
Diffstat (limited to 'Source/kwsys/testProcess.c')
-rw-r--r--Source/kwsys/testProcess.c526
1 files changed, 526 insertions, 0 deletions
diff --git a/Source/kwsys/testProcess.c b/Source/kwsys/testProcess.c
new file mode 100644
index 000000000..877002a14
--- /dev/null
+++ b/Source/kwsys/testProcess.c
@@ -0,0 +1,526 @@
+/*============================================================================
+ KWSys - Kitware System Library
+ Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
+
+ Distributed under the OSI-approved BSD License (the "License");
+ see accompanying file Copyright.txt for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the License for more information.
+============================================================================*/
+#include "kwsysPrivate.h"
+#include KWSYS_HEADER(Process.h)
+
+/* Work-around CMake dependency scanning limitation. This must
+ duplicate the above list of headers. */
+#if 0
+# include "Process.h.in"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined(_WIN32)
+# include <windows.h>
+#else
+# include <unistd.h>
+#endif
+
+#if defined(__BORLANDC__)
+# pragma warn -8060 /* possibly incorrect assignment */
+#endif
+
+#if defined(__BEOS__) && !defined(__ZETA__) && !defined(__HAIKU__)
+/* BeOS 5 doesn't have usleep(), but it has snooze(), which is identical. */
+# include <be/kernel/OS.h>
+static inline void testProcess_usleep(unsigned int msec)
+{
+ snooze(msec);
+}
+#else
+# define testProcess_usleep usleep
+#endif
+
+int runChild(const char* cmd[], int state, int exception, int value,
+ int share, int output, int delay, double timeout, int poll,
+ int repeat, int disown);
+
+int test1(int argc, const char* argv[])
+{
+ (void)argc; (void)argv;
+ fprintf(stdout, "Output on stdout from test returning 0.\n");
+ fprintf(stderr, "Output on stderr from test returning 0.\n");
+ return 0;
+}
+
+int test2(int argc, const char* argv[])
+{
+ (void)argc; (void)argv;
+ fprintf(stdout, "Output on stdout from test returning 123.\n");
+ fprintf(stderr, "Output on stderr from test returning 123.\n");
+ return 123;
+}
+
+int test3(int argc, const char* argv[])
+{
+ (void)argc; (void)argv;
+ fprintf(stdout, "Output before sleep on stdout from timeout test.\n");
+ fprintf(stderr, "Output before sleep on stderr from timeout test.\n");
+ fflush(stdout);
+ fflush(stderr);
+#if defined(_WIN32)
+ Sleep(15000);
+#else
+ sleep(15);
+#endif
+ fprintf(stdout, "Output after sleep on stdout from timeout test.\n");
+ fprintf(stderr, "Output after sleep on stderr from timeout test.\n");
+ return 0;
+}
+
+int test4(int argc, const char* argv[])
+{
+#if defined(_WIN32)
+ /* Avoid error diagnostic popups since we are crashing on purpose. */
+ SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
+#elif defined(__BEOS__) || defined(__HAIKU__)
+ /* Avoid error diagnostic popups since we are crashing on purpose. */
+ disable_debugger(1);
+#endif
+ (void)argc; (void)argv;
+ fprintf(stdout, "Output before crash on stdout from crash test.\n");
+ fprintf(stderr, "Output before crash on stderr from crash test.\n");
+ fflush(stdout);
+ fflush(stderr);
+#if defined(__clang__)
+ *(int*)1 = 0; /* Clang warns about 0-ptr; undefined behavior. */
+#else
+ *(int*)0 = 0;
+#endif
+ fprintf(stdout, "Output after crash on stdout from crash test.\n");
+ fprintf(stderr, "Output after crash on stderr from crash test.\n");
+ return 0;
+}
+
+int test5(int argc, const char* argv[])
+{
+ int r;
+ const char* cmd[4];
+ (void)argc;
+ cmd[0] = argv[0];
+ cmd[1] = "run";
+ cmd[2] = "4";
+ cmd[3] = 0;
+ fprintf(stdout, "Output on stdout before recursive test.\n");
+ fprintf(stderr, "Output on stderr before recursive test.\n");
+ fflush(stdout);
+ fflush(stderr);
+ r = runChild(cmd, kwsysProcess_State_Exception,
+ kwsysProcess_Exception_Fault, 1, 1, 1, 0, 15, 0, 1, 0);
+ fprintf(stdout, "Output on stdout after recursive test.\n");
+ fprintf(stderr, "Output on stderr after recursive test.\n");
+ fflush(stdout);
+ fflush(stderr);
+ return r;
+}
+
+#define TEST6_SIZE (4096*2)
+void test6(int argc, const char* argv[])
+{
+ int i;
+ char runaway[TEST6_SIZE+1];
+ (void)argc; (void)argv;
+ for(i=0;i < TEST6_SIZE;++i)
+ {
+ runaway[i] = '.';
+ }
+ runaway[TEST6_SIZE] = '\n';
+
+ /* Generate huge amounts of output to test killing. */
+ for(;;)
+ {
+ fwrite(runaway, 1, TEST6_SIZE+1, stdout);
+ fflush(stdout);
+ }
+}
+
+/* Define MINPOLL to be one more than the number of times output is
+ written. Define MAXPOLL to be the largest number of times a loop
+ delaying 1/10th of a second should ever have to poll. */
+#define MINPOLL 5
+#define MAXPOLL 20
+int test7(int argc, const char* argv[])
+{
+ (void)argc; (void)argv;
+ fprintf(stdout, "Output on stdout before sleep.\n");
+ fprintf(stderr, "Output on stderr before sleep.\n");
+ fflush(stdout);
+ fflush(stderr);
+ /* Sleep for 1 second. */
+#if defined(_WIN32)
+ Sleep(1000);
+#else
+ sleep(1);
+#endif
+ fprintf(stdout, "Output on stdout after sleep.\n");
+ fprintf(stderr, "Output on stderr after sleep.\n");
+ fflush(stdout);
+ fflush(stderr);
+ return 0;
+}
+
+int test8(int argc, const char* argv[])
+{
+ /* Create a disowned grandchild to test handling of processes
+ that exit before their children. */
+ int r;
+ const char* cmd[4];
+ (void)argc;
+ cmd[0] = argv[0];
+ cmd[1] = "run";
+ cmd[2] = "108";
+ cmd[3] = 0;
+ fprintf(stdout, "Output on stdout before grandchild test.\n");
+ fprintf(stderr, "Output on stderr before grandchild test.\n");
+ fflush(stdout);
+ fflush(stderr);
+ r = runChild(cmd, kwsysProcess_State_Disowned, kwsysProcess_Exception_None,
+ 1, 1, 1, 0, 10, 0, 1, 1);
+ fprintf(stdout, "Output on stdout after grandchild test.\n");
+ fprintf(stderr, "Output on stderr after grandchild test.\n");
+ fflush(stdout);
+ fflush(stderr);
+ return r;
+}
+
+int test8_grandchild(int argc, const char* argv[])
+{
+ (void)argc; (void)argv;
+ fprintf(stdout, "Output on stdout from grandchild before sleep.\n");
+ fprintf(stderr, "Output on stderr from grandchild before sleep.\n");
+ fflush(stdout);
+ fflush(stderr);
+ /* TODO: Instead of closing pipes here leave them open to make sure
+ the grandparent can stop listening when the parent exits. This
+ part of the test cannot be enabled until the feature is
+ implemented. */
+ fclose(stdout);
+ fclose(stderr);
+#if defined(_WIN32)
+ Sleep(15000);
+#else
+ sleep(15);
+#endif
+ return 0;
+}
+
+int runChild2(kwsysProcess* kp,
+ const char* cmd[], int state, int exception, int value,
+ int share, int output, int delay, double timeout,
+ int poll, int disown)
+{
+ int result = 0;
+ char* data = 0;
+ int length = 0;
+ double userTimeout = 0;
+ double* pUserTimeout = 0;
+ kwsysProcess_SetCommand(kp, cmd);
+ if(timeout >= 0)
+ {
+ kwsysProcess_SetTimeout(kp, timeout);
+ }
+ if(share)
+ {
+ kwsysProcess_SetPipeShared(kp, kwsysProcess_Pipe_STDOUT, 1);
+ kwsysProcess_SetPipeShared(kp, kwsysProcess_Pipe_STDERR, 1);
+ }
+ if(disown)
+ {
+ kwsysProcess_SetOption(kp, kwsysProcess_Option_Detach, 1);
+ }
+ kwsysProcess_Execute(kp);
+
+ if(poll)
+ {
+ pUserTimeout = &userTimeout;
+ }
+
+ if(!share && !disown)
+ {
+ int p;
+ while((p = kwsysProcess_WaitForData(kp, &data, &length, pUserTimeout)))
+ {
+ if(output)
+ {
+ if(poll && p == kwsysProcess_Pipe_Timeout)
+ {
+ fprintf(stdout, "WaitForData timeout reached.\n");
+ fflush(stdout);
+
+ /* Count the number of times we polled without getting data.
+ If it is excessive then kill the child and fail. */
+ if(++poll >= MAXPOLL)
+ {
+ fprintf(stdout, "Poll count reached limit %d.\n",
+ MAXPOLL);
+ kwsysProcess_Kill(kp);
+ }
+ }
+ else
+ {
+ fwrite(data, 1, (size_t) length, stdout);
+ fflush(stdout);
+ }
+ }
+ if(poll)
+ {
+ /* Delay to avoid busy loop during polling. */
+#if defined(_WIN32)
+ Sleep(100);
+#else
+ testProcess_usleep(100000);
+#endif
+ }
+ if(delay)
+ {
+ /* Purposely sleeping only on Win32 to let pipe fill up. */
+#if defined(_WIN32)
+ Sleep(100);
+#endif
+ }
+ }
+ }
+
+ if(disown)
+ {
+ kwsysProcess_Disown(kp);
+ }
+ else
+ {
+ kwsysProcess_WaitForExit(kp, 0);
+ }
+
+ switch (kwsysProcess_GetState(kp))
+ {
+ case kwsysProcess_State_Starting:
+ printf("No process has been executed.\n"); break;
+ case kwsysProcess_State_Executing:
+ printf("The process is still executing.\n"); break;
+ case kwsysProcess_State_Expired:
+ printf("Child was killed when timeout expired.\n"); break;
+ case kwsysProcess_State_Exited:
+ printf("Child exited with value = %d\n",
+ kwsysProcess_GetExitValue(kp));
+ result = ((exception != kwsysProcess_GetExitException(kp)) ||
+ (value != kwsysProcess_GetExitValue(kp))); break;
+ case kwsysProcess_State_Killed:
+ printf("Child was killed by parent.\n"); break;
+ case kwsysProcess_State_Exception:
+ printf("Child terminated abnormally: %s\n",
+ kwsysProcess_GetExceptionString(kp));
+ result = ((exception != kwsysProcess_GetExitException(kp)) ||
+ (value != kwsysProcess_GetExitValue(kp))); break;
+ case kwsysProcess_State_Disowned:
+ printf("Child was disowned.\n"); break;
+ case kwsysProcess_State_Error:
+ printf("Error in administrating child process: [%s]\n",
+ kwsysProcess_GetErrorString(kp)); break;
+ };
+
+ if(result)
+ {
+ if(exception != kwsysProcess_GetExitException(kp))
+ {
+ fprintf(stderr, "Mismatch in exit exception. "
+ "Should have been %d, was %d.\n",
+ exception, kwsysProcess_GetExitException(kp));
+ }
+ if(value != kwsysProcess_GetExitValue(kp))
+ {
+ fprintf(stderr, "Mismatch in exit value. "
+ "Should have been %d, was %d.\n",
+ value, kwsysProcess_GetExitValue(kp));
+ }
+ }
+
+ if(kwsysProcess_GetState(kp) != state)
+ {
+ fprintf(stderr, "Mismatch in state. "
+ "Should have been %d, was %d.\n",
+ state, kwsysProcess_GetState(kp));
+ result = 1;
+ }
+
+ /* We should have polled more times than there were data if polling
+ was enabled. */
+ if(poll && poll < MINPOLL)
+ {
+ fprintf(stderr, "Poll count is %d, which is less than %d.\n",
+ poll, MINPOLL);
+ result = 1;
+ }
+
+ return result;
+}
+
+int runChild(const char* cmd[], int state, int exception, int value,
+ int share, int output, int delay, double timeout,
+ int poll, int repeat, int disown)
+{
+ int result = 1;
+ kwsysProcess* kp = kwsysProcess_New();
+ if(!kp)
+ {
+ fprintf(stderr, "kwsysProcess_New returned NULL!\n");
+ return 1;
+ }
+ while(repeat-- > 0)
+ {
+ result = runChild2(kp, cmd, state, exception, value, share,
+ output, delay, timeout, poll, disown);
+ }
+ kwsysProcess_Delete(kp);
+ return result;
+}
+
+int main(int argc, const char* argv[])
+{
+ int n = 0;
+#if 0
+ {
+ HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
+ DuplicateHandle(GetCurrentProcess(), out,
+ GetCurrentProcess(), &out, 0, FALSE,
+ DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
+ SetStdHandle(STD_OUTPUT_HANDLE, out);
+ }
+ {
+ HANDLE out = GetStdHandle(STD_ERROR_HANDLE);
+ DuplicateHandle(GetCurrentProcess(), out,
+ GetCurrentProcess(), &out, 0, FALSE,
+ DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
+ SetStdHandle(STD_ERROR_HANDLE, out);
+ }
+#endif
+ if(argc == 2)
+ {
+ n = atoi(argv[1]);
+ }
+ else if(argc == 3 && strcmp(argv[1], "run") == 0)
+ {
+ n = atoi(argv[2]);
+ }
+ /* Check arguments. */
+ if(((n >= 1 && n <= 8) || n == 108) && argc == 3)
+ {
+ /* This is the child process for a requested test number. */
+ switch (n)
+ {
+ case 1: return test1(argc, argv);
+ case 2: return test2(argc, argv);
+ case 3: return test3(argc, argv);
+ case 4: return test4(argc, argv);
+ case 5: return test5(argc, argv);
+ case 6: test6(argc, argv); return 0;
+ case 7: return test7(argc, argv);
+ case 8: return test8(argc, argv);
+ case 108: return test8_grandchild(argc, argv);
+ }
+ fprintf(stderr, "Invalid test number %d.\n", n);
+ return 1;
+ }
+ else if(n >= 1 && n <= 8)
+ {
+ /* This is the parent process for a requested test number. */
+ int states[8] =
+ {
+ kwsysProcess_State_Exited,
+ kwsysProcess_State_Exited,
+ kwsysProcess_State_Expired,
+ kwsysProcess_State_Exception,
+ kwsysProcess_State_Exited,
+ kwsysProcess_State_Expired,
+ kwsysProcess_State_Exited,
+ kwsysProcess_State_Exited
+ };
+ int exceptions[8] =
+ {
+ kwsysProcess_Exception_None,
+ kwsysProcess_Exception_None,
+ kwsysProcess_Exception_None,
+ kwsysProcess_Exception_Fault,
+ kwsysProcess_Exception_None,
+ kwsysProcess_Exception_None,
+ kwsysProcess_Exception_None,
+ kwsysProcess_Exception_None
+ };
+ int values[8] = {0, 123, 1, 1, 0, 0, 0, 0};
+ int outputs[8] = {1, 1, 1, 1, 1, 0, 1, 1};
+ int delays[8] = {0, 0, 0, 0, 0, 1, 0, 0};
+ double timeouts[8] = {10, 10, 10, 30, 30, 10, -1, 10};
+ int polls[8] = {0, 0, 0, 0, 0, 0, 1, 0};
+ int repeat[8] = {2, 1, 1, 1, 1, 1, 1, 1};
+ int r;
+ const char* cmd[4];
+#ifdef _WIN32
+ char* argv0 = 0;
+ if(n == 0 && (argv0 = strdup(argv[0])))
+ {
+ /* Try converting to forward slashes to see if it works. */
+ char* c;
+ for(c=argv0; *c; ++c)
+ {
+ if(*c == '\\')
+ {
+ *c = '/';
+ }
+ }
+ cmd[0] = argv0;
+ }
+ else
+ {
+ cmd[0] = argv[0];
+ }
+#else
+ cmd[0] = argv[0];
+#endif
+ cmd[1] = "run";
+ cmd[2] = argv[1];
+ cmd[3] = 0;
+ fprintf(stdout, "Output on stdout before test %d.\n", n);
+ fprintf(stderr, "Output on stderr before test %d.\n", n);
+ fflush(stdout);
+ fflush(stderr);
+ r = runChild(cmd, states[n-1], exceptions[n-1], values[n-1], 0,
+ outputs[n-1], delays[n-1], timeouts[n-1],
+ polls[n-1], repeat[n-1], 0);
+ fprintf(stdout, "Output on stdout after test %d.\n", n);
+ fprintf(stderr, "Output on stderr after test %d.\n", n);
+ fflush(stdout);
+ fflush(stderr);
+#if _WIN32
+ if(argv0) { free(argv0); }
+#endif
+ return r;
+ }
+ else if(argc > 2 && strcmp(argv[1], "0") == 0)
+ {
+ /* This is the special debugging test to run a given command
+ line. */
+ const char** cmd = argv+2;
+ int state = kwsysProcess_State_Exited;
+ int exception = kwsysProcess_Exception_None;
+ int value = 0;
+ double timeout = 0;
+ int r = runChild(cmd, state, exception, value, 0, 1, 0, timeout, 0, 1, 0);
+ return r;
+ }
+ else
+ {
+ /* Improper usage. */
+ fprintf(stdout, "Usage: %s <test number>\n", argv[0]);
+ return 1;
+ }
+}