Commit 906c2cae authored by James Johnston's avatar James Johnston Committed by Brad King
Browse files

Process: Added test cases for testing Ctrl+C and process groups.

Two new tests were added to testProcess:
 * Test 9 is constructed of the root test process, a child process,
   and a grandchild process.  The grandchild ignores all Ctrl+C signals
   and then sleeps.  The child runs the grandchild normally.  The root
   process runs the child in a new process group, sends it a Ctrl+C
   signal, and then lets the process expire to prove that the child was
   blocked waiting for the uninterruptable grandchild to die.
 * Test 10 is constructed of the root test process, a child process,
   and a grandchild process.  The grandchild sleeps and processes
   signals normally.  The child runs the grandchild in a new process
   group.  The root process runs the child in a new process group as
   well, sends it a Ctrl+C, and then verifies that: (1) the child does
   indeed terminate with an interrupt signal, (2) the child did not
   expire, proving that it retransmitted the signal to the sleeping
   grandchild before waiting for the grandchild to terminate.

Change-Id: Iba5bee546a82eb61a41d4194341e9382a00279d4
parent ef517b19
......@@ -1237,7 +1237,7 @@ IF(KWSYS_STANDALONE OR CMake_SOURCE_DIR)
IF(NOT CYGWIN)
SET(KWSYS_TEST_PROCESS_7 7)
ENDIF()
FOREACH(n 1 2 3 4 5 6 ${KWSYS_TEST_PROCESS_7})
FOREACH(n 1 2 3 4 5 6 ${KWSYS_TEST_PROCESS_7} 9 10)
ADD_TEST(kwsys.testProcess-${n} ${EXEC_DIR}/${KWSYS_NAMESPACE}TestProcess ${n})
SET_PROPERTY(TEST kwsys.testProcess-${n} PROPERTY LABELS ${KWSYS_LABELS_TEST})
SET_TESTS_PROPERTIES(kwsys.testProcess-${n} PROPERTIES TIMEOUT 120)
......@@ -1270,6 +1270,10 @@ IF(KWSYS_STANDALONE OR CMake_SOURCE_DIR)
MESSAGE(STATUS "GET_TEST_PROPERTY returned: ${wfv}")
ENDIF()
# Set up ctest custom configuration file.
CONFIGURE_FILE(${PROJECT_SOURCE_DIR}/CTestCustom.cmake.in
${PROJECT_BINARY_DIR}/CTestCustom.cmake @ONLY)
# Suppress known consistent failures on buggy systems.
IF(KWSYS_TEST_BOGUS_FAILURES)
SET_TESTS_PROPERTIES(${KWSYS_TEST_BOGUS_FAILURES} PROPERTIES WILL_FAIL ON)
......
# kwsys.testProcess-10 involves sending SIGINT to a child process, which then
# exits abnormally via a call to _exit(). (On Windows, a call to ExitProcess).
# Naturally, this results in plenty of memory being "leaked" by this child
# process - the memory check results are not meaningful in this case.
#
# kwsys.testProcess-9 also tests sending SIGINT to a child process. However,
# normal operation of that test involves the child process timing out, and the
# host process kills (SIGKILL) it as a result. Since it was SIGKILL'ed, the
# resulting memory leaks are not logged by valgrind anyway. Therefore, we
# don't have to exclude it.
set(CTEST_CUSTOM_MEMCHECK_IGNORE
${CTEST_CUSTOM_MEMCHECK_IGNORE}
kwsys.testProcess-10
)
......@@ -29,6 +29,7 @@
# include <windows.h>
#else
# include <unistd.h>
# include <signal.h>
#endif
#if defined(__BORLANDC__)
......@@ -68,7 +69,8 @@ static void testProcess_sleep(unsigned int sec)
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 repeat, int disown, int createNewGroup,
int interruptDelay);
static int test1(int argc, const char* argv[])
{
......@@ -118,7 +120,7 @@ static int test4(int argc, const char* argv[])
#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");
fprintf(stderr, "Output before crash on stderr from crash test.\n");
fflush(stdout);
fflush(stderr);
assert(invalidAddress); /* Quiet Clang scan-build. */
......@@ -143,7 +145,7 @@ static int test5(int argc, const char* argv[])
fflush(stdout);
fflush(stderr);
r = runChild(cmd, kwsysProcess_State_Exception,
kwsysProcess_Exception_Fault, 1, 1, 1, 0, 15, 0, 1, 0);
kwsysProcess_Exception_Fault, 1, 1, 1, 0, 15, 0, 1, 0, 0, 0);
fprintf(stdout, "Output on stdout after recursive test.\n");
fprintf(stderr, "Output on stderr after recursive test.\n");
fflush(stdout);
......@@ -208,7 +210,7 @@ static int test8(int argc, const char* argv[])
fflush(stdout);
fflush(stderr);
r = runChild(cmd, kwsysProcess_State_Disowned, kwsysProcess_Exception_None,
1, 1, 1, 0, 10, 0, 1, 1);
1, 1, 1, 0, 10, 0, 1, 1, 0, 0);
fprintf(stdout, "Output on stdout after grandchild test.\n");
fprintf(stderr, "Output on stderr after grandchild test.\n");
fflush(stdout);
......@@ -233,10 +235,132 @@ static int test8_grandchild(int argc, const char* argv[])
return 0;
}
static int test9(int argc, const char* argv[])
{
/* Test Ctrl+C behavior: the root test program will send a Ctrl+C to this
process. Here, we start a child process that sleeps for a long time
while ignoring signals. The test is successful if this process waits
for the child to return before exiting from the Ctrl+C handler.
WARNING: This test will falsely pass if the share parameter of runChild
was set to 0 when invoking the test9 process. */
int r;
const char* cmd[4];
(void)argc;
cmd[0] = argv[0];
cmd[1] = "run";
cmd[2] = "109";
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_Exited,
kwsysProcess_Exception_None,
0, 1, 1, 0, 30, 0, 1, 0, 0, 0);
/* This sleep will avoid a race condition between this function exiting
normally and our Ctrl+C handler exiting abnormally after the process
exits. */
testProcess_sleep(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;
}
#if defined(_WIN32)
static BOOL WINAPI test9_grandchild_handler(DWORD dwCtrlType)
{
/* Ignore all Ctrl+C/Break signals. We must use an actual handler function
instead of using SetConsoleCtrlHandler(NULL, TRUE) so that we can also
ignore Ctrl+Break in addition to Ctrl+C. */
(void)dwCtrlType;
return TRUE;
}
#endif
static int test9_grandchild(int argc, const char* argv[])
{
/* The grandchild just sleeps for a few seconds while ignoring signals. */
(void)argc; (void)argv;
#if defined(_WIN32)
if(!SetConsoleCtrlHandler(test9_grandchild_handler, TRUE))
{
return 1;
}
#else
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
if(sigaction(SIGINT, &sa, 0) < 0)
{
return 1;
}
#endif
fprintf(stdout, "Output on stdout from grandchild before sleep.\n");
fprintf(stderr, "Output on stderr from grandchild before sleep.\n");
fflush(stdout);
fflush(stderr);
/* Sleep for 9 seconds. */
testProcess_sleep(9);
fprintf(stdout, "Output on stdout from grandchild after sleep.\n");
fprintf(stderr, "Output on stderr from grandchild after sleep.\n");
fflush(stdout);
fflush(stderr);
return 0;
}
static int test10(int argc, const char* argv[])
{
/* Test Ctrl+C behavior: the root test program will send a Ctrl+C to this
process. Here, we start a child process that sleeps for a long time and
processes signals normally. However, this grandchild is created in a new
process group - ensuring that Ctrl+C we receive is sent to our process
groups. We make sure it exits anyway. */
int r;
const char* cmd[4];
(void)argc;
cmd[0] = argv[0];
cmd[1] = "run";
cmd[2] = "110";
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_Exception,
kwsysProcess_Exception_Interrupt,
0, 1, 1, 0, 30, 0, 1, 0, 1, 0);
fprintf(stdout, "Output on stdout after grandchild test.\n");
fprintf(stderr, "Output on stderr after grandchild test.\n");
fflush(stdout);
fflush(stderr);
return r;
}
static int test10_grandchild(int argc, const char* argv[])
{
/* The grandchild just sleeps for a few seconds and handles signals. */
(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);
/* Sleep for 6 seconds. */
testProcess_sleep(6);
fprintf(stdout, "Output on stdout from grandchild after sleep.\n");
fprintf(stderr, "Output on stderr from grandchild after sleep.\n");
fflush(stdout);
fflush(stderr);
return 0;
}
static 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 poll, int disown, int createNewGroup, int interruptDelay)
{
int result = 0;
char* data = 0;
......@@ -257,6 +381,10 @@ static int runChild2(kwsysProcess* kp,
{
kwsysProcess_SetOption(kp, kwsysProcess_Option_Detach, 1);
}
if(createNewGroup)
{
kwsysProcess_SetOption(kp, kwsysProcess_Option_CreateProcessGroup, 1);
}
kwsysProcess_Execute(kp);
if(poll)
......@@ -264,6 +392,12 @@ static int runChild2(kwsysProcess* kp,
pUserTimeout = &userTimeout;
}
if(interruptDelay)
{
testProcess_sleep(interruptDelay);
kwsysProcess_Interrupt(kp);
}
if(!share && !disown)
{
int p;
......@@ -341,7 +475,7 @@ static int runChild2(kwsysProcess* kp,
printf("Error in administrating child process: [%s]\n",
kwsysProcess_GetErrorString(kp)); break;
};
if(result)
{
if(exception != kwsysProcess_GetExitException(kp))
......@@ -357,7 +491,7 @@ static int runChild2(kwsysProcess* kp,
value, kwsysProcess_GetExitValue(kp));
}
}
if(kwsysProcess_GetState(kp) != state)
{
fprintf(stderr, "Mismatch in state. "
......@@ -378,9 +512,37 @@ static int runChild2(kwsysProcess* kp,
return result;
}
/**
* Runs a child process and blocks until it returns. Arguments as follows:
*
* cmd = Command line to run.
* state = Expected return value of kwsysProcess_GetState after exit.
* exception = Expected return value of kwsysProcess_GetExitException.
* value = Expected return value of kwsysProcess_GetExitValue.
* share = Whether to share stdout/stderr child pipes with our pipes
* by way of kwsysProcess_SetPipeShared. If false, new pipes
* are created.
* output = If !share && !disown, whether to write the child's stdout
* and stderr output to our stdout.
* delay = If !share && !disown, adds an additional short delay to
* the pipe loop to allow the pipes to fill up; Windows only.
* timeout = Non-zero to sets a timeout in seconds via
* kwsysProcess_SetTimeout.
* poll = If !share && !disown, we count the number of 0.1 second
* intervals where the child pipes had no new data. We fail
* if not in the bounds of MINPOLL/MAXPOLL.
* repeat = Number of times to run the process.
* disown = If set, the process is disowned.
* createNewGroup = If set, the process is created in a new process group.
* interruptDelay = If non-zero, number of seconds to delay before
* interrupting the process. Note that this delay will occur
* BEFORE any reading/polling of pipes occurs and before any
* detachment occurs.
*/
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 poll, int repeat, int disown, int createNewGroup,
int interruptDelay)
{
int result = 1;
kwsysProcess* kp = kwsysProcess_New();
......@@ -392,7 +554,8 @@ int runChild(const char* cmd[], int state, int exception, int value,
while(repeat-- > 0)
{
result = runChild2(kp, cmd, state, exception, value, share,
output, delay, timeout, poll, disown);
output, delay, timeout, poll, disown, createNewGroup,
interruptDelay);
}
kwsysProcess_Delete(kp);
return result;
......@@ -439,7 +602,7 @@ int main(int argc, const char* argv[])
n = atoi(argv[2]);
}
/* Check arguments. */
if(((n >= 1 && n <= 8) || n == 108) && argc == 3)
if(((n >= 1 && n <= 10) || n == 108 || n == 109 || n == 110) && argc == 3)
{
/* This is the child process for a requested test number. */
switch (n)
......@@ -452,15 +615,19 @@ int main(int argc, const char* argv[])
case 6: test6(argc, argv); return 0;
case 7: return test7(argc, argv);
case 8: return test8(argc, argv);
case 9: return test9(argc, argv);
case 10: return test10(argc, argv);
case 108: return test8_grandchild(argc, argv);
case 109: return test9_grandchild(argc, argv);
case 110: return test10_grandchild(argc, argv);
}
fprintf(stderr, "Invalid test number %d.\n", n);
return 1;
}
else if(n >= 1 && n <= 8)
else if(n >= 1 && n <= 10)
{
/* This is the parent process for a requested test number. */
int states[8] =
int states[10] =
{
kwsysProcess_State_Exited,
kwsysProcess_State_Exited,
......@@ -469,9 +636,11 @@ int main(int argc, const char* argv[])
kwsysProcess_State_Exited,
kwsysProcess_State_Expired,
kwsysProcess_State_Exited,
kwsysProcess_State_Exited
kwsysProcess_State_Exited,
kwsysProcess_State_Expired, /* Ctrl+C handler test */
kwsysProcess_State_Exception /* Process group test */
};
int exceptions[8] =
int exceptions[10] =
{
kwsysProcess_Exception_None,
kwsysProcess_Exception_None,
......@@ -480,14 +649,19 @@ int main(int argc, const char* argv[])
kwsysProcess_Exception_None,
kwsysProcess_Exception_None,
kwsysProcess_Exception_None,
kwsysProcess_Exception_None
kwsysProcess_Exception_None,
kwsysProcess_Exception_None,
kwsysProcess_Exception_Interrupt
};
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 values[10] = {0, 123, 1, 1, 0, 0, 0, 0, 1, 1};
int shares[10] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1};
int outputs[10] = {1, 1, 1, 1, 1, 0, 1, 1, 1, 1};
int delays[10] = {0, 0, 0, 0, 0, 1, 0, 0, 0, 0};
double timeouts[10] = {10, 10, 10, 30, 30, 10, -1, 10, 6, 4};
int polls[10] = {0, 0, 0, 0, 0, 0, 1, 0, 0, 0};
int repeat[10] = {2, 1, 1, 1, 1, 1, 1, 1, 1, 1};
int createNewGroups[10] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1};
int interruptDelays[10] = {0, 0, 0, 0, 0, 0, 0, 0, 3, 2};
int r;
const char* cmd[4];
#ifdef _WIN32
......@@ -519,9 +693,10 @@ int main(int argc, const char* argv[])
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,
r = runChild(cmd, states[n-1], exceptions[n-1], values[n-1], shares[n-1],
outputs[n-1], delays[n-1], timeouts[n-1],
polls[n-1], repeat[n-1], 0);
polls[n-1], repeat[n-1], 0, createNewGroups[n-1],
interruptDelays[n-1]);
fprintf(stdout, "Output on stdout after test %d.\n", n);
fprintf(stderr, "Output on stderr after test %d.\n", n);
fflush(stdout);
......@@ -540,7 +715,8 @@ int main(int argc, const char* argv[])
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);
int r = runChild(cmd, state, exception, value, 0, 1, 0, timeout,
0, 1, 0, 0, 0);
return r;
}
else
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment