diff options
Diffstat (limited to 'src/contrib/SDL-3.2.20/test/testprocess.c')
-rw-r--r-- | src/contrib/SDL-3.2.20/test/testprocess.c | 1094 |
1 files changed, 1094 insertions, 0 deletions
diff --git a/src/contrib/SDL-3.2.20/test/testprocess.c b/src/contrib/SDL-3.2.20/test/testprocess.c new file mode 100644 index 0000000..425b445 --- /dev/null +++ b/src/contrib/SDL-3.2.20/test/testprocess.c | |||
@@ -0,0 +1,1094 @@ | |||
1 | #include <SDL3/SDL.h> | ||
2 | #include <SDL3/SDL_main.h> | ||
3 | #include <SDL3/SDL_test.h> | ||
4 | |||
5 | #ifdef SDL_PLATFORM_WINDOWS | ||
6 | #define EXE ".exe" | ||
7 | #else | ||
8 | #define EXE "" | ||
9 | #endif | ||
10 | |||
11 | /* | ||
12 | * FIXME: Additional tests: | ||
13 | * - stdin to stderr | ||
14 | */ | ||
15 | |||
16 | typedef struct { | ||
17 | const char *childprocess_path; | ||
18 | } TestProcessData; | ||
19 | |||
20 | static TestProcessData parsed_args; | ||
21 | |||
22 | static void SDLCALL setUpProcess(void **arg) { | ||
23 | *arg = &parsed_args; | ||
24 | } | ||
25 | |||
26 | static const char *options[] = { | ||
27 | "/path/to/childprocess" EXE, | ||
28 | NULL | ||
29 | }; | ||
30 | |||
31 | static char **CreateArguments(int ignore, ...) { | ||
32 | va_list ap; | ||
33 | size_t count = 1; | ||
34 | size_t i; | ||
35 | char **result; | ||
36 | |||
37 | va_start(ap, ignore); | ||
38 | for (;;) { | ||
39 | const char *keyN = va_arg(ap, const char *); | ||
40 | if (!keyN) { | ||
41 | break; | ||
42 | } | ||
43 | count += 1; | ||
44 | } | ||
45 | va_end(ap); | ||
46 | |||
47 | result = SDL_calloc(count, sizeof(char *)); | ||
48 | |||
49 | i = 0; | ||
50 | va_start(ap, ignore); | ||
51 | for (;;) { | ||
52 | const char *keyN = va_arg(ap, const char *); | ||
53 | if (!keyN) { | ||
54 | break; | ||
55 | } | ||
56 | result[i++] = SDL_strdup(keyN); | ||
57 | } | ||
58 | va_end(ap); | ||
59 | |||
60 | return result; | ||
61 | } | ||
62 | |||
63 | static void DestroyStringArray(char **list) { | ||
64 | char **current; | ||
65 | |||
66 | if (!list) { | ||
67 | return; | ||
68 | } | ||
69 | for (current = list; *current; current++) { | ||
70 | SDL_free(*current); | ||
71 | } | ||
72 | SDL_free(list); | ||
73 | } | ||
74 | |||
75 | static int SDLCALL process_testArguments(void *arg) | ||
76 | { | ||
77 | TestProcessData *data = (TestProcessData *)arg; | ||
78 | const char *process_args[] = { | ||
79 | data->childprocess_path, | ||
80 | "--print-arguments", | ||
81 | "--", | ||
82 | "", | ||
83 | " ", | ||
84 | "a b c", | ||
85 | "a\tb\tc\t", | ||
86 | "\"a b\" c", | ||
87 | "'a' 'b' 'c'", | ||
88 | "%d%%%s", | ||
89 | "\\t\\c", | ||
90 | "evil\\", | ||
91 | "a\\b\"c\\", | ||
92 | "\"\\^&|<>%", /* characters with a special meaning */ | ||
93 | NULL | ||
94 | }; | ||
95 | SDL_Process *process = NULL; | ||
96 | char *buffer; | ||
97 | int exit_code; | ||
98 | int i; | ||
99 | size_t total_read = 0; | ||
100 | |||
101 | process = SDL_CreateProcess(process_args, true); | ||
102 | SDLTest_AssertCheck(process != NULL, "SDL_CreateProcess()"); | ||
103 | if (!process) { | ||
104 | goto failed; | ||
105 | } | ||
106 | |||
107 | exit_code = 0xdeadbeef; | ||
108 | buffer = (char *)SDL_ReadProcess(process, &total_read, &exit_code); | ||
109 | SDLTest_AssertCheck(buffer != NULL, "SDL_ReadProcess()"); | ||
110 | SDLTest_AssertCheck(exit_code == 0, "Exit code should be 0, is %d", exit_code); | ||
111 | if (!buffer) { | ||
112 | goto failed; | ||
113 | } | ||
114 | SDLTest_LogEscapedString("stdout of process: ", buffer, total_read); | ||
115 | |||
116 | for (i = 3; process_args[i]; i++) { | ||
117 | char line[64]; | ||
118 | SDL_snprintf(line, sizeof(line), "|%d=%s|", i - 3, process_args[i]); | ||
119 | SDLTest_AssertCheck(!!SDL_strstr(buffer, line), "Check %s is in output", line); | ||
120 | } | ||
121 | SDL_free(buffer); | ||
122 | |||
123 | SDLTest_AssertPass("About to destroy process"); | ||
124 | SDL_DestroyProcess(process); | ||
125 | return TEST_COMPLETED; | ||
126 | failed: | ||
127 | SDL_DestroyProcess(process); | ||
128 | return TEST_ABORTED; | ||
129 | } | ||
130 | |||
131 | static int SDLCALL process_testexitCode(void *arg) | ||
132 | { | ||
133 | TestProcessData *data = (TestProcessData *)arg; | ||
134 | int i; | ||
135 | int exit_codes[] = { | ||
136 | 0, 13, 31, 127, 255 | ||
137 | }; | ||
138 | |||
139 | for (i = 0; i < SDL_arraysize(exit_codes); i++) { | ||
140 | bool wait_result; | ||
141 | SDL_Process *process = NULL; | ||
142 | char **process_args = NULL; | ||
143 | char number_buffer[8]; | ||
144 | int exit_code; | ||
145 | |||
146 | SDL_snprintf(number_buffer, sizeof(number_buffer), "%d", exit_codes[i]); | ||
147 | |||
148 | process_args = CreateArguments(0, data->childprocess_path, "--exit-code", number_buffer, NULL); | ||
149 | |||
150 | process = SDL_CreateProcess((const char * const *)process_args, false); | ||
151 | SDLTest_AssertCheck(process != NULL, "SDL_CreateProcess()"); | ||
152 | if (!process) { | ||
153 | goto failed; | ||
154 | } | ||
155 | |||
156 | exit_code = 0xdeadbeef; | ||
157 | SDLTest_AssertPass("About to wait on process (first time)"); | ||
158 | wait_result = SDL_WaitProcess(process, true, &exit_code); | ||
159 | SDLTest_AssertCheck(wait_result == true, "SDL_WaitProcess(): Process should have closed immediately"); | ||
160 | SDLTest_AssertCheck(exit_code == exit_codes[i], "SDL_WaitProcess(): Exit code should be %d, is %d", exit_codes[i], exit_code); | ||
161 | |||
162 | exit_code = 0xdeadbeef; | ||
163 | SDLTest_AssertPass("About to wait on process (second time)"); | ||
164 | wait_result = SDL_WaitProcess(process, true, &exit_code); | ||
165 | SDLTest_AssertCheck(wait_result == true, "SDL_WaitProcess(): Process should have closed immediately"); | ||
166 | SDLTest_AssertCheck(exit_code == exit_codes[i], "SDL_WaitProcess(): Exit code should be %d, is %d", exit_codes[i], exit_code); | ||
167 | |||
168 | SDLTest_AssertPass("About to destroy process"); | ||
169 | SDL_DestroyProcess(process); | ||
170 | DestroyStringArray(process_args); | ||
171 | continue; | ||
172 | failed: | ||
173 | SDL_DestroyProcess(process); | ||
174 | DestroyStringArray(process_args); | ||
175 | return TEST_ABORTED; | ||
176 | } | ||
177 | return TEST_COMPLETED; | ||
178 | #if 0 | ||
179 | failed: | ||
180 | SDL_DestroyProcess(process); | ||
181 | DestroyStringArray(process_args); | ||
182 | return TEST_ABORTED; | ||
183 | #endif | ||
184 | } | ||
185 | |||
186 | static int SDLCALL process_testInheritedEnv(void *arg) | ||
187 | { | ||
188 | TestProcessData *data = (TestProcessData *)arg; | ||
189 | const char *process_args[] = { | ||
190 | data->childprocess_path, | ||
191 | "--print-environment", | ||
192 | NULL, | ||
193 | }; | ||
194 | SDL_PropertiesID props; | ||
195 | SDL_Process *process = NULL; | ||
196 | Sint64 pid; | ||
197 | int exit_code; | ||
198 | char random_env1[64]; | ||
199 | char random_env2[64]; | ||
200 | static const char *const TEST_ENV_KEY1 = "testprocess_inherited_var"; | ||
201 | static const char *const TEST_ENV_KEY2 = "testprocess_other_var"; | ||
202 | char *test_env_val1 = NULL; | ||
203 | char *test_env_val2 = NULL; | ||
204 | char *buffer = NULL; | ||
205 | |||
206 | test_env_val1 = SDLTest_RandomAsciiStringOfSize(32); | ||
207 | SDL_snprintf(random_env1, sizeof(random_env1), "%s=%s", TEST_ENV_KEY1, test_env_val1); | ||
208 | SDLTest_AssertPass("Setting parent environment variable %s=%s", TEST_ENV_KEY1, test_env_val1); | ||
209 | SDL_SetEnvironmentVariable(SDL_GetEnvironment(), TEST_ENV_KEY1, test_env_val1, true); | ||
210 | |||
211 | SDL_UnsetEnvironmentVariable(SDL_GetEnvironment(), TEST_ENV_KEY2); | ||
212 | |||
213 | props = SDL_CreateProperties(); | ||
214 | SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, (void *)process_args); | ||
215 | SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_APP); | ||
216 | process = SDL_CreateProcessWithProperties(props); | ||
217 | SDL_DestroyProperties(props); | ||
218 | SDLTest_AssertCheck(process != NULL, "SDL_CreateProcessWithProperties()"); | ||
219 | if (!process) { | ||
220 | goto failed; | ||
221 | } | ||
222 | |||
223 | test_env_val2 = SDLTest_RandomAsciiStringOfSize(32); | ||
224 | SDL_snprintf(random_env2, sizeof(random_env2), "%s=%s", TEST_ENV_KEY2, test_env_val2); | ||
225 | SDLTest_AssertPass("Setting parent environment variable %s=%s", TEST_ENV_KEY2, test_env_val2); | ||
226 | SDL_SetEnvironmentVariable(SDL_GetEnvironment(),TEST_ENV_KEY2, test_env_val2, true); | ||
227 | SDLTest_AssertCheck(SDL_strcmp(test_env_val1, test_env_val2) != 0, "Sanity checking the 2 random environment variables are not identical"); | ||
228 | |||
229 | props = SDL_GetProcessProperties(process); | ||
230 | SDLTest_AssertCheck(props != 0, "SDL_GetProcessProperties()"); | ||
231 | |||
232 | pid = SDL_GetNumberProperty(props, SDL_PROP_PROCESS_PID_NUMBER, 0); | ||
233 | SDLTest_AssertCheck(pid != 0, "Checking process ID, expected non-zero, got %" SDL_PRIs64, pid); | ||
234 | |||
235 | exit_code = 0xdeadbeef; | ||
236 | buffer = (char *)SDL_ReadProcess(process, NULL, &exit_code); | ||
237 | SDLTest_AssertCheck(buffer != NULL, "SDL_ReadProcess()"); | ||
238 | SDLTest_AssertCheck(exit_code == 0, "Exit code should be 0, is %d", exit_code); | ||
239 | |||
240 | SDLTest_AssertCheck(SDL_strstr(buffer, random_env1) != NULL, "Environment of child should contain \"%s\"", test_env_val1); | ||
241 | SDLTest_AssertCheck(SDL_strstr(buffer, random_env2) == NULL, "Environment of child should not contain \"%s\"", test_env_val2); | ||
242 | |||
243 | SDLTest_AssertPass("About to destroy process"); | ||
244 | SDL_DestroyProcess(process); | ||
245 | SDL_free(test_env_val1); | ||
246 | SDL_free(test_env_val2); | ||
247 | SDL_free(buffer); | ||
248 | return TEST_COMPLETED; | ||
249 | failed: | ||
250 | SDL_free(test_env_val1); | ||
251 | SDL_free(test_env_val2); | ||
252 | SDL_DestroyProcess(process); | ||
253 | SDL_free(buffer); | ||
254 | return TEST_ABORTED; | ||
255 | } | ||
256 | |||
257 | static int SDLCALL process_testNewEnv(void *arg) | ||
258 | { | ||
259 | TestProcessData *data = (TestProcessData *)arg; | ||
260 | const char *process_args[] = { | ||
261 | data->childprocess_path, | ||
262 | "--print-environment", | ||
263 | NULL, | ||
264 | }; | ||
265 | SDL_Environment *process_env; | ||
266 | SDL_PropertiesID props; | ||
267 | SDL_Process *process = NULL; | ||
268 | Sint64 pid; | ||
269 | int exit_code; | ||
270 | char random_env1[64]; | ||
271 | char random_env2[64]; | ||
272 | static const char *const TEST_ENV_KEY1 = "testprocess_inherited_var"; | ||
273 | static const char *const TEST_ENV_KEY2 = "testprocess_other_var"; | ||
274 | char *test_env_val1 = NULL; | ||
275 | char *test_env_val2 = NULL; | ||
276 | char *buffer = NULL; | ||
277 | size_t total_read = 0; | ||
278 | |||
279 | test_env_val1 = SDLTest_RandomAsciiStringOfSize(32); | ||
280 | SDL_snprintf(random_env1, sizeof(random_env1), "%s=%s", TEST_ENV_KEY1, test_env_val1); | ||
281 | SDLTest_AssertPass("Unsetting parent environment variable %s", TEST_ENV_KEY1); | ||
282 | SDL_UnsetEnvironmentVariable(SDL_GetEnvironment(), TEST_ENV_KEY1); | ||
283 | |||
284 | process_env = SDL_CreateEnvironment(true); | ||
285 | SDL_SetEnvironmentVariable(process_env, "PATH", SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "PATH"), true); | ||
286 | SDL_SetEnvironmentVariable(process_env, "LD_LIBRARY_PATH", SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "LD_LIBRARY_PATH"), true); | ||
287 | SDL_SetEnvironmentVariable(process_env, "DYLD_LIBRARY_PATH", SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "DYLD_LIBRARY_PATH"), true); | ||
288 | SDL_SetEnvironmentVariable(process_env, TEST_ENV_KEY1, test_env_val1, true); | ||
289 | |||
290 | test_env_val2 = SDLTest_RandomAsciiStringOfSize(32); | ||
291 | SDL_snprintf(random_env2, sizeof(random_env2), "%s=%s", TEST_ENV_KEY2, test_env_val1); | ||
292 | SDLTest_AssertPass("Setting parent environment variable %s=%s", TEST_ENV_KEY2, test_env_val2); | ||
293 | SDL_SetEnvironmentVariable(SDL_GetEnvironment(), TEST_ENV_KEY2, test_env_val2, true); | ||
294 | SDLTest_AssertCheck(SDL_strcmp(test_env_val1, test_env_val2) != 0, "Sanity checking the 2 random environment variables are not identical"); | ||
295 | |||
296 | props = SDL_CreateProperties(); | ||
297 | SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, (void *)process_args); | ||
298 | SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ENVIRONMENT_POINTER, process_env); | ||
299 | SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_APP); | ||
300 | process = SDL_CreateProcessWithProperties(props); | ||
301 | SDL_DestroyProperties(props); | ||
302 | SDLTest_AssertCheck(process != NULL, "SDL_CreateProcessWithProperties()"); | ||
303 | if (!process) { | ||
304 | goto failed; | ||
305 | } | ||
306 | |||
307 | props = SDL_GetProcessProperties(process); | ||
308 | SDLTest_AssertCheck(props != 0, "SDL_GetProcessProperties()"); | ||
309 | |||
310 | pid = SDL_GetNumberProperty(props, SDL_PROP_PROCESS_PID_NUMBER, 0); | ||
311 | SDLTest_AssertCheck(pid != 0, "Checking process ID, expected non-zero, got %" SDL_PRIs64, pid); | ||
312 | |||
313 | exit_code = 0xdeadbeef; | ||
314 | buffer = (char *)SDL_ReadProcess(process, &total_read, &exit_code); | ||
315 | SDLTest_AssertCheck(buffer != NULL, "SDL_ReadProcess()"); | ||
316 | SDLTest_AssertCheck(exit_code == 0, "Exit code should be 0, is %d", exit_code); | ||
317 | SDLTest_LogEscapedString("Text read from subprocess: ", buffer, total_read); | ||
318 | |||
319 | SDLTest_AssertCheck(SDL_strstr(buffer, random_env1) != NULL, "Environment of child should contain \"%s\"", random_env1); | ||
320 | SDLTest_AssertCheck(SDL_strstr(buffer, random_env2) == NULL, "Environment of child should not contain \"%s\"", random_env1); | ||
321 | |||
322 | SDLTest_AssertPass("About to destroy process"); | ||
323 | SDL_DestroyProcess(process); | ||
324 | SDL_DestroyEnvironment(process_env); | ||
325 | SDL_free(test_env_val1); | ||
326 | SDL_free(test_env_val2); | ||
327 | SDL_free(buffer); | ||
328 | return TEST_COMPLETED; | ||
329 | |||
330 | failed: | ||
331 | SDL_DestroyProcess(process); | ||
332 | SDL_DestroyEnvironment(process_env); | ||
333 | SDL_free(test_env_val1); | ||
334 | SDL_free(test_env_val2); | ||
335 | SDL_free(buffer); | ||
336 | return TEST_ABORTED; | ||
337 | } | ||
338 | |||
339 | static int SDLCALL process_testKill(void *arg) | ||
340 | { | ||
341 | TestProcessData *data = (TestProcessData *)arg; | ||
342 | const char *process_args[] = { | ||
343 | data->childprocess_path, | ||
344 | "--stdin", | ||
345 | NULL, | ||
346 | }; | ||
347 | SDL_Process *process = NULL; | ||
348 | SDL_PropertiesID props; | ||
349 | Sint64 pid; | ||
350 | int result; | ||
351 | int exit_code; | ||
352 | |||
353 | SDLTest_AssertPass("About to call SDL_CreateProcess(true)"); | ||
354 | process = SDL_CreateProcess(process_args, true); | ||
355 | if (!process) { | ||
356 | goto failed; | ||
357 | } | ||
358 | |||
359 | props = SDL_GetProcessProperties(process); | ||
360 | SDLTest_AssertCheck(props != 0, "SDL_GetProcessProperties()"); | ||
361 | |||
362 | pid = SDL_GetNumberProperty(props, SDL_PROP_PROCESS_PID_NUMBER, 0); | ||
363 | SDLTest_AssertCheck(pid != 0, "Checking process ID, expected non-zero, got %" SDL_PRIs64, pid); | ||
364 | |||
365 | exit_code = 0xdeadbeef; | ||
366 | SDLTest_AssertPass("About to call SDL_WaitProcess(false)"); | ||
367 | result = SDL_WaitProcess(process, false, &exit_code); | ||
368 | SDLTest_AssertCheck(result == false, "Process should not have exited yet"); | ||
369 | |||
370 | SDLTest_AssertPass("About to call SDL_KillProcess(false)"); | ||
371 | result = SDL_KillProcess(process, false); | ||
372 | SDLTest_AssertCheck(result == true, "Process should have exited"); | ||
373 | |||
374 | exit_code = 0; | ||
375 | SDLTest_AssertPass("About to call SDL_WaitProcess(true)"); | ||
376 | result = SDL_WaitProcess(process, true, &exit_code); | ||
377 | SDLTest_AssertCheck(result == true, "Process should have exited"); | ||
378 | SDLTest_AssertCheck(exit_code != 0, "Exit code should be non-zero, is %d", exit_code); | ||
379 | |||
380 | SDLTest_AssertPass("About to destroy process"); | ||
381 | SDL_DestroyProcess(process); | ||
382 | return TEST_COMPLETED; | ||
383 | |||
384 | failed: | ||
385 | SDL_DestroyProcess(process); | ||
386 | return TEST_ABORTED; | ||
387 | } | ||
388 | |||
389 | static int process_testStdinToStdout(void *arg) | ||
390 | { | ||
391 | TestProcessData *data = (TestProcessData *)arg; | ||
392 | const char *process_args[] = { | ||
393 | data->childprocess_path, | ||
394 | "--stdin-to-stdout", | ||
395 | NULL, | ||
396 | }; | ||
397 | SDL_PropertiesID props; | ||
398 | SDL_Process *process = NULL; | ||
399 | Sint64 pid; | ||
400 | SDL_IOStream *process_stdin = NULL; | ||
401 | SDL_IOStream *process_stdout = NULL; | ||
402 | SDL_IOStream *process_stderr = NULL; | ||
403 | size_t text_in_size = 1 * 1024 * 1024; | ||
404 | char *text_in = NULL; | ||
405 | size_t total_written; | ||
406 | size_t total_read; | ||
407 | bool wait_result; | ||
408 | int exit_code; | ||
409 | SDL_IOStream *stdout_stream = NULL; | ||
410 | char *stdout_stream_buf; | ||
411 | int iteration_count = 0; | ||
412 | |||
413 | text_in = SDLTest_RandomAsciiStringOfSize((int)text_in_size); | ||
414 | /* Make sure text_in does not contain EOF */ | ||
415 | for (;;) { | ||
416 | char *e = SDL_strstr(text_in, "EOF"); | ||
417 | if (!e) { | ||
418 | break; | ||
419 | } | ||
420 | e[0] = 'N'; | ||
421 | } | ||
422 | text_in[text_in_size - 3] = 'E'; | ||
423 | text_in[text_in_size - 2] = 'O'; | ||
424 | text_in[text_in_size - 1] = 'F'; | ||
425 | |||
426 | stdout_stream = SDL_IOFromDynamicMem(); | ||
427 | |||
428 | props = SDL_CreateProperties(); | ||
429 | SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, (void *)process_args); | ||
430 | SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDIN_NUMBER, SDL_PROCESS_STDIO_APP); | ||
431 | SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_APP); | ||
432 | process = SDL_CreateProcessWithProperties(props); | ||
433 | SDL_DestroyProperties(props); | ||
434 | SDLTest_AssertCheck(process != NULL, "SDL_CreateProcessWithProperties()"); | ||
435 | if (!process) { | ||
436 | goto failed; | ||
437 | } | ||
438 | |||
439 | props = SDL_GetProcessProperties(process); | ||
440 | SDLTest_AssertCheck(props != 0, "SDL_GetProcessProperties()"); | ||
441 | |||
442 | pid = SDL_GetNumberProperty(props, SDL_PROP_PROCESS_PID_NUMBER, 0); | ||
443 | SDLTest_AssertCheck(pid != 0, "Checking process ID, expected non-zero, got %" SDL_PRIs64, pid); | ||
444 | |||
445 | process_stdin = SDL_GetProcessInput(process); | ||
446 | SDLTest_AssertCheck(process_stdin != NULL, "SDL_GetPointerProperty(SDL_PROP_PROCESS_STDIN_POINTER) returns a valid IO stream"); | ||
447 | process_stdout = SDL_GetProcessOutput(process); | ||
448 | SDLTest_AssertCheck(process_stdout != NULL, "SDL_GetPointerProperty(SDL_PROP_PROCESS_STDOUT_POINTER) returns a valid IO stream"); | ||
449 | process_stderr = (SDL_IOStream *)SDL_GetPointerProperty(props, SDL_PROP_PROCESS_STDERR_POINTER, NULL); | ||
450 | SDLTest_AssertCheck(process_stderr == NULL, "SDL_GetPointerProperty(SDL_PROP_PROCESS_STDERR_POINTER) returns NULL"); | ||
451 | if (!process_stdin || !process_stdout) { | ||
452 | goto failed; | ||
453 | } | ||
454 | |||
455 | total_written = 0; | ||
456 | total_read = 0; | ||
457 | for (;;) { | ||
458 | int log_this_iteration = (iteration_count % 32) == 32; | ||
459 | char local_buffer[16 * 4094]; | ||
460 | size_t amount_read; | ||
461 | SDL_IOStatus io_status; | ||
462 | if (total_written != text_in_size) { | ||
463 | size_t amount_written; | ||
464 | if (log_this_iteration) { | ||
465 | SDLTest_AssertPass("About to SDL_WriteIO (%dth time)", iteration_count); | ||
466 | } | ||
467 | amount_written = SDL_WriteIO(process_stdin, text_in + total_written, text_in_size - total_written); | ||
468 | if (log_this_iteration) { | ||
469 | SDLTest_Log("SDL_WriteIO() -> %u (%dth time)", (unsigned)amount_written, iteration_count); | ||
470 | } | ||
471 | if (amount_written == 0) { | ||
472 | io_status = SDL_GetIOStatus(process_stdin); | ||
473 | if (io_status != SDL_IO_STATUS_NOT_READY) { | ||
474 | SDLTest_Log("SDL_GetIOStatus(process_stdin) returns %d, breaking.", io_status); | ||
475 | break; | ||
476 | } | ||
477 | } | ||
478 | total_written += amount_written; | ||
479 | SDL_FlushIO(process_stdin); | ||
480 | } | ||
481 | |||
482 | /* FIXME: this needs a rate limit */ | ||
483 | if (log_this_iteration) { | ||
484 | SDLTest_AssertPass("About to SDL_ReadIO (%dth time)", iteration_count); | ||
485 | } | ||
486 | amount_read = SDL_ReadIO(process_stdout, local_buffer, sizeof(local_buffer)); | ||
487 | if (log_this_iteration) { | ||
488 | SDLTest_Log("SDL_ReadIO() -> %u (%dth time)", (unsigned)amount_read, iteration_count); | ||
489 | } | ||
490 | if (amount_read == 0) { | ||
491 | io_status = SDL_GetIOStatus(process_stdout); | ||
492 | if (io_status != SDL_IO_STATUS_NOT_READY) { | ||
493 | SDLTest_Log("SDL_GetIOStatus(process_stdout) returned %d, breaking.", io_status); | ||
494 | break; | ||
495 | } | ||
496 | } else { | ||
497 | total_read += amount_read; | ||
498 | SDL_WriteIO(stdout_stream, local_buffer, amount_read); | ||
499 | stdout_stream_buf = SDL_GetPointerProperty(SDL_GetIOProperties(stdout_stream), SDL_PROP_IOSTREAM_DYNAMIC_MEMORY_POINTER, NULL); | ||
500 | if (SDL_strstr(stdout_stream_buf, "EOF")) { | ||
501 | SDLTest_Log("Found EOF in stdout"); | ||
502 | break; | ||
503 | } | ||
504 | } | ||
505 | SDL_Delay(10); | ||
506 | } | ||
507 | SDLTest_Log("Wrote %" SDL_PRIu64 " bytes to process.stdin", (Uint64)total_written); | ||
508 | SDLTest_Log("Read %" SDL_PRIu64 " bytes from process.stdout",(Uint64)total_read); | ||
509 | |||
510 | stdout_stream_buf = SDL_GetPointerProperty(SDL_GetIOProperties(stdout_stream), SDL_PROP_IOSTREAM_DYNAMIC_MEMORY_POINTER, NULL); | ||
511 | SDLTest_CompareMemory(stdout_stream_buf, total_written, text_in, text_in_size); | ||
512 | |||
513 | exit_code = 0xdeadbeef; | ||
514 | wait_result = SDL_WaitProcess(process, false, &exit_code); | ||
515 | SDLTest_AssertCheck(wait_result == false, "Process should not have closed yet"); | ||
516 | |||
517 | SDLTest_AssertPass("About to close stdin"); | ||
518 | /* Closing stdin of `subprocessstdin --stdin-to-stdout` should close the process */ | ||
519 | SDL_CloseIO(process_stdin); | ||
520 | |||
521 | process_stdin = SDL_GetProcessInput(process); | ||
522 | SDLTest_AssertCheck(process_stdin == NULL, "SDL_GetPointerProperty(SDL_PROP_PROCESS_STDIN_POINTER) is cleared after close"); | ||
523 | |||
524 | SDLTest_AssertPass("About to wait on process"); | ||
525 | exit_code = 0xdeadbeef; | ||
526 | wait_result = SDL_WaitProcess(process, true, &exit_code); | ||
527 | SDLTest_AssertCheck(wait_result == true, "Process should have closed when closing stdin"); | ||
528 | SDLTest_AssertCheck(exit_code == 0, "Exit code should be 0, is %d", exit_code); | ||
529 | if (!wait_result) { | ||
530 | bool killed; | ||
531 | SDL_Log("About to kill process"); | ||
532 | killed = SDL_KillProcess(process, true); | ||
533 | SDLTest_AssertCheck(killed, "SDL_KillProcess succeeded"); | ||
534 | } | ||
535 | SDLTest_AssertPass("About to destroy process"); | ||
536 | SDL_DestroyProcess(process); | ||
537 | SDL_CloseIO(stdout_stream); | ||
538 | SDL_free(text_in); | ||
539 | return TEST_COMPLETED; | ||
540 | failed: | ||
541 | |||
542 | SDL_DestroyProcess(process); | ||
543 | SDL_CloseIO(stdout_stream); | ||
544 | SDL_free(text_in); | ||
545 | return TEST_ABORTED; | ||
546 | } | ||
547 | |||
548 | static int process_testStdinToStderr(void *arg) | ||
549 | { | ||
550 | TestProcessData *data = (TestProcessData *)arg; | ||
551 | const char *process_args[] = { | ||
552 | data->childprocess_path, | ||
553 | "--stdin-to-stderr", | ||
554 | NULL, | ||
555 | }; | ||
556 | SDL_Process *process = NULL; | ||
557 | SDL_IOStream *process_stdin = NULL; | ||
558 | SDL_IOStream *process_stdout = NULL; | ||
559 | SDL_IOStream *process_stderr = NULL; | ||
560 | 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"; | ||
561 | size_t result; | ||
562 | int exit_code; | ||
563 | SDL_PropertiesID props; | ||
564 | char buffer[256]; | ||
565 | size_t amount_read; | ||
566 | |||
567 | props = SDL_CreateProperties(); | ||
568 | SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, (void *)process_args); | ||
569 | SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDIN_NUMBER, SDL_PROCESS_STDIO_APP); | ||
570 | SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_NULL); | ||
571 | SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDERR_NUMBER, SDL_PROCESS_STDIO_APP); | ||
572 | process = SDL_CreateProcessWithProperties(props); | ||
573 | SDL_DestroyProperties(props); | ||
574 | SDLTest_AssertCheck(process != NULL, "SDL_CreateProcessWithProperties()"); | ||
575 | if (!process) { | ||
576 | goto failed; | ||
577 | } | ||
578 | |||
579 | SDLTest_AssertPass("About to write to process"); | ||
580 | process_stdin = SDL_GetProcessInput(process); | ||
581 | SDLTest_AssertCheck(process_stdin != NULL, "SDL_GetProcessInput()"); | ||
582 | result = SDL_WriteIO(process_stdin, text_in, SDL_strlen(text_in)); | ||
583 | SDLTest_AssertCheck(result == SDL_strlen(text_in), "SDL_WriteIO() wrote %d, expected %d", (int)result, (int)SDL_strlen(text_in)); | ||
584 | SDL_CloseIO(process_stdin); | ||
585 | |||
586 | process_stdout = SDL_GetProcessOutput(process); | ||
587 | SDLTest_AssertCheck(process_stdout == NULL, "Process has no stdout"); | ||
588 | |||
589 | process_stderr = SDL_GetPointerProperty(SDL_GetProcessProperties(process), SDL_PROP_PROCESS_STDERR_POINTER, NULL); | ||
590 | SDLTest_AssertCheck(process_stderr != NULL, "Process has stderr"); | ||
591 | |||
592 | exit_code = 0xdeadbeef; | ||
593 | result = SDL_WaitProcess(process, true, &exit_code); | ||
594 | SDLTest_AssertCheck(result == true, "Process should have finished"); | ||
595 | SDLTest_AssertCheck(exit_code == 0, "Exit code should be 0, is %d", exit_code); | ||
596 | |||
597 | amount_read = SDL_ReadIO(process_stderr, buffer, sizeof(buffer)); | ||
598 | SDLTest_CompareMemory(buffer, amount_read, text_in, SDL_strlen(text_in)); | ||
599 | |||
600 | SDLTest_AssertPass("About to destroy process"); | ||
601 | SDL_DestroyProcess(process); | ||
602 | return TEST_COMPLETED; | ||
603 | |||
604 | failed: | ||
605 | SDL_DestroyProcess(process); | ||
606 | return TEST_ABORTED; | ||
607 | } | ||
608 | |||
609 | static int process_testSimpleStdinToStdout(void *arg) | ||
610 | { | ||
611 | TestProcessData *data = (TestProcessData *)arg; | ||
612 | const char *process_args[] = { | ||
613 | data->childprocess_path, | ||
614 | "--stdin-to-stdout", | ||
615 | NULL, | ||
616 | }; | ||
617 | SDL_Process *process = NULL; | ||
618 | SDL_IOStream *input = NULL; | ||
619 | 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"; | ||
620 | char *buffer; | ||
621 | size_t result; | ||
622 | int exit_code; | ||
623 | size_t total_read = 0; | ||
624 | |||
625 | process = SDL_CreateProcess(process_args, true); | ||
626 | SDLTest_AssertCheck(process != NULL, "SDL_CreateProcess()"); | ||
627 | if (!process) { | ||
628 | goto failed; | ||
629 | } | ||
630 | |||
631 | SDLTest_AssertPass("About to write to process"); | ||
632 | input = SDL_GetProcessInput(process); | ||
633 | SDLTest_AssertCheck(input != NULL, "SDL_GetProcessInput()"); | ||
634 | result = SDL_WriteIO(input, text_in, SDL_strlen(text_in)); | ||
635 | SDLTest_AssertCheck(result == SDL_strlen(text_in), "SDL_WriteIO() wrote %d, expected %d", (int)result, (int)SDL_strlen(text_in)); | ||
636 | SDL_CloseIO(input); | ||
637 | |||
638 | input = SDL_GetProcessInput(process); | ||
639 | SDLTest_AssertCheck(input == NULL, "SDL_GetProcessInput() after close"); | ||
640 | |||
641 | exit_code = 0xdeadbeef; | ||
642 | buffer = (char *)SDL_ReadProcess(process, &total_read, &exit_code); | ||
643 | SDLTest_AssertCheck(buffer != NULL, "SDL_ReadProcess()"); | ||
644 | SDLTest_AssertCheck(exit_code == 0, "Exit code should be 0, is %d", exit_code); | ||
645 | if (!buffer) { | ||
646 | goto failed; | ||
647 | } | ||
648 | |||
649 | SDLTest_LogEscapedString("Expected text read from subprocess: %s", text_in, SDL_strlen(text_in)); | ||
650 | SDLTest_LogEscapedString("Actual text read from subprocess: %s", buffer, total_read); | ||
651 | 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); | ||
652 | SDLTest_AssertCheck(SDL_strcmp(buffer, text_in) == 0, "Subprocess stdout should match text written to stdin"); | ||
653 | SDL_free(buffer); | ||
654 | |||
655 | SDLTest_AssertPass("About to destroy process"); | ||
656 | SDL_DestroyProcess(process); | ||
657 | return TEST_COMPLETED; | ||
658 | |||
659 | failed: | ||
660 | SDL_DestroyProcess(process); | ||
661 | return TEST_ABORTED; | ||
662 | } | ||
663 | |||
664 | static int process_testMultiprocessStdinToStdout(void *arg) | ||
665 | { | ||
666 | TestProcessData *data = (TestProcessData *)arg; | ||
667 | const char *process_args[] = { | ||
668 | data->childprocess_path, | ||
669 | "--stdin-to-stdout", | ||
670 | "--log-stdin", | ||
671 | NULL, | ||
672 | NULL, | ||
673 | }; | ||
674 | SDL_Process *process1 = NULL; | ||
675 | SDL_Process *process2 = NULL; | ||
676 | SDL_PropertiesID props; | ||
677 | SDL_IOStream *input = NULL; | ||
678 | 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"; | ||
679 | char *buffer; | ||
680 | size_t result; | ||
681 | int exit_code; | ||
682 | size_t total_read = 0; | ||
683 | bool finished; | ||
684 | |||
685 | process_args[3] = "child1-stdin.txt"; | ||
686 | process1 = SDL_CreateProcess(process_args, true); | ||
687 | SDLTest_AssertCheck(process1 != NULL, "SDL_CreateProcess()"); | ||
688 | if (!process1) { | ||
689 | goto failed; | ||
690 | } | ||
691 | |||
692 | props = SDL_CreateProperties(); | ||
693 | SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, (void *)process_args); | ||
694 | SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDIN_NUMBER, SDL_PROCESS_STDIO_REDIRECT); | ||
695 | SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_STDIN_POINTER, SDL_GetPointerProperty(SDL_GetProcessProperties(process1), SDL_PROP_PROCESS_STDOUT_POINTER, NULL)); | ||
696 | SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_APP); | ||
697 | SDLTest_AssertPass("About to call SDL_CreateProcessWithProperties"); | ||
698 | process_args[3] = "child2-stdin.txt"; | ||
699 | process2 = SDL_CreateProcessWithProperties(props); | ||
700 | SDL_DestroyProperties(props); | ||
701 | SDLTest_AssertCheck(process2 != NULL, "SDL_CreateProcess()"); | ||
702 | if (!process2) { | ||
703 | goto failed; | ||
704 | } | ||
705 | |||
706 | SDLTest_AssertPass("About to write to process"); | ||
707 | input = SDL_GetProcessInput(process1); | ||
708 | SDLTest_AssertCheck(input != NULL, "SDL_GetProcessInput()"); | ||
709 | result = SDL_WriteIO(input, text_in, SDL_strlen(text_in)); | ||
710 | SDLTest_AssertCheck(result == SDL_strlen(text_in), "SDL_WriteIO() wrote %d, expected %d", (int)result, (int)SDL_strlen(text_in)); | ||
711 | SDL_CloseIO(input); | ||
712 | |||
713 | exit_code = 0xdeadbeef; | ||
714 | finished = SDL_WaitProcess(process1, true, &exit_code); | ||
715 | SDLTest_AssertCheck(finished == true, "process 1 should have finished"); | ||
716 | SDLTest_AssertCheck(exit_code == 0, "Exit code of process 1 should be 0, is %d", exit_code); | ||
717 | |||
718 | exit_code = 0xdeadbeef; | ||
719 | buffer = (char *)SDL_ReadProcess(process2, &total_read, &exit_code); | ||
720 | SDLTest_AssertCheck(buffer != NULL, "SDL_ReadProcess()"); | ||
721 | SDLTest_AssertCheck(exit_code == 0, "Exit code of process 2 should be 0, is %d", exit_code); | ||
722 | if (!buffer) { | ||
723 | goto failed; | ||
724 | } | ||
725 | |||
726 | SDLTest_LogEscapedString("Expected text read from subprocess: ", text_in, SDL_strlen(text_in)); | ||
727 | SDLTest_LogEscapedString("Actual text read from subprocess: ", buffer, total_read); | ||
728 | 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); | ||
729 | SDLTest_AssertCheck(SDL_strcmp(buffer, text_in) == 0, "Subprocess stdout should match text written to stdin"); | ||
730 | SDL_free(buffer); | ||
731 | SDLTest_AssertPass("About to destroy processes"); | ||
732 | SDL_DestroyProcess(process1); | ||
733 | SDL_DestroyProcess(process2); | ||
734 | return TEST_COMPLETED; | ||
735 | |||
736 | failed: | ||
737 | SDL_DestroyProcess(process1); | ||
738 | SDL_DestroyProcess(process2); | ||
739 | return TEST_ABORTED; | ||
740 | } | ||
741 | |||
742 | static int process_testWriteToFinishedProcess(void *arg) | ||
743 | { | ||
744 | TestProcessData *data = (TestProcessData *)arg; | ||
745 | const char *process_args[] = { | ||
746 | data->childprocess_path, | ||
747 | NULL, | ||
748 | }; | ||
749 | SDL_Process *process = NULL; | ||
750 | bool result; | ||
751 | int exit_code; | ||
752 | SDL_IOStream *process_stdin; | ||
753 | const char *text_in = "text_in"; | ||
754 | |||
755 | SDLTest_AssertPass("About to call SDL_CreateProcess"); | ||
756 | process = SDL_CreateProcess(process_args, true); | ||
757 | SDLTest_AssertCheck(process != NULL, "SDL_CreateProcess()"); | ||
758 | if (!process) { | ||
759 | goto failed; | ||
760 | } | ||
761 | |||
762 | exit_code = 0xdeadbeef; | ||
763 | SDLTest_AssertPass("About to call SDL_WaitProcess"); | ||
764 | result = SDL_WaitProcess(process, true, &exit_code); | ||
765 | SDLTest_AssertCheck(result, "SDL_WaitProcess()"); | ||
766 | SDLTest_AssertCheck(exit_code == 0, "Exit code should be 0, is %d", exit_code); | ||
767 | |||
768 | process_stdin = SDL_GetProcessInput(process); | ||
769 | SDLTest_AssertCheck(process_stdin != NULL, "SDL_GetProcessInput returns non-Null SDL_IOStream"); | ||
770 | SDLTest_AssertPass("About to call SDL_WriteIO on dead child process"); | ||
771 | SDL_WriteIO(process_stdin, text_in, SDL_strlen(text_in)); | ||
772 | |||
773 | SDLTest_AssertPass("About to destroy process"); | ||
774 | SDL_DestroyProcess(process); | ||
775 | return TEST_COMPLETED; | ||
776 | |||
777 | failed: | ||
778 | SDL_DestroyProcess(process); | ||
779 | return TEST_ABORTED; | ||
780 | } | ||
781 | |||
782 | static int process_testNonExistingExecutable(void *arg) | ||
783 | { | ||
784 | static const int STEM_LENGTH = 16; | ||
785 | char **process_args; | ||
786 | char *random_stem; | ||
787 | char *random_path; | ||
788 | SDL_Process *process = NULL; | ||
789 | |||
790 | random_stem = SDLTest_RandomAsciiStringOfSize(STEM_LENGTH); | ||
791 | random_path = SDL_malloc(STEM_LENGTH + SDL_strlen(EXE) + 1); | ||
792 | SDL_snprintf(random_path, STEM_LENGTH + SDL_strlen(EXE) + 1, "%s%s", random_stem, EXE); | ||
793 | SDL_free(random_stem); | ||
794 | SDLTest_AssertCheck(!SDL_GetPathInfo(random_path, NULL), "%s does not exist", random_path); | ||
795 | |||
796 | process_args = CreateArguments(0, random_path, NULL); | ||
797 | SDL_free(random_path); | ||
798 | |||
799 | SDLTest_AssertPass("About to call SDL_CreateProcess"); | ||
800 | process = SDL_CreateProcess((const char * const *)process_args, false); | ||
801 | SDLTest_AssertCheck(process == NULL, "SDL_CreateProcess() should have failed (%s)", SDL_GetError()); | ||
802 | |||
803 | DestroyStringArray(process_args); | ||
804 | return TEST_COMPLETED; | ||
805 | } | ||
806 | |||
807 | static int process_testBatBadButVulnerability(void *arg) | ||
808 | { | ||
809 | TestProcessData *data = (TestProcessData *)arg; | ||
810 | char *inject_arg = NULL; | ||
811 | char **process_args = NULL; | ||
812 | char *text_out = NULL; | ||
813 | size_t len_text_out; | ||
814 | int exitcode; | ||
815 | SDL_Process *process = NULL; | ||
816 | SDL_IOStream *child_bat; | ||
817 | char buffer[256]; | ||
818 | |||
819 | #ifndef SDL_PLATFORM_WINDOWS | ||
820 | SDLTest_AssertPass("The BatBadBut vulnerability only applied to Windows"); | ||
821 | return TEST_SKIPPED; | ||
822 | #endif | ||
823 | /* FIXME: remove child.bat at end of loop and/or create in temporary directory */ | ||
824 | child_bat = SDL_IOFromFile("child_batbadbut.bat", "w"); | ||
825 | SDL_IOprintf(child_bat, "@echo off\necho Hello from child_batbadbut.bat\necho \"|bat1=%%1|\"\n"); | ||
826 | SDL_CloseIO(child_bat); | ||
827 | |||
828 | inject_arg = SDL_malloc(SDL_strlen(data->childprocess_path) + 100); | ||
829 | SDL_snprintf(inject_arg, SDL_strlen(data->childprocess_path) + 100, "\"&%s --version --print-arguments --stdout OWNEDSTDOUT\"", data->childprocess_path); | ||
830 | process_args = CreateArguments(0, "child_batbadbut.bat", inject_arg, NULL); | ||
831 | |||
832 | SDLTest_AssertPass("About to call SDL_CreateProcess"); | ||
833 | process = SDL_CreateProcess((const char * const*)process_args, true); | ||
834 | SDLTest_AssertCheck(process != NULL, "SDL_CreateProcess"); | ||
835 | if (!process) { | ||
836 | goto cleanup; | ||
837 | } | ||
838 | text_out = SDL_ReadProcess(process, &len_text_out, &exitcode); | ||
839 | SDLTest_AssertCheck(exitcode == 0, "process exited with exitcode 0, was %d", exitcode); | ||
840 | SDLTest_AssertCheck(text_out != NULL, "SDL_ReadProcess returned data"); | ||
841 | SDLTest_LogEscapedString("Output: ", text_out, len_text_out); | ||
842 | if (!text_out) { | ||
843 | goto cleanup; | ||
844 | } | ||
845 | |||
846 | SDLTest_AssertCheck(SDL_strstr(text_out, "Hello from child_batbadbut") != NULL, "stdout contains 'Hello from child'"); | ||
847 | SDLTest_AssertCheck(SDL_strstr(text_out, "SDL version") == NULL, "stdout should not contain SDL version"); | ||
848 | SDL_snprintf(buffer, sizeof(buffer), "|bat1=\"\"\"&%s\"\"|", process_args[1] + 2); | ||
849 | SDLTest_LogEscapedString("stdout should contain: ", buffer, SDL_strlen(buffer)); | ||
850 | SDLTest_AssertCheck(SDL_strstr(text_out, buffer) != NULL, "Verify first argument"); | ||
851 | |||
852 | cleanup: | ||
853 | SDL_free(text_out); | ||
854 | SDL_DestroyProcess(process); | ||
855 | SDL_free(inject_arg); | ||
856 | DestroyStringArray(process_args); | ||
857 | return TEST_COMPLETED; | ||
858 | } | ||
859 | |||
860 | static int process_testFileRedirection(void *arg) | ||
861 | { | ||
862 | TestProcessData *data = (TestProcessData *)arg; | ||
863 | SDL_PropertiesID props = 0; | ||
864 | const char * process_args[] = { | ||
865 | data->childprocess_path, | ||
866 | "--stdin-to-stdout", | ||
867 | "--stdin-to-stderr", | ||
868 | NULL, | ||
869 | }; | ||
870 | const char TEXT_REF[] = "This is input for the child process"; | ||
871 | static const char *PATH_STDIN = "test_redirection_stdin.txt"; | ||
872 | static const char *PATH_STDOUT = "test_redirection_stdout.txt"; | ||
873 | static const char *PATH_STDERR = "test_redirection_stderr.txt"; | ||
874 | char *text_out = NULL; | ||
875 | size_t len_text_out; | ||
876 | int exitcode; | ||
877 | bool result; | ||
878 | SDL_Process *process = NULL; | ||
879 | SDL_IOStream *stream; | ||
880 | SDL_IOStream *input_stream = NULL; | ||
881 | SDL_IOStream *output_stream = NULL; | ||
882 | SDL_IOStream *error_stream = NULL; | ||
883 | |||
884 | stream = SDL_IOFromFile(PATH_STDIN, "w"); | ||
885 | SDLTest_AssertCheck(stream != NULL, "SDL_IOFromFile(\"%s\", \"w\")", PATH_STDIN); | ||
886 | if (!stream) { | ||
887 | goto cleanup; | ||
888 | } | ||
889 | SDL_WriteIO(stream, TEXT_REF, sizeof(TEXT_REF)); | ||
890 | SDL_CloseIO(stream); | ||
891 | |||
892 | input_stream = SDL_IOFromFile(PATH_STDIN, "r"); | ||
893 | SDLTest_AssertCheck(input_stream != NULL, "SDL_IOFromFile(\"%s\", \"r\")", PATH_STDIN); | ||
894 | if (!input_stream) { | ||
895 | goto cleanup; | ||
896 | } | ||
897 | |||
898 | output_stream = SDL_IOFromFile(PATH_STDOUT, "w"); | ||
899 | SDLTest_AssertCheck(output_stream != NULL, "SDL_IOFromFile(\"%s\", \"w\")", PATH_STDOUT); | ||
900 | if (!output_stream) { | ||
901 | goto cleanup; | ||
902 | } | ||
903 | |||
904 | error_stream = SDL_IOFromFile(PATH_STDERR, "w"); | ||
905 | SDLTest_AssertCheck(error_stream != NULL, "SDL_IOFromFile(\"%s\", \"w\")", PATH_STDERR); | ||
906 | if (!error_stream) { | ||
907 | goto cleanup; | ||
908 | } | ||
909 | |||
910 | props = SDL_CreateProperties(); | ||
911 | SDLTest_AssertCheck(props != 0, "SDL_CreateProperties()"); | ||
912 | if (!props) { | ||
913 | goto cleanup; | ||
914 | } | ||
915 | SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, (void *)process_args); | ||
916 | SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDIN_NUMBER, SDL_PROCESS_STDIO_REDIRECT); | ||
917 | SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_STDIN_POINTER, (void *)input_stream); | ||
918 | SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_REDIRECT); | ||
919 | SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_POINTER, (void *)output_stream); | ||
920 | SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDERR_NUMBER, SDL_PROCESS_STDIO_REDIRECT); | ||
921 | SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_STDERR_POINTER, (void *)error_stream); | ||
922 | process = SDL_CreateProcessWithProperties(props); | ||
923 | SDL_DestroyProperties(props); | ||
924 | SDLTest_AssertCheck(process != NULL, "SDL_CreateProcessWithProperties (%s)", SDL_GetError()); | ||
925 | if (!process) { | ||
926 | goto cleanup; | ||
927 | } | ||
928 | |||
929 | exitcode = 0xdeadbeef; | ||
930 | text_out = SDL_ReadProcess(process, &len_text_out, &exitcode); | ||
931 | SDLTest_AssertCheck(text_out == NULL, "SDL_ReadProcess should not be able to close a redirected process (%s)", SDL_GetError()); | ||
932 | SDLTest_AssertCheck(len_text_out == 0, "length written by SDL_ReadProcess should be 0"); | ||
933 | SDL_free(text_out); | ||
934 | text_out = NULL; | ||
935 | |||
936 | exitcode = 0xdeadbeef; | ||
937 | result = SDL_WaitProcess(process, true, &exitcode); | ||
938 | SDLTest_AssertCheck(result, "process must have exited"); | ||
939 | SDLTest_AssertCheck(exitcode == 0, "process exited with exitcode 0, was %d", exitcode); | ||
940 | |||
941 | SDL_CloseIO(input_stream); | ||
942 | input_stream = NULL; | ||
943 | SDL_CloseIO(output_stream); | ||
944 | output_stream = NULL; | ||
945 | SDL_CloseIO(error_stream); | ||
946 | error_stream = NULL; | ||
947 | |||
948 | text_out = SDL_LoadFile(PATH_STDOUT, &len_text_out); | ||
949 | SDLTest_AssertCheck(text_out != NULL, "SDL_LoadFile(\"%s\") succeeded (%s)", PATH_STDOUT, SDL_GetError()); | ||
950 | SDLTest_AssertPass("Comparing stdout with reference"); | ||
951 | SDLTest_CompareMemory(text_out, len_text_out, TEXT_REF, sizeof(TEXT_REF)); | ||
952 | SDL_free(text_out); | ||
953 | |||
954 | text_out = SDL_LoadFile(PATH_STDERR, &len_text_out); | ||
955 | SDLTest_AssertCheck(text_out != NULL, "SDL_LoadFile(\"%s\") succeeded (%s)", PATH_STDERR, SDL_GetError()); | ||
956 | SDLTest_AssertPass("Comparing stderr with reference"); | ||
957 | SDLTest_CompareMemory(text_out, len_text_out, TEXT_REF, sizeof(TEXT_REF)); | ||
958 | SDL_free(text_out); | ||
959 | |||
960 | cleanup: | ||
961 | SDL_CloseIO(input_stream); | ||
962 | SDL_CloseIO(output_stream); | ||
963 | SDL_CloseIO(error_stream); | ||
964 | SDL_DestroyProcess(process); | ||
965 | return TEST_COMPLETED; | ||
966 | } | ||
967 | |||
968 | static const SDLTest_TestCaseReference processTestArguments = { | ||
969 | process_testArguments, "process_testArguments", "Test passing arguments to child process", TEST_ENABLED | ||
970 | }; | ||
971 | |||
972 | static const SDLTest_TestCaseReference processTestExitCode = { | ||
973 | process_testexitCode, "process_testExitCode", "Test exit codes", TEST_ENABLED | ||
974 | }; | ||
975 | |||
976 | static const SDLTest_TestCaseReference processTestInheritedEnv = { | ||
977 | process_testInheritedEnv, "process_testInheritedEnv", "Test inheriting environment from parent process", TEST_ENABLED | ||
978 | }; | ||
979 | |||
980 | static const SDLTest_TestCaseReference processTestNewEnv = { | ||
981 | process_testNewEnv, "process_testNewEnv", "Test creating new environment for child process", TEST_ENABLED | ||
982 | }; | ||
983 | |||
984 | static const SDLTest_TestCaseReference processTestKill = { | ||
985 | process_testKill, "process_testKill", "Test Killing a child process", TEST_ENABLED | ||
986 | }; | ||
987 | |||
988 | static const SDLTest_TestCaseReference processTestStdinToStdout = { | ||
989 | process_testStdinToStdout, "process_testStdinToStdout", "Test writing to stdin and reading from stdout", TEST_ENABLED | ||
990 | }; | ||
991 | |||
992 | static const SDLTest_TestCaseReference processTestStdinToStderr = { | ||
993 | process_testStdinToStderr, "process_testStdinToStderr", "Test writing to stdin and reading from stderr", TEST_ENABLED | ||
994 | }; | ||
995 | |||
996 | static const SDLTest_TestCaseReference processTestSimpleStdinToStdout = { | ||
997 | process_testSimpleStdinToStdout, "process_testSimpleStdinToStdout", "Test writing to stdin and reading from stdout using the simplified API", TEST_ENABLED | ||
998 | }; | ||
999 | |||
1000 | static const SDLTest_TestCaseReference processTestMultiprocessStdinToStdout = { | ||
1001 | process_testMultiprocessStdinToStdout, "process_testMultiprocessStdinToStdout", "Test writing to stdin and reading from stdout using the simplified API", TEST_ENABLED | ||
1002 | }; | ||
1003 | |||
1004 | static const SDLTest_TestCaseReference processTestWriteToFinishedProcess = { | ||
1005 | process_testWriteToFinishedProcess, "process_testWriteToFinishedProcess", "Test writing to stdin of terminated process", TEST_ENABLED | ||
1006 | }; | ||
1007 | |||
1008 | static const SDLTest_TestCaseReference processTestNonExistingExecutable = { | ||
1009 | process_testNonExistingExecutable, "process_testNonExistingExecutable", "Test running a non-existing executable", TEST_ENABLED | ||
1010 | }; | ||
1011 | |||
1012 | static const SDLTest_TestCaseReference processTestBatBadButVulnerability = { | ||
1013 | process_testBatBadButVulnerability, "process_testBatBadButVulnerability", "Test BatBadBut vulnerability: command injection through cmd.exe", TEST_ENABLED | ||
1014 | }; | ||
1015 | |||
1016 | static const SDLTest_TestCaseReference processTestFileRedirection = { | ||
1017 | process_testFileRedirection, "process_testFileRedirection", "Test redirection from/to files", TEST_ENABLED | ||
1018 | }; | ||
1019 | |||
1020 | static const SDLTest_TestCaseReference *processTests[] = { | ||
1021 | &processTestArguments, | ||
1022 | &processTestExitCode, | ||
1023 | &processTestInheritedEnv, | ||
1024 | &processTestNewEnv, | ||
1025 | &processTestKill, | ||
1026 | &processTestStdinToStdout, | ||
1027 | &processTestStdinToStderr, | ||
1028 | &processTestSimpleStdinToStdout, | ||
1029 | &processTestMultiprocessStdinToStdout, | ||
1030 | &processTestWriteToFinishedProcess, | ||
1031 | &processTestNonExistingExecutable, | ||
1032 | &processTestBatBadButVulnerability, | ||
1033 | &processTestFileRedirection, | ||
1034 | NULL | ||
1035 | }; | ||
1036 | |||
1037 | static SDLTest_TestSuiteReference processTestSuite = { | ||
1038 | "Process", | ||
1039 | setUpProcess, | ||
1040 | processTests, | ||
1041 | NULL | ||
1042 | }; | ||
1043 | |||
1044 | static SDLTest_TestSuiteReference *testSuites[] = { | ||
1045 | &processTestSuite, | ||
1046 | NULL | ||
1047 | }; | ||
1048 | |||
1049 | int main(int argc, char *argv[]) | ||
1050 | { | ||
1051 | int i; | ||
1052 | int result; | ||
1053 | SDLTest_CommonState *state; | ||
1054 | SDLTest_TestSuiteRunner *runner; | ||
1055 | |||
1056 | /* Initialize test framework */ | ||
1057 | state = SDLTest_CommonCreateState(argv, 0); | ||
1058 | if (!state) { | ||
1059 | return 1; | ||
1060 | } | ||
1061 | |||
1062 | runner = SDLTest_CreateTestSuiteRunner(state, testSuites); | ||
1063 | |||
1064 | /* Parse commandline */ | ||
1065 | for (i = 1; i < argc;) { | ||
1066 | int consumed; | ||
1067 | |||
1068 | consumed = SDLTest_CommonArg(state, i); | ||
1069 | if (!consumed) { | ||
1070 | if (!parsed_args.childprocess_path) { | ||
1071 | parsed_args.childprocess_path = argv[i]; | ||
1072 | consumed = 1; | ||
1073 | } | ||
1074 | } | ||
1075 | if (consumed <= 0) { | ||
1076 | SDLTest_CommonLogUsage(state, argv[0], options); | ||
1077 | return 1; | ||
1078 | } | ||
1079 | |||
1080 | i += consumed; | ||
1081 | } | ||
1082 | |||
1083 | if (!parsed_args.childprocess_path) { | ||
1084 | SDLTest_CommonLogUsage(state, argv[0], options); | ||
1085 | return 1; | ||
1086 | } | ||
1087 | |||
1088 | result = SDLTest_ExecuteTestSuiteRunner(runner); | ||
1089 | |||
1090 | SDL_Quit(); | ||
1091 | SDLTest_DestroyTestSuiteRunner(runner); | ||
1092 | SDLTest_CommonDestroyState(state); | ||
1093 | return result; | ||
1094 | } | ||