mirror of https://github.com/libsdl-org/SDL.git
Add more SDL_Process tests
This commit is contained in:
parent
f6055432c8
commit
7241dd9ec3
|
|
@ -368,13 +368,6 @@ add_sdl_test_executable(testmouse SOURCES testmouse.c)
|
|||
add_sdl_test_executable(testoverlay NEEDS_RESOURCES TESTUTILS SOURCES testoverlay.c)
|
||||
add_sdl_test_executable(testplatform NONINTERACTIVE SOURCES testplatform.c)
|
||||
add_sdl_test_executable(testpower NONINTERACTIVE SOURCES testpower.c)
|
||||
add_sdl_test_executable(testprocess
|
||||
NONINTERACTIVE THREADS
|
||||
NONINTERACTIVE_ARGS $<TARGET_FILE:childprocess>
|
||||
INSTALLED_ARGS "${CMAKE_INSTALL_FULL_LIBEXECDIR}/installed-tests/SDL3/childprocess${CMAKE_EXECUTABLE_SUFFIX}"
|
||||
SOURCES testprocess.c)
|
||||
add_sdl_test_executable(childprocess SOURCES childprocess.c)
|
||||
add_dependencies(testprocess childprocess)
|
||||
add_sdl_test_executable(testfilesystem NONINTERACTIVE SOURCES testfilesystem.c)
|
||||
if(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||
add_sdl_test_executable(pretest SOURCES pretest.c NONINTERACTIVE NONINTERACTIVE_TIMEOUT 60)
|
||||
|
|
@ -414,6 +407,16 @@ add_sdl_test_executable(testtime SOURCES testtime.c)
|
|||
add_sdl_test_executable(testmanymouse SOURCES testmanymouse.c)
|
||||
add_sdl_test_executable(testmodal SOURCES testmodal.c)
|
||||
|
||||
|
||||
add_sdl_test_executable(testprocess
|
||||
NONINTERACTIVE THREADS
|
||||
NONINTERACTIVE_ARGS $<TARGET_FILE:childprocess>
|
||||
INSTALLED_ARGS "${CMAKE_INSTALL_FULL_LIBEXECDIR}/installed-tests/SDL3/childprocess${CMAKE_EXECUTABLE_SUFFIX}"
|
||||
SOURCES testprocess.c
|
||||
)
|
||||
add_sdl_test_executable(childprocess SOURCES childprocess.c)
|
||||
add_dependencies(testprocess childprocess)
|
||||
|
||||
if (HAVE_WAYLAND)
|
||||
# Set the GENERATED property on the protocol file, since it is first created at build time
|
||||
set_property(SOURCE ${SDL3_BINARY_DIR}/wayland-generated-protocols/xdg-shell-protocol.c PROPERTY GENERATED 1)
|
||||
|
|
|
|||
|
|
@ -5,15 +5,20 @@
|
|||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef SDL_PLATFORM_WINDOWS
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
SDLTest_CommonState *state;
|
||||
int i;
|
||||
const char *expect_environment = NULL;
|
||||
bool expect_environment_match = false;
|
||||
bool print_arguments = false;
|
||||
bool print_environment = false;
|
||||
bool stdin_to_stdout = false;
|
||||
bool read_stdin = false;
|
||||
bool stdin_to_stderr = false;
|
||||
int exit_code = 0;
|
||||
|
||||
|
|
@ -21,23 +26,22 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
for (i = 1; i < argc;) {
|
||||
int consumed = SDLTest_CommonArg(state, i);
|
||||
if (!consumed) {
|
||||
if (SDL_strcmp(argv[i], "--print-arguments") == 0) {
|
||||
print_arguments = true;
|
||||
consumed = 1;
|
||||
} else if (SDL_strcmp(argv[i], "--print-environment") == 0) {
|
||||
print_environment = true;
|
||||
consumed = 1;
|
||||
} else if (SDL_strcmp(argv[i], "--expect-env") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
expect_environment = argv[i + 1];
|
||||
consumed = 2;
|
||||
}
|
||||
} else if (SDL_strcmp(argv[i], "--stdin-to-stdout") == 0) {
|
||||
stdin_to_stdout = true;
|
||||
consumed = 1;
|
||||
} else if (SDL_strcmp(argv[i], "--stdin-to-stderr") == 0) {
|
||||
stdin_to_stderr = true;
|
||||
consumed = 1;
|
||||
} else if (SDL_strcmp(argv[i], "--stdin") == 0) {
|
||||
read_stdin = true;
|
||||
consumed = 1;
|
||||
} else if (SDL_strcmp(argv[i], "--stdout") == 0) {
|
||||
if (i + 1 < argc) {
|
||||
fprintf(stdout, "%s", argv[i + 1]);
|
||||
|
|
@ -56,15 +60,28 @@ int main(int argc, char *argv[]) {
|
|||
consumed = 2;
|
||||
}
|
||||
}
|
||||
} else if (SDL_strcmp(argv[i], "--version") == 0) {
|
||||
int version = SDL_GetVersion();
|
||||
fprintf(stdout, "SDL version %d.%d.%d",
|
||||
SDL_VERSIONNUM_MAJOR(version),
|
||||
SDL_VERSIONNUM_MINOR(version),
|
||||
SDL_VERSIONNUM_MICRO(version));
|
||||
fprintf(stderr, "SDL version %d.%d.%d",
|
||||
SDL_VERSIONNUM_MAJOR(version),
|
||||
SDL_VERSIONNUM_MINOR(version),
|
||||
SDL_VERSIONNUM_MICRO(version));
|
||||
consumed = 1;
|
||||
break;
|
||||
} else if (SDL_strcmp(argv[i], "--") == 0) {
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (consumed <= 0) {
|
||||
const char *args[] = {
|
||||
"[--print-arguments]",
|
||||
"[--print-environment]",
|
||||
"[--expect-env KEY=VAL]",
|
||||
"[--stdin]",
|
||||
"[--stdin-to-stdout]",
|
||||
"[--stdout TEXT]",
|
||||
"[--stdin-to-stderr]",
|
||||
|
|
@ -86,48 +103,52 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
}
|
||||
|
||||
if (print_environment || expect_environment) {
|
||||
if (print_environment) {
|
||||
char **env = SDL_GetEnvironmentVariables(SDL_GetEnvironment());
|
||||
if (env) {
|
||||
for (i = 0; env[i]; ++i) {
|
||||
if (print_environment) {
|
||||
fprintf(stdout, "%s\n", env[i]);
|
||||
}
|
||||
if (expect_environment) {
|
||||
expect_environment_match |= SDL_strcmp(env[i], expect_environment) == 0;
|
||||
}
|
||||
}
|
||||
SDL_free(env);
|
||||
}
|
||||
}
|
||||
|
||||
if (stdin_to_stdout || stdin_to_stderr) {
|
||||
#ifdef SDL_PLATFORM_WINDOWS
|
||||
{
|
||||
DWORD mode;
|
||||
HANDLE stdout_handle = GetStdHandle(STD_INPUT_HANDLE);
|
||||
GetConsoleMode(stdout_handle, &mode);
|
||||
SetConsoleMode(stdout_handle, mode & ~(ENABLE_LINE_INPUT));
|
||||
}
|
||||
#else
|
||||
fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL, 0) & ~(O_NONBLOCK));
|
||||
#endif
|
||||
|
||||
if (stdin_to_stdout || stdin_to_stderr || read_stdin) {
|
||||
for (;;) {
|
||||
int c;
|
||||
c = fgetc(stdin);
|
||||
if (c == EOF) {
|
||||
char buffer[4 * 4096];
|
||||
size_t result;
|
||||
|
||||
result = fread(buffer, 1, sizeof(buffer), stdin);
|
||||
if (result == 0) {
|
||||
if (errno == EAGAIN) {
|
||||
clearerr(stdin);
|
||||
SDL_Delay(10);
|
||||
SDL_Delay(20);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (stdin_to_stdout) {
|
||||
fputc(c, stdout);
|
||||
fwrite(buffer, 1, result, stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
if (stdin_to_stderr) {
|
||||
fputc(c, stderr);
|
||||
fwrite(buffer, 1, result, stderr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SDLTest_CommonDestroyState(state);
|
||||
|
||||
if (expect_environment && !expect_environment_match) {
|
||||
exit_code |= 0x1;
|
||||
}
|
||||
return exit_code;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,17 +10,7 @@
|
|||
|
||||
/*
|
||||
* FIXME: Additional tests:
|
||||
* - stdin to stdout
|
||||
* - stdin to stderr
|
||||
* - read env, using env set by parent process
|
||||
* - exit codes
|
||||
* - kill process
|
||||
* - waiting twice on process
|
||||
* - executing a non-existing program
|
||||
* - executing a process linking to a shared library not in the search paths
|
||||
* - piping processes
|
||||
* - forwarding SDL_IOFromFile stream to process
|
||||
* - forwarding process to SDL_IOFromFile stream
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
|
|
@ -33,45 +23,53 @@ static void SDLCALL setUpProcess(void **arg) {
|
|||
*arg = &parsed_args;
|
||||
}
|
||||
|
||||
static const char *options[] = { "/path/to/childprocess" EXE, NULL };
|
||||
static const char *options[] = {
|
||||
"/path/to/childprocess" EXE,
|
||||
NULL
|
||||
};
|
||||
|
||||
static SDL_Environment *DuplicateEnvironment(const char *key0, ...)
|
||||
{
|
||||
static char **CreateArguments(int ignore, ...) {
|
||||
va_list ap;
|
||||
const char *keyN;
|
||||
SDL_Environment *env = SDL_GetEnvironment();
|
||||
SDL_Environment *new_env = SDL_CreateEnvironment(false);
|
||||
size_t count = 1;
|
||||
size_t i;
|
||||
char **result;
|
||||
|
||||
if (key0) {
|
||||
char *sep = SDL_strchr(key0, '=');
|
||||
if (sep) {
|
||||
*sep = '\0';
|
||||
SDL_SetEnvironmentVariable(new_env, key0, sep + 1, true);
|
||||
*sep = '=';
|
||||
SDL_SetEnvironmentVariable(new_env, key0, sep, true);
|
||||
} else {
|
||||
SDL_SetEnvironmentVariable(new_env, key0, SDL_GetEnvironmentVariable(env, key0), true);
|
||||
}
|
||||
va_start(ap, key0);
|
||||
va_start(ap, ignore);
|
||||
for (;;) {
|
||||
keyN = va_arg(ap, const char *);
|
||||
if (keyN) {
|
||||
sep = SDL_strchr(keyN, '=');
|
||||
if (sep) {
|
||||
*sep = '\0';
|
||||
SDL_SetEnvironmentVariable(new_env, keyN, sep + 1, true);
|
||||
*sep = '=';
|
||||
} else {
|
||||
SDL_SetEnvironmentVariable(new_env, keyN, SDL_GetEnvironmentVariable(env, keyN), true);
|
||||
}
|
||||
} else {
|
||||
const char *keyN = va_arg(ap, const char *);
|
||||
if (!keyN) {
|
||||
break;
|
||||
}
|
||||
count += 1;
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
result = SDL_calloc(count, sizeof(char *));
|
||||
|
||||
i = 0;
|
||||
va_start(ap, ignore);
|
||||
for (;;) {
|
||||
const char *keyN = va_arg(ap, const char *);
|
||||
if (!keyN) {
|
||||
break;
|
||||
}
|
||||
result[i++] = SDL_strdup(keyN);
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return new_env;
|
||||
static void DestroyStringArray(char **list) {
|
||||
char **current;
|
||||
|
||||
if (!list) {
|
||||
return;
|
||||
}
|
||||
for (current = list; *current; current++) {
|
||||
SDL_free(*current);
|
||||
}
|
||||
SDL_free(list);
|
||||
}
|
||||
|
||||
static int SDLCALL process_testArguments(void *arg)
|
||||
|
|
@ -98,6 +96,7 @@ static int SDLCALL process_testArguments(void *arg)
|
|||
char *buffer;
|
||||
int exit_code;
|
||||
int i;
|
||||
size_t total_read = 0;
|
||||
|
||||
process = SDL_CreateProcess(process_args, true);
|
||||
SDLTest_AssertCheck(process != NULL, "SDL_CreateProcess()");
|
||||
|
|
@ -106,12 +105,13 @@ static int SDLCALL process_testArguments(void *arg)
|
|||
}
|
||||
|
||||
exit_code = 0xdeadbeef;
|
||||
buffer = (char *)SDL_ReadProcess(process, NULL, &exit_code);
|
||||
buffer = (char *)SDL_ReadProcess(process, &total_read, &exit_code);
|
||||
SDLTest_AssertCheck(buffer != NULL, "SDL_ReadProcess()");
|
||||
SDLTest_AssertCheck(exit_code == 0, "Exit code should be 0, is %d", exit_code);
|
||||
if (!buffer) {
|
||||
goto failed;
|
||||
}
|
||||
SDLTest_LogEscapedString("stdout of process: ", buffer, total_read);
|
||||
|
||||
for (i = 3; process_args[i]; i++) {
|
||||
char line[64];
|
||||
|
|
@ -128,30 +128,87 @@ failed:
|
|||
return TEST_ABORTED;
|
||||
}
|
||||
|
||||
static int SDLCALL process_testexitCode(void *arg)
|
||||
{
|
||||
TestProcessData *data = (TestProcessData *)arg;
|
||||
int i;
|
||||
int exit_codes[] = {
|
||||
0, 13, 31, 127, 255
|
||||
};
|
||||
|
||||
for (i = 0; i < SDL_arraysize(exit_codes); i++) {
|
||||
bool wait_result;
|
||||
SDL_Process *process = NULL;
|
||||
char **process_args = NULL;
|
||||
char number_buffer[8];
|
||||
int exit_code;
|
||||
|
||||
SDL_snprintf(number_buffer, sizeof(number_buffer), "%d", exit_codes[i]);
|
||||
|
||||
process_args = CreateArguments(0, data->childprocess_path, "--exit-code", number_buffer, NULL);
|
||||
|
||||
process = SDL_CreateProcess((const char * const *)process_args, false);
|
||||
SDLTest_AssertCheck(process != NULL, "SDL_CreateProcess()");
|
||||
if (!process) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
exit_code = 0xdeadbeef;
|
||||
SDLTest_AssertPass("About to wait on process (first time)");
|
||||
wait_result = SDL_WaitProcess(process, true, &exit_code);
|
||||
SDLTest_AssertCheck(wait_result == true, "SDL_WaitProcess(): Process should have closed immediately");
|
||||
SDLTest_AssertCheck(exit_code == exit_codes[i], "SDL_WaitProcess(): Exit code should be %d, is %d", exit_codes[i], exit_code);
|
||||
|
||||
exit_code = 0xdeadbeef;
|
||||
SDLTest_AssertPass("About to wait on process (second time)");
|
||||
wait_result = SDL_WaitProcess(process, true, &exit_code);
|
||||
SDLTest_AssertCheck(wait_result == true, "SDL_WaitProcess(): Process should have closed immediately");
|
||||
SDLTest_AssertCheck(exit_code == exit_codes[i], "SDL_WaitProcess(): Exit code should be %d, is %d", exit_codes[i], exit_code);
|
||||
|
||||
SDLTest_AssertPass("About to destroy process");
|
||||
SDL_DestroyProcess(process);
|
||||
DestroyStringArray(process_args);
|
||||
continue;
|
||||
failed:
|
||||
SDL_DestroyProcess(process);
|
||||
DestroyStringArray(process_args);
|
||||
return TEST_ABORTED;
|
||||
}
|
||||
return TEST_COMPLETED;
|
||||
#if 0
|
||||
failed:
|
||||
SDL_DestroyProcess(process);
|
||||
DestroyStringArray(process_args);
|
||||
return TEST_ABORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int SDLCALL process_testInheritedEnv(void *arg)
|
||||
{
|
||||
TestProcessData *data = (TestProcessData *)arg;
|
||||
const char *process_args[] = {
|
||||
data->childprocess_path,
|
||||
"--print-environment",
|
||||
"--expect-env", NULL,
|
||||
NULL,
|
||||
};
|
||||
SDL_PropertiesID props;
|
||||
SDL_Process *process = NULL;
|
||||
Sint64 pid;
|
||||
SDL_IOStream *process_stdout = NULL;
|
||||
char buffer[256];
|
||||
bool wait_result;
|
||||
int exit_code;
|
||||
static const char *const TEST_ENV_KEY = "testprocess_environment";
|
||||
char *test_env_val = NULL;
|
||||
char random_env1[64];
|
||||
char random_env2[64];
|
||||
static const char *const TEST_ENV_KEY1 = "testprocess_inherited_var";
|
||||
static const char *const TEST_ENV_KEY2 = "testprocess_other_var";
|
||||
char *test_env_val1 = NULL;
|
||||
char *test_env_val2 = NULL;
|
||||
char *buffer = NULL;
|
||||
|
||||
test_env_val = SDLTest_RandomAsciiStringOfSize(32);
|
||||
SDLTest_AssertPass("Setting parent environment variable %s=%s", TEST_ENV_KEY, test_env_val);
|
||||
SDL_SetEnvironmentVariable(SDL_GetEnvironment(), TEST_ENV_KEY, test_env_val, true);
|
||||
SDL_snprintf(buffer, sizeof(buffer), "%s=%s", TEST_ENV_KEY, test_env_val);
|
||||
process_args[3] = buffer;
|
||||
test_env_val1 = SDLTest_RandomAsciiStringOfSize(32);
|
||||
SDL_snprintf(random_env1, sizeof(random_env1), "%s=%s", TEST_ENV_KEY1, test_env_val1);
|
||||
SDLTest_AssertPass("Setting parent environment variable %s=%s", TEST_ENV_KEY1, test_env_val1);
|
||||
SDL_SetEnvironmentVariable(SDL_GetEnvironment(), TEST_ENV_KEY1, test_env_val1, true);
|
||||
|
||||
SDL_UnsetEnvironmentVariable(SDL_GetEnvironment(), TEST_ENV_KEY2);
|
||||
|
||||
props = SDL_CreateProperties();
|
||||
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, (void *)process_args);
|
||||
|
|
@ -163,44 +220,37 @@ static int SDLCALL process_testInheritedEnv(void *arg)
|
|||
goto failed;
|
||||
}
|
||||
|
||||
test_env_val2 = SDLTest_RandomAsciiStringOfSize(32);
|
||||
SDL_snprintf(random_env2, sizeof(random_env2), "%s=%s", TEST_ENV_KEY2, test_env_val2);
|
||||
SDLTest_AssertPass("Setting parent environment variable %s=%s", TEST_ENV_KEY2, test_env_val2);
|
||||
SDL_SetEnvironmentVariable(SDL_GetEnvironment(),TEST_ENV_KEY2, test_env_val2, true);
|
||||
SDLTest_AssertCheck(SDL_strcmp(test_env_val1, test_env_val2) != 0, "Sanity checking the 2 random environment variables are not identical");
|
||||
|
||||
props = SDL_GetProcessProperties(process);
|
||||
SDLTest_AssertCheck(props != 0, "SDL_GetProcessProperties()");
|
||||
|
||||
pid = SDL_GetNumberProperty(props, SDL_PROP_PROCESS_PID_NUMBER, 0);
|
||||
SDLTest_AssertCheck(pid != 0, "Checking process ID, expected non-zero, got %" SDL_PRIs64, pid);
|
||||
|
||||
process_stdout = SDL_GetProcessOutput(process);
|
||||
SDLTest_AssertCheck(process_stdout != NULL, "SDL_GetPointerProperty(SDL_PROP_PROCESS_STDOUT_POINTER) returns a valid IO stream");
|
||||
if (!process_stdout) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
size_t amount_read;
|
||||
|
||||
amount_read = SDL_ReadIO(process_stdout, buffer, sizeof(buffer) - 1);
|
||||
if (amount_read > 0) {
|
||||
buffer[amount_read] = '\0';
|
||||
SDLTest_Log("READ: %s", buffer);
|
||||
} else if (SDL_GetIOStatus(process_stdout) != SDL_IO_STATUS_NOT_READY) {
|
||||
break;
|
||||
}
|
||||
SDL_Delay(10);
|
||||
}
|
||||
|
||||
SDLTest_AssertPass("About to wait on process");
|
||||
exit_code = 0xdeadbeef;
|
||||
wait_result = SDL_WaitProcess(process, true, &exit_code);
|
||||
SDLTest_AssertCheck(wait_result == true, "Process should have closed when closing stdin");
|
||||
SDLTest_AssertPass("exit_code will be != 0 when environment variable was not set");
|
||||
buffer = (char *)SDL_ReadProcess(process, NULL, &exit_code);
|
||||
SDLTest_AssertCheck(buffer != NULL, "SDL_ReadProcess()");
|
||||
SDLTest_AssertCheck(exit_code == 0, "Exit code should be 0, is %d", exit_code);
|
||||
|
||||
SDLTest_AssertCheck(SDL_strstr(buffer, random_env1) != NULL, "Environment of child should contain \"%s\"", test_env_val1);
|
||||
SDLTest_AssertCheck(SDL_strstr(buffer, random_env2) == NULL, "Environment of child should not contain \"%s\"", test_env_val2);
|
||||
|
||||
SDLTest_AssertPass("About to destroy process");
|
||||
SDL_DestroyProcess(process);
|
||||
SDL_free(test_env_val);
|
||||
SDL_free(test_env_val1);
|
||||
SDL_free(test_env_val2);
|
||||
SDL_free(buffer);
|
||||
return TEST_COMPLETED;
|
||||
failed:
|
||||
SDL_free(test_env_val);
|
||||
SDL_free(test_env_val1);
|
||||
SDL_free(test_env_val2);
|
||||
SDL_DestroyProcess(process);
|
||||
SDL_free(buffer);
|
||||
return TEST_ABORTED;
|
||||
}
|
||||
|
||||
|
|
@ -210,25 +260,38 @@ static int SDLCALL process_testNewEnv(void *arg)
|
|||
const char *process_args[] = {
|
||||
data->childprocess_path,
|
||||
"--print-environment",
|
||||
"--expect-env", NULL,
|
||||
NULL,
|
||||
};
|
||||
SDL_Environment *process_env;
|
||||
SDL_PropertiesID props;
|
||||
SDL_Process *process = NULL;
|
||||
Sint64 pid;
|
||||
SDL_IOStream *process_stdout = NULL;
|
||||
char buffer[256];
|
||||
bool wait_result;
|
||||
int exit_code;
|
||||
static const char *const TEST_ENV_KEY = "testprocess_environment";
|
||||
char *test_env_val = NULL;
|
||||
char random_env1[64];
|
||||
char random_env2[64];
|
||||
static const char *const TEST_ENV_KEY1 = "testprocess_inherited_var";
|
||||
static const char *const TEST_ENV_KEY2 = "testprocess_other_var";
|
||||
char *test_env_val1 = NULL;
|
||||
char *test_env_val2 = NULL;
|
||||
char *buffer = NULL;
|
||||
size_t total_read = 0;
|
||||
|
||||
test_env_val = SDLTest_RandomAsciiStringOfSize(32);
|
||||
SDL_snprintf(buffer, sizeof(buffer), "%s=%s", TEST_ENV_KEY, test_env_val);
|
||||
process_args[3] = buffer;
|
||||
test_env_val1 = SDLTest_RandomAsciiStringOfSize(32);
|
||||
SDL_snprintf(random_env1, sizeof(random_env1), "%s=%s", TEST_ENV_KEY1, test_env_val1);
|
||||
SDLTest_AssertPass("Unsetting parent environment variable %s", TEST_ENV_KEY1);
|
||||
SDL_UnsetEnvironmentVariable(SDL_GetEnvironment(), TEST_ENV_KEY1);
|
||||
|
||||
process_env = DuplicateEnvironment("PATH", "LD_LIBRARY_PATH", "DYLD_LIBRARY_PATH", buffer, NULL);
|
||||
process_env = SDL_CreateEnvironment(true);
|
||||
SDL_SetEnvironmentVariable(process_env, "PATH", SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "PATH"), true);
|
||||
SDL_SetEnvironmentVariable(process_env, "LD_LIBRARY_PATH", SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "LD_LIBRARY_PATH"), true);
|
||||
SDL_SetEnvironmentVariable(process_env, "DYLD_LIBRARY_PATH", SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "DYLD_LIBRARY_PATH"), true);
|
||||
SDL_SetEnvironmentVariable(process_env, TEST_ENV_KEY1, test_env_val1, true);
|
||||
|
||||
test_env_val2 = SDLTest_RandomAsciiStringOfSize(32);
|
||||
SDL_snprintf(random_env2, sizeof(random_env2), "%s=%s", TEST_ENV_KEY2, test_env_val1);
|
||||
SDLTest_AssertPass("Setting parent environment variable %s=%s", TEST_ENV_KEY2, test_env_val2);
|
||||
SDL_SetEnvironmentVariable(SDL_GetEnvironment(), TEST_ENV_KEY2, test_env_val2, true);
|
||||
SDLTest_AssertCheck(SDL_strcmp(test_env_val1, test_env_val2) != 0, "Sanity checking the 2 random environment variables are not identical");
|
||||
|
||||
props = SDL_CreateProperties();
|
||||
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, (void *)process_args);
|
||||
|
|
@ -247,41 +310,79 @@ static int SDLCALL process_testNewEnv(void *arg)
|
|||
pid = SDL_GetNumberProperty(props, SDL_PROP_PROCESS_PID_NUMBER, 0);
|
||||
SDLTest_AssertCheck(pid != 0, "Checking process ID, expected non-zero, got %" SDL_PRIs64, pid);
|
||||
|
||||
process_stdout = SDL_GetProcessOutput(process);
|
||||
SDLTest_AssertCheck(process_stdout != NULL, "SDL_GetPointerProperty(SDL_PROP_PROCESS_STDOUT_POINTER) returns a valid IO stream");
|
||||
if (!process_stdout) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
size_t amount_read;
|
||||
|
||||
amount_read = SDL_ReadIO(process_stdout, buffer, sizeof(buffer) - 1);
|
||||
if (amount_read > 0) {
|
||||
buffer[amount_read] = '\0';
|
||||
SDLTest_Log("READ: %s", buffer);
|
||||
} else if (SDL_GetIOStatus(process_stdout) != SDL_IO_STATUS_NOT_READY) {
|
||||
break;
|
||||
}
|
||||
SDL_Delay(10);
|
||||
}
|
||||
|
||||
SDLTest_AssertPass("About to wait on process");
|
||||
exit_code = 0xdeadbeef;
|
||||
wait_result = SDL_WaitProcess(process, true, &exit_code);
|
||||
SDLTest_AssertCheck(wait_result == true, "Process should have closed when closing stdin");
|
||||
SDLTest_AssertPass("exit_code will be != 0 when environment variable was not set");
|
||||
buffer = (char *)SDL_ReadProcess(process, &total_read, &exit_code);
|
||||
SDLTest_AssertCheck(buffer != NULL, "SDL_ReadProcess()");
|
||||
SDLTest_AssertCheck(exit_code == 0, "Exit code should be 0, is %d", exit_code);
|
||||
SDLTest_LogEscapedString("Text read from subprocess: ", buffer, total_read);
|
||||
|
||||
SDLTest_AssertCheck(SDL_strstr(buffer, random_env1) != NULL, "Environment of child should contain \"%s\"", random_env1);
|
||||
SDLTest_AssertCheck(SDL_strstr(buffer, random_env2) == NULL, "Environment of child should not contain \"%s\"", random_env1);
|
||||
|
||||
SDLTest_AssertPass("About to destroy process");
|
||||
SDL_free(test_env_val);
|
||||
SDL_DestroyProcess(process);
|
||||
SDL_DestroyEnvironment(process_env);
|
||||
SDL_free(test_env_val1);
|
||||
SDL_free(test_env_val2);
|
||||
SDL_free(buffer);
|
||||
return TEST_COMPLETED;
|
||||
|
||||
failed:
|
||||
SDL_free(test_env_val);
|
||||
SDL_DestroyProcess(process);
|
||||
SDL_DestroyEnvironment(process_env);
|
||||
SDL_free(test_env_val1);
|
||||
SDL_free(test_env_val2);
|
||||
SDL_free(buffer);
|
||||
return TEST_ABORTED;
|
||||
}
|
||||
|
||||
static int SDLCALL process_testKill(void *arg)
|
||||
{
|
||||
TestProcessData *data = (TestProcessData *)arg;
|
||||
const char *process_args[] = {
|
||||
data->childprocess_path,
|
||||
"--stdin",
|
||||
NULL,
|
||||
};
|
||||
SDL_Process *process = NULL;
|
||||
SDL_PropertiesID props;
|
||||
Sint64 pid;
|
||||
int result;
|
||||
int exit_code;
|
||||
|
||||
SDLTest_AssertPass("About to call SDL_CreateProcess(true)");
|
||||
process = SDL_CreateProcess(process_args, true);
|
||||
if (!process) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
props = SDL_GetProcessProperties(process);
|
||||
SDLTest_AssertCheck(props != 0, "SDL_GetProcessProperties()");
|
||||
|
||||
pid = SDL_GetNumberProperty(props, SDL_PROP_PROCESS_PID_NUMBER, 0);
|
||||
SDLTest_AssertCheck(pid != 0, "Checking process ID, expected non-zero, got %" SDL_PRIs64, pid);
|
||||
|
||||
exit_code = 0xdeadbeef;
|
||||
SDLTest_AssertPass("About to call SDL_WaitProcess(false)");
|
||||
result = SDL_WaitProcess(process, false, &exit_code);
|
||||
SDLTest_AssertCheck(result == false, "Process should not have exited yet");
|
||||
|
||||
SDLTest_AssertPass("About to call SDL_KillProcess(false)");
|
||||
result = SDL_KillProcess(process, false);
|
||||
SDLTest_AssertCheck(result == true, "Process should have exited");
|
||||
|
||||
exit_code = 0;
|
||||
SDLTest_AssertPass("About to call SDL_WaitProcess(true)");
|
||||
result = SDL_WaitProcess(process, true, &exit_code);
|
||||
SDLTest_AssertCheck(result == true, "Process should have exited");
|
||||
SDLTest_AssertCheck(exit_code != 0, "Exit code should be non-zero, is %d", exit_code);
|
||||
|
||||
SDLTest_AssertPass("About to destroy process");
|
||||
SDL_DestroyProcess(process);
|
||||
return TEST_COMPLETED;
|
||||
|
||||
failed:
|
||||
SDL_DestroyProcess(process);
|
||||
return TEST_ABORTED;
|
||||
}
|
||||
|
||||
|
|
@ -298,13 +399,31 @@ static int process_testStdinToStdout(void *arg)
|
|||
Sint64 pid;
|
||||
SDL_IOStream *process_stdin = NULL;
|
||||
SDL_IOStream *process_stdout = NULL;
|
||||
const char *text_in = "Tests whether we can write to stdin and read from stdout\r\n{'succes': true, 'message': 'Success!'}\r\nYippie ka yee\r\nEOF";
|
||||
size_t amount_written;
|
||||
size_t amount_to_write;
|
||||
char buffer[128];
|
||||
SDL_IOStream *process_stderr = NULL;
|
||||
size_t text_in_size = 1 * 1024 * 1024;
|
||||
char *text_in = NULL;
|
||||
size_t total_written;
|
||||
size_t total_read;
|
||||
bool wait_result;
|
||||
int exit_code;
|
||||
SDL_IOStream *stdout_stream = NULL;
|
||||
char *stdout_stream_buf;
|
||||
int iteration_count = 0;
|
||||
|
||||
text_in = SDLTest_RandomAsciiStringOfSize((int)text_in_size);
|
||||
/* Make sure text_in does not contain EOF */
|
||||
for (;;) {
|
||||
char *e = SDL_strstr(text_in, "EOF");
|
||||
if (!e) {
|
||||
break;
|
||||
}
|
||||
e[0] = 'N';
|
||||
}
|
||||
text_in[text_in_size - 3] = 'E';
|
||||
text_in[text_in_size - 2] = 'O';
|
||||
text_in[text_in_size - 1] = 'F';
|
||||
|
||||
stdout_stream = SDL_IOFromDynamicMem();
|
||||
|
||||
props = SDL_CreateProperties();
|
||||
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, (void *)process_args);
|
||||
|
|
@ -327,41 +446,73 @@ static int process_testStdinToStdout(void *arg)
|
|||
SDLTest_AssertCheck(process_stdin != NULL, "SDL_GetPointerProperty(SDL_PROP_PROCESS_STDIN_POINTER) returns a valid IO stream");
|
||||
process_stdout = SDL_GetProcessOutput(process);
|
||||
SDLTest_AssertCheck(process_stdout != NULL, "SDL_GetPointerProperty(SDL_PROP_PROCESS_STDOUT_POINTER) returns a valid IO stream");
|
||||
process_stderr = (SDL_IOStream *)SDL_GetPointerProperty(props, SDL_PROP_PROCESS_STDERR_POINTER, NULL);
|
||||
SDLTest_AssertCheck(process_stderr == NULL, "SDL_GetPointerProperty(SDL_PROP_PROCESS_STDERR_POINTER) returns NULL");
|
||||
if (!process_stdin || !process_stdout) {
|
||||
goto failed;
|
||||
}
|
||||
SDLTest_AssertPass("About to write to process");
|
||||
amount_to_write = SDL_strlen(text_in);
|
||||
amount_written = SDL_WriteIO(process_stdin, text_in, amount_to_write);
|
||||
SDLTest_AssertCheck(amount_written == amount_to_write, "SDL_WriteIO(subprocess.stdin) wrote %" SDL_PRIu64 " bytes, expected %" SDL_PRIu64, (Uint64)amount_written, (Uint64)amount_to_write);
|
||||
if (amount_to_write != amount_written) {
|
||||
goto failed;
|
||||
}
|
||||
SDL_FlushIO(process_stdin);
|
||||
|
||||
total_written = 0;
|
||||
total_read = 0;
|
||||
buffer[0] = '\0';
|
||||
for (;;) {
|
||||
int log_this_iteration = (iteration_count % 32) == 32;
|
||||
char local_buffer[16 * 4094];
|
||||
size_t amount_read;
|
||||
if (total_read >= sizeof(buffer) - 1) {
|
||||
SDLTest_AssertCheck(0, "Buffer is too small for input data.");
|
||||
goto failed;
|
||||
SDL_IOStatus io_status;
|
||||
if (total_written != text_in_size) {
|
||||
size_t amount_written;
|
||||
if (log_this_iteration) {
|
||||
SDLTest_AssertPass("About to SDL_WriteIO (%dth time)", iteration_count);
|
||||
}
|
||||
amount_written = SDL_WriteIO(process_stdin, text_in + total_written, text_in_size - total_written);
|
||||
if (log_this_iteration) {
|
||||
SDLTest_Log("SDL_WriteIO() -> %u (%dth time)", (unsigned)amount_written, iteration_count);
|
||||
}
|
||||
if (amount_written == 0) {
|
||||
io_status = SDL_GetIOStatus(process_stdin);
|
||||
if (io_status != SDL_IO_STATUS_NOT_READY) {
|
||||
SDLTest_Log("SDL_GetIOStatus(process_stdin) returns %d, breaking.", io_status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
total_written += amount_written;
|
||||
SDL_FlushIO(process_stdin);
|
||||
}
|
||||
|
||||
SDLTest_AssertPass("About to read from process");
|
||||
amount_read = SDL_ReadIO(process_stdout, buffer + total_read, sizeof(buffer) - total_read - 1);
|
||||
if (amount_read == 0 && SDL_GetIOStatus(process_stdout) != SDL_IO_STATUS_NOT_READY) {
|
||||
/* FIXME: this needs a rate limit */
|
||||
if (log_this_iteration) {
|
||||
SDLTest_AssertPass("About to SDL_ReadIO (%dth time)", iteration_count);
|
||||
}
|
||||
amount_read = SDL_ReadIO(process_stdout, local_buffer, sizeof(local_buffer));
|
||||
if (log_this_iteration) {
|
||||
SDLTest_Log("SDL_ReadIO() -> %u (%dth time)", (unsigned)amount_read, iteration_count);
|
||||
}
|
||||
if (amount_read == 0) {
|
||||
io_status = SDL_GetIOStatus(process_stdout);
|
||||
if (io_status != SDL_IO_STATUS_NOT_READY) {
|
||||
SDLTest_Log("SDL_GetIOStatus(process_stdout) returned %d, breaking.", io_status);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
total_read += amount_read;
|
||||
buffer[total_read] = '\0';
|
||||
if (total_read >= sizeof(buffer) - 1 || SDL_strstr(buffer, "EOF")) {
|
||||
SDL_WriteIO(stdout_stream, local_buffer, amount_read);
|
||||
stdout_stream_buf = SDL_GetPointerProperty(SDL_GetIOProperties(stdout_stream), SDL_PROP_IOSTREAM_DYNAMIC_MEMORY_POINTER, NULL);
|
||||
if (SDL_strstr(stdout_stream_buf, "EOF")) {
|
||||
SDLTest_Log("Found EOF in stdout");
|
||||
break;
|
||||
}
|
||||
}
|
||||
SDL_Delay(10);
|
||||
}
|
||||
SDLTest_Log("Text read from subprocess: %s", buffer);
|
||||
SDLTest_AssertCheck(SDL_strcmp(buffer, text_in) == 0, "Subprocess stdout should match text written to stdin");
|
||||
SDLTest_Log("Wrote %" SDL_PRIu64 " bytes to process.stdin", (Uint64)total_written);
|
||||
SDLTest_Log("Read %" SDL_PRIu64 " bytes from process.stdout",(Uint64)total_read);
|
||||
|
||||
stdout_stream_buf = SDL_GetPointerProperty(SDL_GetIOProperties(stdout_stream), SDL_PROP_IOSTREAM_DYNAMIC_MEMORY_POINTER, NULL);
|
||||
SDLTest_CompareMemory(stdout_stream_buf, total_written, text_in, text_in_size);
|
||||
|
||||
exit_code = 0xdeadbeef;
|
||||
wait_result = SDL_WaitProcess(process, false, &exit_code);
|
||||
SDLTest_AssertCheck(wait_result == false, "Process should not have closed yet");
|
||||
|
||||
SDLTest_AssertPass("About to close stdin");
|
||||
/* Closing stdin of `subprocessstdin --stdin-to-stdout` should close the process */
|
||||
|
|
@ -383,9 +534,74 @@ static int process_testStdinToStdout(void *arg)
|
|||
}
|
||||
SDLTest_AssertPass("About to destroy process");
|
||||
SDL_DestroyProcess(process);
|
||||
SDL_CloseIO(stdout_stream);
|
||||
SDL_free(text_in);
|
||||
return TEST_COMPLETED;
|
||||
failed:
|
||||
|
||||
SDL_DestroyProcess(process);
|
||||
SDL_CloseIO(stdout_stream);
|
||||
SDL_free(text_in);
|
||||
return TEST_ABORTED;
|
||||
}
|
||||
|
||||
static int process_testStdinToStderr(void *arg)
|
||||
{
|
||||
TestProcessData *data = (TestProcessData *)arg;
|
||||
const char *process_args[] = {
|
||||
data->childprocess_path,
|
||||
"--stdin-to-stderr",
|
||||
NULL,
|
||||
};
|
||||
SDL_Process *process = NULL;
|
||||
SDL_IOStream *process_stdin = NULL;
|
||||
SDL_IOStream *process_stdout = NULL;
|
||||
SDL_IOStream *process_stderr = NULL;
|
||||
const char *text_in = "Tests whether we can write to stdin and read from stderr\r\n{'succes': true, 'message': 'Success!'}\r\nYippie ka yee\r\nEOF";
|
||||
size_t result;
|
||||
int exit_code;
|
||||
SDL_PropertiesID props;
|
||||
char buffer[256];
|
||||
size_t amount_read;
|
||||
|
||||
props = SDL_CreateProperties();
|
||||
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, (void *)process_args);
|
||||
SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDIN_NUMBER, SDL_PROCESS_STDIO_APP);
|
||||
SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_NULL);
|
||||
SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDERR_NUMBER, SDL_PROCESS_STDIO_APP);
|
||||
process = SDL_CreateProcessWithProperties(props);
|
||||
SDL_DestroyProperties(props);
|
||||
SDLTest_AssertCheck(process != NULL, "SDL_CreateProcessWithProperties()");
|
||||
if (!process) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
SDLTest_AssertPass("About to write to process");
|
||||
process_stdin = SDL_GetProcessInput(process);
|
||||
SDLTest_AssertCheck(process_stdin != NULL, "SDL_GetProcessInput()");
|
||||
result = SDL_WriteIO(process_stdin, text_in, SDL_strlen(text_in));
|
||||
SDLTest_AssertCheck(result == SDL_strlen(text_in), "SDL_WriteIO() wrote %d, expected %d", (int)result, (int)SDL_strlen(text_in));
|
||||
SDL_CloseIO(process_stdin);
|
||||
|
||||
process_stdout = SDL_GetProcessOutput(process);
|
||||
SDLTest_AssertCheck(process_stdout == NULL, "Process has no stdout");
|
||||
|
||||
process_stderr = SDL_GetPointerProperty(SDL_GetProcessProperties(process), SDL_PROP_PROCESS_STDERR_POINTER, NULL);
|
||||
SDLTest_AssertCheck(process_stderr != NULL, "Process has stderr");
|
||||
|
||||
exit_code = 0xdeadbeef;
|
||||
result = SDL_WaitProcess(process, true, &exit_code);
|
||||
SDLTest_AssertCheck(result == true, "Process should have finished");
|
||||
SDLTest_AssertCheck(exit_code == 0, "Exit code should be 0, is %d", exit_code);
|
||||
|
||||
amount_read = SDL_ReadIO(process_stderr, buffer, sizeof(buffer));
|
||||
SDLTest_CompareMemory(buffer, amount_read, text_in, SDL_strlen(text_in));
|
||||
|
||||
SDLTest_AssertPass("About to destroy process");
|
||||
SDL_DestroyProcess(process);
|
||||
return TEST_COMPLETED;
|
||||
|
||||
failed:
|
||||
SDL_DestroyProcess(process);
|
||||
return TEST_ABORTED;
|
||||
}
|
||||
|
|
@ -404,6 +620,7 @@ static int process_testSimpleStdinToStdout(void *arg)
|
|||
char *buffer;
|
||||
size_t result;
|
||||
int exit_code;
|
||||
size_t total_read = 0;
|
||||
|
||||
process = SDL_CreateProcess(process_args, true);
|
||||
SDLTest_AssertCheck(process != NULL, "SDL_CreateProcess()");
|
||||
|
|
@ -422,14 +639,16 @@ static int process_testSimpleStdinToStdout(void *arg)
|
|||
SDLTest_AssertCheck(input == NULL, "SDL_GetProcessInput() after close");
|
||||
|
||||
exit_code = 0xdeadbeef;
|
||||
buffer = (char *)SDL_ReadProcess(process, NULL, &exit_code);
|
||||
buffer = (char *)SDL_ReadProcess(process, &total_read, &exit_code);
|
||||
SDLTest_AssertCheck(buffer != NULL, "SDL_ReadProcess()");
|
||||
SDLTest_AssertCheck(exit_code == 0, "Exit code should be 0, is %d", exit_code);
|
||||
if (!buffer) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
SDLTest_Log("Text read from subprocess: %s", buffer);
|
||||
SDLTest_LogEscapedString("Expected text read from subprocess: %s", text_in, SDL_strlen(text_in));
|
||||
SDLTest_LogEscapedString("Actual text read from subprocess: %s", buffer, total_read);
|
||||
SDLTest_AssertCheck(total_read == SDL_strlen(text_in), "Expected to read %u bytes, actually read %u bytes", (unsigned)SDL_strlen(text_in), (unsigned)total_read);
|
||||
SDLTest_AssertCheck(SDL_strcmp(buffer, text_in) == 0, "Subprocess stdout should match text written to stdin");
|
||||
SDL_free(buffer);
|
||||
|
||||
|
|
@ -458,6 +677,8 @@ static int process_testMultiprocessStdinToStdout(void *arg)
|
|||
char *buffer;
|
||||
size_t result;
|
||||
int exit_code;
|
||||
size_t total_read = 0;
|
||||
bool finished;
|
||||
|
||||
process1 = SDL_CreateProcess(process_args, true);
|
||||
SDLTest_AssertCheck(process1 != NULL, "SDL_CreateProcess()");
|
||||
|
|
@ -470,6 +691,7 @@ static int process_testMultiprocessStdinToStdout(void *arg)
|
|||
SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDIN_NUMBER, SDL_PROCESS_STDIO_REDIRECT);
|
||||
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_STDIN_POINTER, SDL_GetPointerProperty(SDL_GetProcessProperties(process1), SDL_PROP_PROCESS_STDOUT_POINTER, NULL));
|
||||
SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_APP);
|
||||
SDLTest_AssertPass("About to call SDL_CreateProcessWithProperties");
|
||||
process2 = SDL_CreateProcessWithProperties(props);
|
||||
SDL_DestroyProperties(props);
|
||||
SDLTest_AssertCheck(process2 != NULL, "SDL_CreateProcess()");
|
||||
|
|
@ -485,14 +707,21 @@ static int process_testMultiprocessStdinToStdout(void *arg)
|
|||
SDL_CloseIO(input);
|
||||
|
||||
exit_code = 0xdeadbeef;
|
||||
buffer = (char *)SDL_ReadProcess(process2, NULL, &exit_code);
|
||||
finished = SDL_WaitProcess(process1, true, &exit_code);
|
||||
SDLTest_AssertCheck(finished == true, "process 1 should have finished");
|
||||
SDLTest_AssertCheck(exit_code == 0, "Exit code of process 1 should be 0, is %d", exit_code);
|
||||
|
||||
exit_code = 0xdeadbeef;
|
||||
buffer = (char *)SDL_ReadProcess(process2, &total_read, &exit_code);
|
||||
SDLTest_AssertCheck(buffer != NULL, "SDL_ReadProcess()");
|
||||
SDLTest_AssertCheck(exit_code == 0, "Exit code should be 0, is %d", exit_code);
|
||||
SDLTest_AssertCheck(exit_code == 0, "Exit code of process 2 should be 0, is %d", exit_code);
|
||||
if (!buffer) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
SDLTest_Log("Text read from subprocess: %s", buffer);
|
||||
SDLTest_LogEscapedString("Expected text read from subprocess: ", text_in, SDL_strlen(text_in));
|
||||
SDLTest_LogEscapedString("Actual text read from subprocess: ", buffer, total_read);
|
||||
SDLTest_AssertCheck(total_read == SDL_strlen(text_in), "Expected to read %u bytes, actually read %u bytes", (unsigned)SDL_strlen(text_in), (unsigned)total_read);
|
||||
SDLTest_AssertCheck(SDL_strcmp(buffer, text_in) == 0, "Subprocess stdout should match text written to stdin");
|
||||
SDL_free(buffer);
|
||||
SDLTest_AssertPass("About to destroy processes");
|
||||
|
|
@ -506,11 +735,241 @@ failed:
|
|||
return TEST_ABORTED;
|
||||
}
|
||||
|
||||
static int process_testWriteToFinishedProcess(void *arg)
|
||||
{
|
||||
TestProcessData *data = (TestProcessData *)arg;
|
||||
const char *process_args[] = {
|
||||
data->childprocess_path,
|
||||
NULL,
|
||||
};
|
||||
SDL_Process *process = NULL;
|
||||
bool result;
|
||||
int exit_code;
|
||||
SDL_IOStream *process_stdin;
|
||||
const char *text_in = "text_in";
|
||||
|
||||
SDLTest_AssertPass("About to call SDL_CreateProcess");
|
||||
process = SDL_CreateProcess(process_args, true);
|
||||
SDLTest_AssertCheck(process != NULL, "SDL_CreateProcess()");
|
||||
if (!process) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
exit_code = 0xdeadbeef;
|
||||
SDLTest_AssertPass("About to call SDL_WaitProcess");
|
||||
result = SDL_WaitProcess(process, true, &exit_code);
|
||||
SDLTest_AssertCheck(result, "SDL_WaitProcess()");
|
||||
SDLTest_AssertCheck(exit_code == 0, "Exit code should be 0, is %d", exit_code);
|
||||
|
||||
process_stdin = SDL_GetProcessInput(process);
|
||||
SDLTest_AssertCheck(process_stdin != NULL, "SDL_GetProcessInput returns non-Null SDL_IOStream");
|
||||
SDLTest_AssertPass("About to call SDL_WriteIO on dead child process");
|
||||
SDL_WriteIO(process_stdin, text_in, SDL_strlen(text_in));
|
||||
|
||||
SDLTest_AssertPass("About to destroy process");
|
||||
SDL_DestroyProcess(process);
|
||||
return TEST_COMPLETED;
|
||||
|
||||
failed:
|
||||
SDL_DestroyProcess(process);
|
||||
return TEST_ABORTED;
|
||||
}
|
||||
|
||||
static int process_testNonExistingExecutable(void *arg)
|
||||
{
|
||||
static const int STEM_LENGTH = 16;
|
||||
char **process_args;
|
||||
char *random_stem;
|
||||
char *random_path;
|
||||
SDL_Process *process = NULL;
|
||||
|
||||
random_stem = SDLTest_RandomAsciiStringOfSize(STEM_LENGTH);
|
||||
random_path = SDL_malloc(STEM_LENGTH + SDL_strlen(EXE) + 1);
|
||||
SDL_snprintf(random_path, STEM_LENGTH + SDL_strlen(EXE) + 1, "%s%s", random_stem, EXE);
|
||||
SDL_free(random_stem);
|
||||
SDLTest_AssertCheck(!SDL_GetPathInfo(random_path, NULL), "%s does not exist", random_path);
|
||||
|
||||
process_args = CreateArguments(0, random_path, NULL);
|
||||
SDL_free(random_path);
|
||||
|
||||
SDLTest_AssertPass("About to call SDL_CreateProcess");
|
||||
process = SDL_CreateProcess((const char * const *)process_args, false);
|
||||
SDLTest_AssertCheck(process == NULL, "SDL_CreateProcess() should have failed (%s)", SDL_GetError());
|
||||
|
||||
DestroyStringArray(process_args);
|
||||
return TEST_COMPLETED;
|
||||
}
|
||||
|
||||
static int process_testBatBadButVulnerability(void *arg)
|
||||
{
|
||||
TestProcessData *data = (TestProcessData *)arg;
|
||||
char *inject_arg = NULL;
|
||||
char **process_args = NULL;
|
||||
char *text_out = NULL;
|
||||
size_t len_text_out;
|
||||
int exitcode;
|
||||
SDL_Process *process = NULL;
|
||||
SDL_IOStream *child_bat;
|
||||
char buffer[256];
|
||||
|
||||
#ifndef SDL_PLATFORM_WINDOWS
|
||||
SDLTest_AssertPass("The BatBadBut vulnerability only applied to Windows");
|
||||
return TEST_SKIPPED;
|
||||
#endif
|
||||
/* FIXME: remove child.bat at end of loop and/or create in temporary directory */
|
||||
child_bat = SDL_IOFromFile("child_batbadbut.bat", "w");
|
||||
SDL_IOprintf(child_bat, "@echo off\necho Hello from child_batbadbut.bat\necho \"|bat1=%%1|\"\n");
|
||||
SDL_CloseIO(child_bat);
|
||||
|
||||
inject_arg = SDL_malloc(SDL_strlen(data->childprocess_path) + 100);
|
||||
SDL_snprintf(inject_arg, SDL_strlen(data->childprocess_path) + 100, "\"&%s --version --print-arguments --stdout OWNEDSTDOUT\"", data->childprocess_path);
|
||||
process_args = CreateArguments(0, "child_batbadbut.bat", inject_arg, NULL);
|
||||
|
||||
SDLTest_AssertPass("About to call SDL_CreateProcess");
|
||||
process = SDL_CreateProcess((const char * const*)process_args, true);
|
||||
SDLTest_AssertCheck(process != NULL, "SDL_CreateProcess");
|
||||
if (!process) {
|
||||
goto cleanup;
|
||||
}
|
||||
text_out = SDL_ReadProcess(process, &len_text_out, &exitcode);
|
||||
SDLTest_AssertCheck(exitcode == 0, "process exited with exitcode 0, was %d", exitcode);
|
||||
SDLTest_AssertCheck(text_out != NULL, "SDL_ReadProcess returned data");
|
||||
SDLTest_LogEscapedString("Output: ", text_out, len_text_out);
|
||||
if (!text_out) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
SDLTest_AssertCheck(SDL_strstr(text_out, "Hello from child_batbadbut") != NULL, "stdout contains 'Hello from child'");
|
||||
SDLTest_AssertCheck(SDL_strstr(text_out, "SDL version") == NULL, "stdout should not contain SDL version");
|
||||
SDL_snprintf(buffer, sizeof(buffer), "|bat1=\"\"\"&%s\"\"|", process_args[1] + 2);
|
||||
SDLTest_LogEscapedString("stdout should contain: ", buffer, SDL_strlen(buffer));
|
||||
SDLTest_AssertCheck(SDL_strstr(text_out, buffer) != NULL, "Verify first argument");
|
||||
|
||||
cleanup:
|
||||
SDL_free(text_out);
|
||||
SDL_DestroyProcess(process);
|
||||
SDL_free(inject_arg);
|
||||
DestroyStringArray(process_args);
|
||||
return TEST_COMPLETED;
|
||||
}
|
||||
|
||||
static int process_testFileRedirection(void *arg)
|
||||
{
|
||||
TestProcessData *data = (TestProcessData *)arg;
|
||||
SDL_PropertiesID props = 0;
|
||||
const char * process_args[] = {
|
||||
data->childprocess_path,
|
||||
"--stdin-to-stdout",
|
||||
"--stdin-to-stderr",
|
||||
NULL,
|
||||
};
|
||||
const char TEXT_REF[] = "This is input for the child process";
|
||||
static const char *PATH_STDIN = "test_redirection_stdin.txt";
|
||||
static const char *PATH_STDOUT = "test_redirection_stdout.txt";
|
||||
static const char *PATH_STDERR = "test_redirection_stderr.txt";
|
||||
char *text_out = NULL;
|
||||
size_t len_text_out;
|
||||
int exitcode;
|
||||
bool result;
|
||||
SDL_Process *process = NULL;
|
||||
SDL_IOStream *stream;
|
||||
SDL_IOStream *input_stream = NULL;
|
||||
SDL_IOStream *output_stream = NULL;
|
||||
SDL_IOStream *error_stream = NULL;
|
||||
|
||||
stream = SDL_IOFromFile(PATH_STDIN, "w");
|
||||
SDLTest_AssertCheck(stream != NULL, "SDL_IOFromFile(\"%s\", \"w\")", PATH_STDIN);
|
||||
if (!stream) {
|
||||
goto cleanup;
|
||||
}
|
||||
SDL_WriteIO(stream, TEXT_REF, sizeof(TEXT_REF));
|
||||
SDL_CloseIO(stream);
|
||||
|
||||
input_stream = SDL_IOFromFile(PATH_STDIN, "r");
|
||||
SDLTest_AssertCheck(input_stream != NULL, "SDL_IOFromFile(\"%s\", \"r\")", PATH_STDIN);
|
||||
if (!input_stream) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
output_stream = SDL_IOFromFile(PATH_STDOUT, "w");
|
||||
SDLTest_AssertCheck(output_stream != NULL, "SDL_IOFromFile(\"%s\", \"w\")", PATH_STDOUT);
|
||||
if (!output_stream) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
error_stream = SDL_IOFromFile(PATH_STDERR, "w");
|
||||
SDLTest_AssertCheck(error_stream != NULL, "SDL_IOFromFile(\"%s\", \"w\")", PATH_STDERR);
|
||||
if (!error_stream) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
props = SDL_CreateProperties();
|
||||
SDLTest_AssertCheck(props != 0, "SDL_CreateProperties()");
|
||||
if (!props) {
|
||||
goto cleanup;
|
||||
}
|
||||
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, (void *)process_args);
|
||||
SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDIN_NUMBER, SDL_PROCESS_STDIO_REDIRECT);
|
||||
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_STDIN_POINTER, (void *)input_stream);
|
||||
SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_REDIRECT);
|
||||
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_POINTER, (void *)output_stream);
|
||||
SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDERR_NUMBER, SDL_PROCESS_STDIO_REDIRECT);
|
||||
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_STDERR_POINTER, (void *)error_stream);
|
||||
process = SDL_CreateProcessWithProperties(props);
|
||||
SDL_DestroyProperties(props);
|
||||
SDLTest_AssertCheck(process != NULL, "SDL_CreateProcessWithProperties (%s)", SDL_GetError());
|
||||
if (!process) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
exitcode = 0xdeadbeef;
|
||||
text_out = SDL_ReadProcess(process, &len_text_out, &exitcode);
|
||||
SDLTest_AssertCheck(text_out == NULL, "SDL_ReadProcess should not be able to close a redirected process (%s)", SDL_GetError());
|
||||
SDLTest_AssertCheck(len_text_out == 0, "length written by SDL_ReadProcess should be 0");
|
||||
SDL_free(text_out);
|
||||
text_out = NULL;
|
||||
|
||||
exitcode = 0xdeadbeef;
|
||||
result = SDL_WaitProcess(process, true, &exitcode);
|
||||
SDLTest_AssertCheck(result, "process must have exited");
|
||||
SDLTest_AssertCheck(exitcode == 0, "process exited with exitcode 0, was %d", exitcode);
|
||||
|
||||
SDL_CloseIO(input_stream);
|
||||
input_stream = NULL;
|
||||
SDL_CloseIO(output_stream);
|
||||
output_stream = NULL;
|
||||
SDL_CloseIO(error_stream);
|
||||
error_stream = NULL;
|
||||
|
||||
text_out = SDL_LoadFile(PATH_STDOUT, &len_text_out);
|
||||
SDLTest_AssertCheck(text_out != NULL, "SDL_LoadFile(\"%s\") succeeded (%s)", PATH_STDOUT, SDL_GetError());
|
||||
SDLTest_AssertPass("Comparing stdout with reference");
|
||||
SDLTest_CompareMemory(text_out, len_text_out, TEXT_REF, sizeof(TEXT_REF));
|
||||
SDL_free(text_out);
|
||||
|
||||
text_out = SDL_LoadFile(PATH_STDERR, &len_text_out);
|
||||
SDLTest_AssertCheck(text_out != NULL, "SDL_LoadFile(\"%s\") succeeded (%s)", PATH_STDERR, SDL_GetError());
|
||||
SDLTest_AssertPass("Comparing stderr with reference");
|
||||
SDLTest_CompareMemory(text_out, len_text_out, TEXT_REF, sizeof(TEXT_REF));
|
||||
SDL_free(text_out);
|
||||
|
||||
cleanup:
|
||||
SDL_CloseIO(input_stream);
|
||||
SDL_CloseIO(output_stream);
|
||||
SDL_CloseIO(error_stream);
|
||||
SDL_DestroyProcess(process);
|
||||
return TEST_COMPLETED;
|
||||
}
|
||||
|
||||
static const SDLTest_TestCaseReference processTestArguments = {
|
||||
process_testArguments, "process_testArguments", "Test passing arguments to child process", TEST_ENABLED
|
||||
};
|
||||
|
||||
static const SDLTest_TestCaseReference processTestIneritedEnv = {
|
||||
static const SDLTest_TestCaseReference processTestExitCode = {
|
||||
process_testexitCode, "process_testExitCode", "Test exit codes", TEST_ENABLED
|
||||
};
|
||||
|
||||
static const SDLTest_TestCaseReference processTestInheritedEnv = {
|
||||
process_testInheritedEnv, "process_testInheritedEnv", "Test inheriting environment from parent process", TEST_ENABLED
|
||||
};
|
||||
|
||||
|
|
@ -518,10 +977,18 @@ static const SDLTest_TestCaseReference processTestNewEnv = {
|
|||
process_testNewEnv, "process_testNewEnv", "Test creating new environment for child process", TEST_ENABLED
|
||||
};
|
||||
|
||||
static const SDLTest_TestCaseReference processTestKill = {
|
||||
process_testKill, "process_testKill", "Test Killing a child process", TEST_ENABLED
|
||||
};
|
||||
|
||||
static const SDLTest_TestCaseReference processTestStdinToStdout = {
|
||||
process_testStdinToStdout, "process_testStdinToStdout", "Test writing to stdin and reading from stdout", TEST_ENABLED
|
||||
};
|
||||
|
||||
static const SDLTest_TestCaseReference processTestStdinToStderr = {
|
||||
process_testStdinToStderr, "process_testStdinToStderr", "Test writing to stdin and reading from stderr", TEST_ENABLED
|
||||
};
|
||||
|
||||
static const SDLTest_TestCaseReference processTestSimpleStdinToStdout = {
|
||||
process_testSimpleStdinToStdout, "process_testSimpleStdinToStdout", "Test writing to stdin and reading from stdout using the simplified API", TEST_ENABLED
|
||||
};
|
||||
|
|
@ -530,13 +997,36 @@ static const SDLTest_TestCaseReference processTestMultiprocessStdinToStdout = {
|
|||
process_testMultiprocessStdinToStdout, "process_testMultiprocessStdinToStdout", "Test writing to stdin and reading from stdout using the simplified API", TEST_ENABLED
|
||||
};
|
||||
|
||||
static const SDLTest_TestCaseReference processTestWriteToFinishedProcess = {
|
||||
process_testWriteToFinishedProcess, "process_testWriteToFinishedProcess", "Test writing to stdin of terminated process", TEST_ENABLED
|
||||
};
|
||||
|
||||
static const SDLTest_TestCaseReference processTestNonExistingExecutable = {
|
||||
process_testNonExistingExecutable, "process_testNonExistingExecutable", "Test running a non-existing executable", TEST_ENABLED
|
||||
};
|
||||
|
||||
static const SDLTest_TestCaseReference processTestBatBadButVulnerability = {
|
||||
process_testBatBadButVulnerability, "process_testBatBadButVulnerability", "Test BatBadBut vulnerability: command injection through cmd.exe", TEST_ENABLED
|
||||
};
|
||||
|
||||
static const SDLTest_TestCaseReference processTestFileRedirection = {
|
||||
process_testFileRedirection, "process_testFileRedirection", "Test redirection from/to files", TEST_DISABLED
|
||||
};
|
||||
|
||||
static const SDLTest_TestCaseReference *processTests[] = {
|
||||
&processTestArguments,
|
||||
&processTestIneritedEnv,
|
||||
&processTestExitCode,
|
||||
&processTestInheritedEnv,
|
||||
&processTestNewEnv,
|
||||
&processTestKill,
|
||||
&processTestStdinToStdout,
|
||||
&processTestStdinToStderr,
|
||||
&processTestSimpleStdinToStdout,
|
||||
&processTestMultiprocessStdinToStdout,
|
||||
&processTestWriteToFinishedProcess,
|
||||
&processTestNonExistingExecutable,
|
||||
&processTestBatBadButVulnerability,
|
||||
&processTestFileRedirection,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue