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