Rudimentary process management.
Signed-off-by: Martin Bremmer <martin.bremmer@adlinktech.com>
This commit is contained in:
parent
d146716d1d
commit
0269774a60
9 changed files with 1378 additions and 33 deletions
|
@ -25,6 +25,7 @@ set(sources
|
|||
"random.c"
|
||||
"strlcpy.c"
|
||||
"socket.c"
|
||||
"process.c"
|
||||
"select.c")
|
||||
|
||||
add_cunit_executable(cunit_ddsrt ${sources})
|
||||
|
@ -42,3 +43,26 @@ endif()
|
|||
|
||||
target_include_directories(
|
||||
cunit_ddsrt PRIVATE "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>")
|
||||
|
||||
# Create a separate test application that will be used to
|
||||
# test process management.
|
||||
add_executable(process_app process_app.c)
|
||||
target_link_libraries(process_app PRIVATE ddsrt)
|
||||
target_include_directories(
|
||||
process_app
|
||||
PRIVATE
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>")
|
||||
# Force the app to be at the same location, no matter what platform or build type.
|
||||
set_target_properties(
|
||||
process_app
|
||||
PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_BINARY_DIR}
|
||||
RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_CURRENT_BINARY_DIR}
|
||||
RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_CURRENT_BINARY_DIR}
|
||||
RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL ${CMAKE_CURRENT_BINARY_DIR} )
|
||||
# Let the cunit application know the location and name of the test application.
|
||||
set(process_app_name "${CMAKE_CURRENT_BINARY_DIR}/process_app${CMAKE_EXECUTABLE_SUFFIX}")
|
||||
configure_file(
|
||||
"process_test.h.in" "${CMAKE_CURRENT_BINARY_DIR}/include/process_test.h" @ONLY)
|
||||
|
||||
|
|
284
src/ddsrt/tests/process.c
Normal file
284
src/ddsrt/tests/process.c
Normal file
|
@ -0,0 +1,284 @@
|
|||
/*
|
||||
* Copyright(c) 2019 ADLINK Technology Limited and others
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License v. 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
* v. 1.0 which is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "CUnit/Test.h"
|
||||
#include "process_test.h"
|
||||
#include "dds/ddsrt/time.h"
|
||||
#include "dds/ddsrt/strtol.h"
|
||||
#include "dds/ddsrt/environ.h"
|
||||
#include "dds/ddsrt/process.h"
|
||||
|
||||
|
||||
/*
|
||||
* Create a process that is expected to exit quickly.
|
||||
* Compare the exit code with the expected exit code.
|
||||
*/
|
||||
static void create_and_test_exit(const char *arg, int code)
|
||||
{
|
||||
dds_retcode_t ret;
|
||||
ddsrt_pid_t pid;
|
||||
int32_t status;
|
||||
char *argv[] = { NULL, NULL };
|
||||
|
||||
argv[0] = (char*)arg;
|
||||
ret = ddsrt_proc_create(TEST_APPLICATION, argv, &pid);
|
||||
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
|
||||
|
||||
ret = ddsrt_proc_waitpid(pid, DDS_SECS(10), &status);
|
||||
CU_ASSERT_EQUAL(ret, DDS_RETCODE_OK);
|
||||
|
||||
/* Check result. */
|
||||
CU_ASSERT_EQUAL(status, code);
|
||||
|
||||
/* Garbage collection when needed. */
|
||||
if (ret != DDS_RETCODE_OK) {
|
||||
ddsrt_proc_kill(pid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Try to create a process without arguments.
|
||||
* The exit status of the process should be PROCESS_DONE_NOTHING_EXIT_CODE.
|
||||
*/
|
||||
CU_Test(ddsrt_process, create)
|
||||
{
|
||||
create_and_test_exit(NULL, TEST_CREATE_EXIT);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create a process that'll sleep for a while.
|
||||
* Try to kill that process.
|
||||
*/
|
||||
CU_Test(ddsrt_process, kill)
|
||||
{
|
||||
dds_retcode_t ret;
|
||||
ddsrt_pid_t pid;
|
||||
|
||||
/* Sleep for 20 seconds. It should be killed before then. */
|
||||
char *argv[] = { TEST_SLEEP_ARG, "20", NULL };
|
||||
|
||||
ret = ddsrt_proc_create(TEST_APPLICATION, argv, &pid);
|
||||
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
|
||||
CU_ASSERT_NOT_EQUAL_FATAL(pid, 0);
|
||||
|
||||
/* Check if process is running. */
|
||||
ret = ddsrt_proc_exists(pid);
|
||||
CU_ASSERT_EQUAL(ret, DDS_RETCODE_OK);
|
||||
|
||||
/* Kill it. */
|
||||
ret = ddsrt_proc_kill(pid);
|
||||
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
|
||||
|
||||
/* Check if process is actually gone. */
|
||||
ret = ddsrt_proc_waitpid(pid, DDS_SECS(10), NULL);
|
||||
CU_ASSERT_EQUAL(ret, DDS_RETCODE_OK);
|
||||
ret = ddsrt_proc_exists(pid);
|
||||
CU_ASSERT_EQUAL(ret, DDS_RETCODE_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create a process that'll return it's own pid value (reduced
|
||||
* to fit the exit code range). It should match the pid that was
|
||||
* returned by the process create (also reduced to be able to
|
||||
* match the returned semi-pid value).
|
||||
*/
|
||||
CU_Test(ddsrt_process, pid)
|
||||
{
|
||||
dds_retcode_t ret;
|
||||
ddsrt_pid_t pid;
|
||||
int32_t status;
|
||||
char *argv[] = { TEST_PID_ARG, NULL };
|
||||
|
||||
ret = ddsrt_proc_create(TEST_APPLICATION, argv, &pid);
|
||||
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
|
||||
CU_ASSERT_NOT_EQUAL_FATAL(pid, 0);
|
||||
|
||||
ret = ddsrt_proc_waitpid(pid, DDS_SECS(10), &status);
|
||||
CU_ASSERT_EQUAL(ret, DDS_RETCODE_OK);
|
||||
|
||||
/* Compare the pid values. */
|
||||
CU_ASSERT_EQUAL(status, TEST_PID_EXIT(pid));
|
||||
|
||||
/* Garbage collection when needed. */
|
||||
if (ret != DDS_RETCODE_OK) {
|
||||
ddsrt_proc_kill(pid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Set a environment variable in the parent process.
|
||||
* Create a process that should have access to that env var.
|
||||
*/
|
||||
CU_Test(ddsrt_process, env)
|
||||
{
|
||||
dds_retcode_t ret;
|
||||
|
||||
ret = ddsrt_setenv(TEST_ENV_VAR_NAME, TEST_ENV_VAR_VALUE);
|
||||
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
|
||||
|
||||
create_and_test_exit(TEST_ENV_ARG, TEST_ENV_EXIT);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Try to create a process with an non-existing executable file.
|
||||
* It should fail.
|
||||
*/
|
||||
CU_Test(ddsrt_process, invalid)
|
||||
{
|
||||
dds_retcode_t ret;
|
||||
ddsrt_pid_t pid;
|
||||
|
||||
ret = ddsrt_proc_create("ProbablyNotAnValidExecutable", NULL, &pid);
|
||||
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_BAD_PARAMETER);
|
||||
|
||||
/* Garbage collection when needed. */
|
||||
if (ret == DDS_RETCODE_OK) {
|
||||
ddsrt_proc_kill(pid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create a process with a backslash in the argument
|
||||
*/
|
||||
CU_Test(ddsrt_process, arg_bslash)
|
||||
{
|
||||
create_and_test_exit(TEST_BSLASH_ARG, TEST_BSLASH_EXIT);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create a process with a double-quote in the argument
|
||||
*/
|
||||
CU_Test(ddsrt_process, arg_dquote)
|
||||
{
|
||||
create_and_test_exit(TEST_DQUOTE_ARG, TEST_DQUOTE_EXIT);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create two processes and wait for them simultaneously.
|
||||
*/
|
||||
CU_Test(ddsrt_process, waitpids)
|
||||
{
|
||||
dds_retcode_t ret;
|
||||
ddsrt_pid_t child;
|
||||
ddsrt_pid_t pid1 = 0;
|
||||
ddsrt_pid_t pid2 = 0;
|
||||
int32_t status;
|
||||
|
||||
/* Use retpid option to identify return values. */
|
||||
char *argv[] = { TEST_PID_ARG, NULL };
|
||||
|
||||
/* Start two processes. */
|
||||
ret = ddsrt_proc_create(TEST_APPLICATION, argv, &pid1);
|
||||
CU_ASSERT_EQUAL(ret, DDS_RETCODE_OK);
|
||||
ret = ddsrt_proc_create(TEST_APPLICATION, argv, &pid2);
|
||||
CU_ASSERT_EQUAL(ret, DDS_RETCODE_OK);
|
||||
|
||||
/* Wait for both processes to have finished. */
|
||||
while (((pid1 != 0) || (pid2 != 0)) && (ret == DDS_RETCODE_OK)) {
|
||||
ret = ddsrt_proc_waitpids(DDS_SECS(10), &child, &status);
|
||||
CU_ASSERT_EQUAL(ret, DDS_RETCODE_OK);
|
||||
if (child == pid1) {
|
||||
CU_ASSERT_EQUAL(status, TEST_PID_EXIT(pid1));
|
||||
pid1 = 0;
|
||||
} else if (child == pid2) {
|
||||
CU_ASSERT_EQUAL(status, TEST_PID_EXIT(pid2));
|
||||
pid2 = 0;
|
||||
} else {
|
||||
CU_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
ret = ddsrt_proc_waitpids(DDS_SECS(10), &child, &status);
|
||||
CU_ASSERT_EQUAL(ret, DDS_RETCODE_NOT_FOUND);
|
||||
|
||||
/* Garbage collection when needed. */
|
||||
if (pid1 != 0) {
|
||||
ddsrt_proc_kill(pid1);
|
||||
}
|
||||
if (pid2 != 0) {
|
||||
ddsrt_proc_kill(pid2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create a sleeping process. Waiting for it should timeout.
|
||||
*/
|
||||
CU_Test(ddsrt_process, waitpid_timeout)
|
||||
{
|
||||
dds_retcode_t ret;
|
||||
ddsrt_pid_t pid;
|
||||
|
||||
/* Sleep for 20 seconds. We should have a timeout before then. */
|
||||
char *argv[] = { TEST_SLEEP_ARG, "20", NULL };
|
||||
ret = ddsrt_proc_create(TEST_APPLICATION, argv, &pid);
|
||||
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
|
||||
CU_ASSERT_NOT_EQUAL_FATAL(pid, 0);
|
||||
|
||||
/* Timeout 0 should return DDS_RETCODE_PRECONDITION_NOT_MET when alive. */
|
||||
ret = ddsrt_proc_waitpid(pid, 0, NULL);
|
||||
CU_ASSERT_EQUAL(ret, DDS_RETCODE_PRECONDITION_NOT_MET);
|
||||
|
||||
/* Valid timeout should return DDS_RETCODE_TIMEOUT when alive. */
|
||||
ret = ddsrt_proc_waitpid(pid, DDS_SECS(1), NULL);
|
||||
CU_ASSERT_EQUAL(ret, DDS_RETCODE_TIMEOUT);
|
||||
|
||||
/* Kill it. */
|
||||
ret = ddsrt_proc_kill(pid);
|
||||
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
|
||||
|
||||
/* When killed, DDS_RETCODE_OK should be returned. */
|
||||
ret = ddsrt_proc_waitpid(pid, DDS_SECS(10), NULL);
|
||||
CU_ASSERT_EQUAL(ret, DDS_RETCODE_OK);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create a sleeping process. Waiting for it should timeout.
|
||||
*/
|
||||
CU_Test(ddsrt_process, waitpids_timeout)
|
||||
{
|
||||
dds_retcode_t ret;
|
||||
ddsrt_pid_t pid;
|
||||
|
||||
/* Sleep for 20 seconds. We should have a timeout before then. */
|
||||
char *argv[] = { TEST_SLEEP_ARG, "20", NULL };
|
||||
ret = ddsrt_proc_create(TEST_APPLICATION, argv, &pid);
|
||||
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
|
||||
CU_ASSERT_NOT_EQUAL_FATAL(pid, 0);
|
||||
|
||||
/* Timeout 0 should return DDS_RETCODE_PRECONDITION_NOT_MET when alive. */
|
||||
ret = ddsrt_proc_waitpids(0, NULL, NULL);
|
||||
CU_ASSERT_EQUAL(ret, DDS_RETCODE_PRECONDITION_NOT_MET);
|
||||
|
||||
/* Valid timeout should return DDS_RETCODE_TIMEOUT when alive. */
|
||||
ret = ddsrt_proc_waitpids(DDS_SECS(1), NULL, NULL);
|
||||
CU_ASSERT_EQUAL(ret, DDS_RETCODE_TIMEOUT);
|
||||
|
||||
/* Kill it. */
|
||||
ret = ddsrt_proc_kill(pid);
|
||||
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
|
||||
|
||||
/* When killed, DDS_RETCODE_OK should be returned. */
|
||||
ret = ddsrt_proc_waitpids(DDS_SECS(10), NULL, NULL);
|
||||
CU_ASSERT_EQUAL(ret, DDS_RETCODE_OK);
|
||||
}
|
108
src/ddsrt/tests/process_app.c
Normal file
108
src/ddsrt/tests/process_app.c
Normal file
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Copyright(c) 2019 ADLINK Technology Limited and others
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License v. 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
* v. 1.0 which is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "process_test.h"
|
||||
#include "dds/ddsrt/strtol.h"
|
||||
#include "dds/ddsrt/environ.h"
|
||||
#include "dds/ddsrt/process.h"
|
||||
|
||||
|
||||
static int test_create(void)
|
||||
{
|
||||
printf(" Process: created without args.\n");
|
||||
return TEST_CREATE_EXIT;
|
||||
}
|
||||
|
||||
static int test_sleep(int argi, int argc, char **argv)
|
||||
{
|
||||
argi++;
|
||||
if (argi < argc) {
|
||||
long long dorment;
|
||||
ddsrt_strtoll(argv[argi], NULL, 0, &dorment);
|
||||
printf(" Process: sleep %d seconds.\n", (int)dorment);
|
||||
dds_sleepfor(DDS_SECS((int64_t)dorment));
|
||||
} else {
|
||||
printf(" Process: no --sleep value.\n");
|
||||
return TEST_EXIT_WRONG_ARGS;
|
||||
}
|
||||
/* Expected to be destroyed before reaching this. */
|
||||
return TEST_EXIT_FAILURE;
|
||||
}
|
||||
|
||||
static int test_pid(void)
|
||||
{
|
||||
int ret;
|
||||
ddsrt_pid_t pid;
|
||||
pid = ddsrt_getpid();
|
||||
ret = TEST_PID_EXIT(pid);
|
||||
printf(" Process: pid %d reduced to %d exit code.\n", (int)pid, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test_env(void)
|
||||
{
|
||||
int ret = TEST_EXIT_FAILURE;
|
||||
char *envptr = NULL;
|
||||
if (ddsrt_getenv(TEST_ENV_VAR_NAME, &envptr) == DDS_RETCODE_OK) {
|
||||
printf(" Process: env %s=%s.\n", TEST_ENV_VAR_NAME, envptr);
|
||||
if (strcmp(envptr, TEST_ENV_VAR_VALUE) == 0) {
|
||||
ret = TEST_ENV_EXIT;
|
||||
}
|
||||
} else {
|
||||
printf(" Process: failed to get environment variable.\n");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test_bslash(void)
|
||||
{
|
||||
printf(" Process: backslash argument.\n");
|
||||
return TEST_BSLASH_EXIT;
|
||||
}
|
||||
|
||||
static int test_dquote(void)
|
||||
{
|
||||
printf(" Process: double-quote argument.\n");
|
||||
return TEST_DQUOTE_EXIT;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The spawned application used in the process tests.
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (argc == 1) {
|
||||
ret = test_create();
|
||||
} else {
|
||||
if (strcmp(argv[1], TEST_SLEEP_ARG) == 0) {
|
||||
ret = test_sleep(1, argc, argv);
|
||||
} else if (strcmp(argv[1], TEST_PID_ARG) == 0) {
|
||||
ret = test_pid();
|
||||
} else if (strcmp(argv[1], TEST_ENV_ARG) == 0) {
|
||||
ret = test_env();
|
||||
} else if (strcmp(argv[1], TEST_BSLASH_ARG) == 0) {
|
||||
ret = test_bslash();
|
||||
} else if (strcmp(argv[1], TEST_DQUOTE_ARG) == 0) {
|
||||
ret = test_dquote();
|
||||
} else {
|
||||
printf(" Process: unknown argument.\n");
|
||||
ret = TEST_EXIT_WRONG_ARGS;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
42
src/ddsrt/tests/process_test.h.in
Normal file
42
src/ddsrt/tests/process_test.h.in
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License v. 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||||
* v. 1.0 which is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||||
*/
|
||||
#ifndef DDSRT_TEST_PROCESS_TEST_H
|
||||
#define DDSRT_TEST_PROCESS_TEST_H
|
||||
|
||||
/* Get the application name from cmake to automatically
|
||||
* get the proper extension and location. */
|
||||
#define TEST_APPLICATION "@process_app_name@"
|
||||
|
||||
#define TEST_SLEEP_ARG "--sleep"
|
||||
|
||||
#define TEST_EXIT_GENERIC_OK (0)
|
||||
#define TEST_EXIT_FAILURE (1)
|
||||
#define TEST_EXIT_WRONG_ARGS (2)
|
||||
|
||||
#define TEST_CREATE_ARG NULL
|
||||
#define TEST_CREATE_EXIT (0)
|
||||
|
||||
#define TEST_PID_ARG "--retpid"
|
||||
#define TEST_PID_EXIT(pid) ((int)(int32_t)(pid % 127))
|
||||
|
||||
#define TEST_ENV_ARG "--checkenv"
|
||||
#define TEST_ENV_EXIT (12)
|
||||
#define TEST_ENV_VAR_NAME "TEST_ENV_VAR_NAME"
|
||||
#define TEST_ENV_VAR_VALUE "TEST_ENV_VAR_VALUE"
|
||||
|
||||
#define TEST_BSLASH_ARG "\\left\\\\right\\"
|
||||
#define TEST_BSLASH_EXIT (int)('\\')
|
||||
|
||||
#define TEST_DQUOTE_ARG "\"left\"\"right\""
|
||||
#define TEST_DQUOTE_EXIT (int)('"')
|
||||
|
||||
#endif /* DDSRT_TEST_PROCESS_TEST_H */
|
Loading…
Add table
Add a link
Reference in a new issue