1 /*
2 * This file Copyright (C) 2017 Mnemosyne LLC
3 *
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 */
8
9 #include <stdlib.h>
10
11 #include "transmission.h"
12 #include "error.h"
13 #include "file.h"
14 #include "subprocess.h"
15 #include "utils.h"
16
17 #include "libtransmission-test.h"
18
19 static char arg_dump_args[] = "--dump-args";
20 static char arg_dump_env[] = "--dump-env";
21 static char arg_dump_cwd[] = "--dump-cwd";
22
23 static char* self_path = NULL;
24
test_spawn_async_missing_exe(void)25 static int test_spawn_async_missing_exe(void)
26 {
27 char missing_exe_path[] = TR_IF_WIN32("C:\\", "/") "tr-missing-test-exe" TR_IF_WIN32(".exe", "");
28
29 char* const args[] =
30 {
31 missing_exe_path,
32 NULL
33 };
34
35 tr_error* error = NULL;
36 bool const ret = tr_spawn_async(args, NULL, NULL, &error);
37 check_bool(ret, ==, false);
38 check_ptr(error, !=, NULL);
39 check_int(error->code, !=, 0);
40 check_str(error->message, !=, NULL);
41
42 tr_error_clear(&error);
43
44 return 0;
45 }
46
test_spawn_async_args(void)47 static int test_spawn_async_args(void)
48 {
49 char* const test_dir = libtest_sandbox_create();
50 char* const result_path = tr_sys_path_native_separators(tr_buildPath(test_dir, "result.txt", NULL));
51 bool const allow_batch_metachars = TR_IF_WIN32(false, true) || !tr_str_has_suffix(self_path, ".cmd");
52
53 char test_arg_1[] = "arg1 ";
54 char test_arg_2[] = " arg2";
55 char test_arg_3[] = "";
56 char test_arg_4[] = "\"arg3'^! $PATH %PATH% \\";
57
58 char* const args[] =
59 {
60 self_path,
61 result_path,
62 arg_dump_args,
63 test_arg_1,
64 test_arg_2,
65 test_arg_3,
66 allow_batch_metachars ? test_arg_4 : NULL,
67 NULL
68 };
69
70 tr_error* error = NULL;
71 bool const ret = tr_spawn_async(args, NULL, NULL, &error);
72 check_bool(ret, ==, true);
73 check_ptr(error, ==, NULL);
74
75 while (!tr_sys_path_exists(result_path, NULL))
76 {
77 tr_wait_msec(10);
78 }
79
80 tr_sys_file_t fd = tr_sys_file_open(result_path, TR_SYS_FILE_READ, 0, NULL);
81 check_int(fd, !=, TR_BAD_SYS_FILE);
82
83 char buffer[1024];
84
85 check(tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
86 check_str(buffer, ==, test_arg_1);
87
88 check(tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
89 check_str(buffer, ==, test_arg_2);
90
91 check(tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
92 check_str(buffer, ==, test_arg_3);
93
94 if (allow_batch_metachars)
95 {
96 check(tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
97 check_str(buffer, ==, test_arg_4);
98 }
99
100 check(!tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
101
102 tr_sys_file_close(fd, NULL);
103
104 tr_free(result_path);
105 libtest_sandbox_destroy(test_dir);
106 tr_free(test_dir);
107 return 0;
108 }
109
test_spawn_async_env(void)110 static int test_spawn_async_env(void)
111 {
112 char* const test_dir = libtest_sandbox_create();
113 char* const result_path = tr_sys_path_native_separators(tr_buildPath(test_dir, "result.txt", NULL));
114
115 char test_env_key_1[] = "VAR1";
116 char test_env_key_2[] = "_VAR_2_";
117 char test_env_key_3[] = "vAr#";
118 char test_env_key_4[] = "FOO";
119 char test_env_key_5[] = "ZOO";
120 char test_env_key_6[] = "TR_MISSING_TEST_ENV_KEY";
121
122 char test_env_value_1[] = "value1 ";
123 char test_env_value_2[] = " value2";
124 char test_env_value_3[] = " \"value3'^! $PATH %PATH% ";
125 char test_env_value_4[] = "bar";
126 char test_env_value_5[] = "jar";
127
128 char* const args[] =
129 {
130 self_path,
131 result_path,
132 arg_dump_env,
133 test_env_key_1,
134 test_env_key_2,
135 test_env_key_3,
136 test_env_key_4,
137 test_env_key_5,
138 test_env_key_6,
139 NULL
140 };
141
142 char* const env[] =
143 {
144 tr_strdup_printf("%s=%s", test_env_key_1, test_env_value_1),
145 tr_strdup_printf("%s=%s", test_env_key_2, test_env_value_2),
146 tr_strdup_printf("%s=%s", test_env_key_3, test_env_value_3),
147 tr_strdup_printf("%s=%s", test_env_key_5, test_env_value_5),
148 NULL
149 };
150
151 /* Inherited */
152 char foo_env_value[] = "FOO=bar";
153 putenv(foo_env_value);
154
155 /* Overridden */
156 char zoo_env_value[] = "ZOO=tar";
157 putenv(zoo_env_value);
158
159 tr_error* error = NULL;
160 bool const ret = tr_spawn_async(args, env, NULL, &error);
161 check_bool(ret, ==, true);
162 check_ptr(error, ==, NULL);
163
164 while (!tr_sys_path_exists(result_path, NULL))
165 {
166 tr_wait_msec(10);
167 }
168
169 tr_sys_file_t fd = tr_sys_file_open(result_path, TR_SYS_FILE_READ, 0, NULL);
170 check_int(fd, !=, TR_BAD_SYS_FILE);
171
172 char buffer[1024];
173
174 check(tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
175 check_str(buffer, ==, test_env_value_1);
176
177 check(tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
178 check_str(buffer, ==, test_env_value_2);
179
180 check(tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
181 check_str(buffer, ==, test_env_value_3);
182
183 check(tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
184 check_str(buffer, ==, test_env_value_4);
185
186 check(tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
187 check_str(buffer, ==, test_env_value_5);
188
189 check(tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
190 check_str(buffer, ==, "<null>");
191
192 check(!tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
193
194 tr_sys_file_close(fd, NULL);
195
196 tr_free_ptrv((void* const*)env);
197 tr_free(result_path);
198 libtest_sandbox_destroy(test_dir);
199 tr_free(test_dir);
200 return 0;
201 }
202
test_spawn_async_cwd_explicit(void)203 static int test_spawn_async_cwd_explicit(void)
204 {
205 char* const test_dir = libtest_sandbox_create();
206 char* const result_path = tr_sys_path_native_separators(tr_buildPath(test_dir, "result.txt", NULL));
207
208 char* const args[] =
209 {
210 self_path,
211 result_path,
212 arg_dump_cwd,
213 NULL
214 };
215
216 tr_error* error = NULL;
217 bool const ret = tr_spawn_async(args, NULL, test_dir, &error);
218 check_bool(ret, ==, true);
219 check_ptr(error, ==, NULL);
220
221 while (!tr_sys_path_exists(result_path, NULL))
222 {
223 tr_wait_msec(10);
224 }
225
226 tr_sys_file_t fd = tr_sys_file_open(result_path, TR_SYS_FILE_READ, 0, NULL);
227 check_int(fd, !=, TR_BAD_SYS_FILE);
228
229 char buffer[1024];
230
231 check(tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
232 check_str(tr_sys_path_native_separators(buffer), ==, tr_sys_path_native_separators(test_dir));
233
234 check(!tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
235
236 tr_sys_file_close(fd, NULL);
237
238 tr_free(result_path);
239 libtest_sandbox_destroy(test_dir);
240 tr_free(test_dir);
241 return 0;
242 }
243
test_spawn_async_cwd_inherit(void)244 static int test_spawn_async_cwd_inherit(void)
245 {
246 char* const test_dir = libtest_sandbox_create();
247 char* const result_path = tr_sys_path_native_separators(tr_buildPath(test_dir, "result.txt", NULL));
248
249 char* const expected_cwd = tr_sys_dir_get_current(NULL);
250
251 char* const args[] =
252 {
253 self_path,
254 result_path,
255 arg_dump_cwd,
256 NULL
257 };
258
259 tr_error* error = NULL;
260 bool const ret = tr_spawn_async(args, NULL, NULL, &error);
261 check_bool(ret, ==, true);
262 check_ptr(error, ==, NULL);
263
264 while (!tr_sys_path_exists(result_path, NULL))
265 {
266 tr_wait_msec(10);
267 }
268
269 tr_sys_file_t fd = tr_sys_file_open(result_path, TR_SYS_FILE_READ, 0, NULL);
270 check_int(fd, !=, TR_BAD_SYS_FILE);
271
272 char buffer[1024];
273
274 check(tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
275 check_str(tr_sys_path_native_separators(buffer), ==, tr_sys_path_native_separators(expected_cwd));
276
277 check(!tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
278
279 tr_sys_file_close(fd, NULL);
280
281 tr_free(expected_cwd);
282 tr_free(result_path);
283 libtest_sandbox_destroy(test_dir);
284 tr_free(test_dir);
285 return 0;
286 }
287
test_spawn_async_cwd_missing(void)288 static int test_spawn_async_cwd_missing(void)
289 {
290 char* const test_dir = libtest_sandbox_create();
291 char* const result_path = tr_sys_path_native_separators(tr_buildPath(test_dir, "result.txt", NULL));
292
293 char* const args[] =
294 {
295 self_path,
296 result_path,
297 arg_dump_cwd,
298 NULL
299 };
300
301 tr_error* error = NULL;
302 bool const ret = tr_spawn_async(args, NULL, TR_IF_WIN32("C:\\", "/") "tr-missing-test-work-dir", &error);
303 check_bool(ret, ==, false);
304 check_ptr(error, !=, NULL);
305 check_int(error->code, !=, 0);
306 check_str(error->message, !=, NULL);
307
308 tr_error_clear(&error);
309
310 tr_free(result_path);
311 libtest_sandbox_destroy(test_dir);
312 tr_free(test_dir);
313 return 0;
314 }
315
main(int argc,char ** argv)316 int main(int argc, char** argv)
317 {
318 self_path = tr_sys_path_resolve(argv[0], NULL);
319
320 if (argc >= 3)
321 {
322 char* const result_path = argv[1];
323 char* const test_action = argv[2];
324
325 char* const tmp_result_path = tr_strdup_printf("%s.tmp", result_path);
326
327 tr_sys_file_t const fd = tr_sys_file_open(tmp_result_path, TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE |
328 TR_SYS_FILE_TRUNCATE, 0644, NULL);
329
330 if (fd == TR_BAD_SYS_FILE)
331 {
332 tr_free(tmp_result_path);
333 return 1;
334 }
335
336 if (strcmp(test_action, arg_dump_args) == 0)
337 {
338 for (int i = 3; i < argc; ++i)
339 {
340 tr_sys_file_write_line(fd, argv[i], NULL);
341 }
342 }
343 else if (strcmp(test_action, arg_dump_env) == 0)
344 {
345 for (int i = 3; i < argc; ++i)
346 {
347 char* const value = tr_env_get_string(argv[i], "<null>");
348 tr_sys_file_write_line(fd, value, NULL);
349 tr_free(value);
350 }
351 }
352 else if (strcmp(test_action, arg_dump_cwd) == 0)
353 {
354 char* const value = tr_sys_dir_get_current(NULL);
355 tr_sys_file_write_line(fd, value != NULL ? value : "<null>", NULL);
356 tr_free(value);
357 }
358 else
359 {
360 tr_sys_file_close(fd, NULL);
361 tr_sys_path_remove(tmp_result_path, NULL);
362
363 tr_free(tmp_result_path);
364 return 1;
365 }
366
367 tr_sys_file_close(fd, NULL);
368 tr_sys_path_rename(tmp_result_path, result_path, NULL);
369
370 tr_free(tmp_result_path);
371 return 0;
372 }
373
374 testFunc const tests[] =
375 {
376 test_spawn_async_missing_exe,
377 test_spawn_async_args,
378 test_spawn_async_env,
379 test_spawn_async_cwd_explicit,
380 test_spawn_async_cwd_inherit,
381 test_spawn_async_cwd_missing
382 };
383
384 int ret = runTests(tests, NUM_TESTS(tests));
385
386 #ifdef _WIN32
387
388 strcpy(self_path + strlen(self_path) - 4, ".cmd");
389
390 int ret2 = runTests(tests, NUM_TESTS(tests));
391
392 if (ret == 0)
393 {
394 ret = ret2;
395 }
396
397 #endif
398
399 tr_free(self_path);
400 return ret;
401 }
402