patch: bus: Fix timeout restarts
[platform/upstream/dbus.git] / test-runner.c
1 /* This file contains test-runner
2  *
3  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
4  * Author: Kazimierz Krosman <k.krosman@samsung.com>
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17 */
18
19 #include <unistd.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <getopt.h>
24 #include <fcntl.h>
25 #include <limits.h>
26 #include <signal.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <sys/select.h>
30 #include <sys/time.h>
31 #include <ctype.h>
32 #include <time.h>
33 #include <sys/select.h>
34 #include <errno.h>
35 #include <sys/types.h>
36 #include <sys/wait.h>
37 #include <assert.h>
38 #include <stdbool.h>
39
40 #define MAX_TC_NUM 1024
41 #define MAX_BUFFER (64*1024)
42 #define MAX_COMMENT 1024
43
44 #define INSTALLED_TESTS_DIR "/usr/lib/dbus/installed-tests/dbus/data"
45
46 enum {
47         INIT_TEST,
48         NEW_STDOUT,
49         NEW_STDERR,
50         RESULT_CODE,
51         RESULT_SIGNAL,
52         RESULT_ERROR,
53         RESULT_TIMEOUT
54 };
55
56 struct test_result {
57         bool is_positive;
58         char comment[MAX_COMMENT];
59         char result[MAX_COMMENT];
60         char name[MAX_COMMENT];
61 };
62
63 struct test_case {
64         const char* name;
65         const char* description;
66 };
67
68 struct binary {
69         const char* path;
70         const char* name;
71         struct test_case* test_cases;
72         int timeout;
73
74         char** (*prepare_args) (const struct binary* b, const char* test_name);
75         void (*parse) (const struct binary* b, const char* test_name, char* buffer, int state_change, int state_option);
76         int (*init)(void);
77         int (*clean)(void);
78 };
79
80 char* get_test_id(char* dest, const struct binary* b, const char* test_name);
81 void add_test_result(const char* test_id, const char* result, const char* comment, int res);
82 enum {
83         PIPE_READ,
84         PIPE_WRITE,
85 };
86
87 void parse_binary_outputs(const struct binary* b, const char* test_name, char* buffer, int state_change, int state_option);
88 char** prepare_args_for_binary(const struct binary* b, const char* test_name);
89 char** prepare_args_for_dir_iter(const struct binary* b, const char* test_name);
90 int init_environment_vars();
91
92 static struct test_case desc_2[] = {
93         {"", "Testing DBus ability to iterate over directory contents."}, {NULL, NULL}
94 };
95 static struct test_case desc_3[] = {
96         {"", "Simple manual tcp check."}, {NULL, NULL}
97 };
98 static struct test_case desc_4[] = {
99         {"", "Test for being disconnected by a corrupt message."}, {NULL, NULL}
100 };
101 static struct test_case desc_5[] = {
102         {"", "Integration tests for the dbus-daemon."}, {NULL, NULL}
103 };
104 static struct test_case desc_6[] = {
105         {"", "Checks DBus daemon eavesdropping ability."}, {NULL, NULL}
106 };
107 static struct test_case desc_7[] = {
108         {"", "Tests passing various ammounts of fds(If supported on given platform)."}, {NULL, NULL}
109 };
110 static struct test_case desc_8[] = {
111         {"", "Simple sanity-check for loopback through TCP and Unix sockets."}, {NULL, NULL}
112 };
113 static struct test_case desc_9[] = {
114         {"", "Simple sanity-check for D-Bus message serialization."}, {NULL, NULL}
115 };
116 static struct test_case desc_10[] = {
117         {"", "Integration tests for monitor-mode D-Bus connections."}, {NULL, NULL}
118 };
119 static struct test_case desc_11[] = {
120         {"", "Test for _dbus_printf_string_upper_bound."}, {NULL, NULL}
121 };
122 static struct test_case desc_12[] = {
123         {"", "Test for thread-safe reference-counting."}, {NULL, NULL}
124 };
125 static struct test_case desc_13[] = {
126         {"", "Test for passing unmodified messages between connections."}, {NULL, NULL}
127 };
128 static struct test_case desc_14[] = {
129         {"", "Unit tests for systemd activation."}, {NULL, NULL}
130 };
131 static struct test_case desc_15[] = {
132         {"", "Test for shell commands."}, {NULL, NULL}
133 };
134 static struct test_case desc_16[] = {
135         {"", "Test dor D-bus syntax validation."}, {NULL, NULL}
136 };
137 static struct test_case desc_17[] = {
138         {"", "Manual test for syslog support."}, {NULL, NULL}
139 };
140 static struct test_case desc_18[] = {
141         {"", "Integration tests for the dbus-daemon's uid-based hardening."}, {NULL, NULL}
142 };
143
144 /* This table is used to start binaries */
145 struct binary tests[] = {
146 /*path, name, TC_table, timeout in us, prepare_args_handler, parse_function_handler, init_handler, clean_handler*/
147         {"/usr/lib/dbus-tests/test-suites/dbus-tests/manual-dir-iter",
148                 "manual-dir-iter", desc_2, 1000*1000, prepare_args_for_dir_iter, parse_binary_outputs, init_environment_vars, NULL},
149         {"/usr/lib/dbus-tests/test-suites/dbus-tests/manual-tcp",
150                 "manual-tcp", desc_3, 1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
151         {"/usr/lib/dbus-tests/test-suites/dbus-tests/test-corrupt",
152                 "test-corrupt", desc_4, 10*1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
153         {"/usr/lib/dbus-tests/test-suites/dbus-tests/test-dbus-daemon",
154                 "test-dbus-daemon", desc_5, 15*1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
155         {"/usr/lib/dbus-tests/test-suites/dbus-tests/test-dbus-daemon-eavesdrop",
156                 "test-dbus-daemon-eavesdrop", desc_6, 1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
157         {"/usr/lib/dbus-tests/test-suites/dbus-tests/test-fdpass",
158                 "test-fdpass", desc_7, 3*1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
159         {"/usr/lib/dbus-tests/test-suites/dbus-tests/test-loopback",
160                 "test-loopback", desc_8, 1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
161         {"/usr/lib/dbus-tests/test-suites/dbus-tests/test-marshal",
162                 "test-marshal", desc_9, 1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
163         {"/usr/lib/dbus-tests/test-suites/dbus-tests/test-monitor",
164                 "test-monitor", desc_10, 3*1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
165         {"/usr/lib/dbus-tests/test-suites/dbus-tests/test-printf",
166                 "test-printf", desc_11, 2*1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
167         {"/usr/lib/dbus-tests/test-suites/dbus-tests/test-refs",
168                 "test-refs", desc_12, 90*1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
169         {"/usr/lib/dbus-tests/test-suites/dbus-tests/test-relay",
170                 "test-relay", desc_13, 6*1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
171         {"/usr/lib/dbus-tests/test-suites/dbus-tests/test-sd-activation",
172                 "test-sd-activation", desc_14, 90*1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
173         {"/usr/lib/dbus-tests/test-suites/dbus-tests/test-shell",
174                 "test-shell", desc_15, 1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
175         {"/usr/lib/dbus-tests/test-suites/dbus-tests/test-syntax",
176                 "test-syntax", desc_16, 1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
177         {"/usr/lib/dbus-tests/test-suites/dbus-tests/test-syslog",
178                 "test-syslog", desc_17, 1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
179         {"/usr/lib/dbus-tests/test-suites/dbus-tests/test-uid-permissions",
180                 "test-uid-permissions", desc_18, 1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL}
181 };
182
183
184
185 static const char result_pattern[] = "[r][%0]%1";
186 static struct state {
187         unsigned int i; // index of input buffer
188         unsigned int j; // index in results[n]
189         unsigned int n; //index in results buffer (0 or 1)
190         char results[2][MAX_COMMENT];
191 } g_state;
192
193 static void sm_reset(void)
194 {
195         memset(&g_state, 0, sizeof (g_state));
196 }
197
198 static int sm_update(char* buffer, int i)
199 {
200         int l = strlen(buffer) + 1;
201         while (i < l  && g_state.i < sizeof(result_pattern) - 1) {
202                 if (result_pattern[g_state.i] == '%') {
203                         g_state.n = result_pattern[g_state.i+1] - '0';
204                         if (g_state.n > 1) {
205                                 sm_reset();
206                                 i--;
207                         } else if (isalnum(buffer[i])) {
208                                 g_state.results[g_state.n][g_state.j++] = buffer[i];
209                         } else if (buffer[i] == result_pattern[g_state.i+2] || buffer[i] == '\n') {
210                                 g_state.results[g_state.n][g_state.j] = 0;
211                                 g_state.i += 3;
212                                 g_state.j = 0;
213                                 if (g_state.n == 1)
214                                         return i;
215                         } else {
216                                 g_state.i = 0;
217                                 g_state.j = 0;
218                         }
219                 } else if (result_pattern[g_state.i] == buffer[i]) {
220                         g_state.i++;
221                 } else {
222                         sm_reset();
223                 }
224                 i++;
225         }
226
227         if (g_state.i >= sizeof(result_pattern) - 1) {
228                 g_state.results[g_state.n][g_state.j] = 0;
229                 g_state.i += 3;
230                 g_state.j = 0;
231                 if (g_state.n == 1)
232                         return i;
233         }
234
235         return 0;
236 }
237
238 static const char* sm_get_result(int i)
239 {
240         return g_state.results[i];
241 }
242
243 static char* args[3];
244 char** prepare_args_for_binary(const struct binary* b, const char* test_name)
245 {
246         args[0] = (char*)b->name;
247         if (!test_name[0])
248                 args[1] = NULL;
249         else {
250                 args[1] = (char*)test_name;
251                 args[2] = NULL;
252         }
253         return args;
254 }
255
256 char** prepare_args_for_dir_iter(const struct binary* b, const char* test_name)
257 {
258         static char* args_dir[2];
259         args_dir[0] = (char*)b->name;
260         args_dir[1] = INSTALLED_TESTS_DIR;
261         return args_dir;
262 }
263
264 int init_environment_vars()
265 {
266         return !(putenv("DBUS_TEST_DATA="INSTALLED_TESTS_DIR));
267 }
268
269 void parse_binary_outputs(const struct binary* b, const char* test_name, char* buffer, int state_change, int state_option)
270 {
271         char test_id[MAX_COMMENT];
272
273         switch(state_change) {
274         case INIT_TEST:
275                 break;
276         case NEW_STDOUT:
277                 buffer[state_option] = 0;
278                 get_test_id(test_id, b, test_name);
279                 fprintf(stderr, "[stdout][%s]%s\n",test_id, buffer);
280                 break;
281         case NEW_STDERR:
282                 buffer[state_option] = 0;
283                 get_test_id(test_id, b, test_name);
284                 fprintf(stderr, "[stderr][%s]%s\n",test_id, buffer);
285                 break;
286         case RESULT_CODE:
287                 get_test_id(test_id, b, test_name);
288                 if (state_option != 0)
289                         add_test_result(test_id, "FAIL", "", 0);
290                 else if (state_option == 77)
291                         add_test_result(test_id, "SKIP", "", 0);
292                 else
293                         add_test_result(test_id, "PASS", "", 1);
294                 break;
295         case RESULT_SIGNAL:
296                 get_test_id(test_id, b, test_name);
297                 add_test_result(test_id, "FAIL", "Finished by SIGNAL", 0);
298                 break;
299         case RESULT_TIMEOUT:
300                 get_test_id(test_id, b, test_name);
301                 add_test_result(test_id, "FAIL", "Test TIMEOUT", 0);
302                 break;
303         }
304 }
305
306 static struct option long_options[] = {
307         {"list",        no_argument,       0, 'l'},
308         {"run",         required_argument, 0, 'r'},
309         {"description", required_argument, 0, 'd'},
310         {0,             0,                 0,  0 }
311 };
312
313 static int stdin_pipe[2];
314 static int stdout_pipe[2];
315 static int stderr_pipe[2];
316 static int gravedigger_pipe[2];
317 static char buffer[MAX_BUFFER];
318 static const char* requested_tc[MAX_TC_NUM];
319
320 char* get_test_id(char* dest, const struct binary* b, const char* test_name)
321 {
322         int len = strlen(b->name);
323         memcpy(dest, b->name, len);
324         memcpy(dest + len, test_name, strlen(test_name)+1);
325         return dest;
326 }
327
328 static void print_description(const char* name, const char* description)
329 {
330         printf("%s;%s\n",name, description);
331 }
332
333 static void print_list(const char* test_name)
334 {
335         unsigned int i;
336         char full_name[MAX_COMMENT];
337         for (i = 0;i < sizeof(tests)/sizeof(struct binary); i++) {
338                 int j = 0;
339                 int l = strlen(tests[i].name);
340                 memcpy(full_name, tests[i].name, l+1);
341                 if (test_name && strncmp(test_name, full_name, l) != 0)
342                         continue;
343
344                 while (tests[i].test_cases[j].name) {
345                         memcpy(full_name + l, tests[i].test_cases[j].name, strlen(tests[i].test_cases[j].name) + 1);
346                         if (!test_name || strcmp(full_name, test_name) == 0)
347                                 print_description(full_name,tests[i].test_cases[j].description);
348                         j++;
349                 }
350         }
351 }
352
353 static void stop_binary(const struct binary* b, pid_t pid, const char* test_name, int w_res)
354 {
355         int status = 0;
356         int res = 0;
357         if (w_res == 0)
358                 res = waitpid(pid, &status, WNOHANG);
359         else
360                 res = waitpid(pid, &status, 0);
361
362         if (res == 0) {
363                 //timeouted
364                 kill(pid, SIGKILL);
365                 res = waitpid(pid, &status, WNOHANG);
366                 b->parse(b, test_name, buffer, RESULT_TIMEOUT, res);
367         } else if (res < 0) {
368                 //errno check
369                 kill(pid, SIGKILL);
370                 res = waitpid(pid, &status, WNOHANG);
371                 b->parse(b, test_name, buffer, RESULT_ERROR, res);
372         } else if (res > 0) {
373                 if (WIFEXITED(status)) {
374                         b->parse(b, test_name, buffer, RESULT_CODE, WEXITSTATUS(status));
375                 } else if (WIFSIGNALED(status)) {
376                         b->parse(b, test_name, buffer, RESULT_SIGNAL, WTERMSIG(status));
377                 } else if (WIFSTOPPED(status)) {
378                         b->parse(b, test_name, buffer, RESULT_SIGNAL, WSTOPSIG(status));
379         } else if (WIFCONTINUED(status)) {
380                         kill(pid, SIGKILL);
381                         b->parse(b, test_name, buffer, RESULT_SIGNAL, -1);
382                 }
383         }
384 }
385
386
387 static void parse_output_with_timeout(const struct binary* b, pid_t pid, const char* test_name)
388 {
389         struct timeval tv;
390         fd_set rfds;
391         int nfds;
392         int res;
393         int w_res = 0;
394         tv.tv_sec = b->timeout/(1000*1000);
395         tv.tv_usec = (b->timeout-tv.tv_sec*1000*1000);
396         while (1) {
397                 FD_ZERO(&rfds);
398                 if (stdout_pipe[PIPE_READ] > -1) {
399                         assert(stdout_pipe[PIPE_READ] > -1);
400                         assert(stdout_pipe[PIPE_READ] < 1024);
401                         FD_SET(stdout_pipe[PIPE_READ], &rfds);
402                 }
403                 if (stderr_pipe[PIPE_READ] > -1) {
404                         assert(stderr_pipe[PIPE_READ] > -1);
405                         assert(stderr_pipe[PIPE_READ] < 1024);
406                         FD_SET(stderr_pipe[PIPE_READ], &rfds);
407                 }
408                 FD_SET(gravedigger_pipe[PIPE_READ], &rfds);
409
410                 nfds = select(FD_SETSIZE, &rfds, NULL, NULL, &tv);
411                 if (nfds == -1) {
412                         if (errno != EINTR) {
413                                 w_res = 0;
414                                 break;
415                         }
416                 } else if (nfds > 0) {
417                         if (stdout_pipe[PIPE_READ] > -1 && FD_ISSET(stdout_pipe[PIPE_READ], &rfds)) {
418                                 res = read(stdout_pipe[PIPE_READ], buffer, MAX_BUFFER-1);
419                                 if (res == 0 || (res < 0 && errno != EINTR)) {
420                                         close (stdout_pipe[PIPE_READ]);
421                                         stdout_pipe[PIPE_READ] = -1;
422                                         continue;
423                                 } else if (res >=0) {
424                                         b->parse(b, test_name, buffer, NEW_STDOUT, res);
425                                 }
426                         }
427
428                         if (stderr_pipe[PIPE_READ] > -1 && FD_ISSET(stderr_pipe[PIPE_READ], &rfds)) {
429                                 res = read(stderr_pipe[PIPE_READ], buffer, MAX_BUFFER-1);
430                                 if (res == 0 || (res < 0 && errno != EINTR)) {
431                                         close (stderr_pipe[PIPE_READ]);
432                                         stderr_pipe[PIPE_READ] = -1;
433                                         continue;
434                                 }
435                                 b->parse(b, test_name, buffer, NEW_STDERR, res);
436                         }
437
438                         if (FD_ISSET(gravedigger_pipe[PIPE_READ], &rfds)) {
439                                 w_res = 1;
440                                 break; //it has ended
441                         }
442                 } else {
443                         //timeout
444                         w_res = 0;
445                         break;
446                 }
447         }
448         stop_binary(b, pid, test_name, w_res);
449 }
450
451 static int create_child(const char* path, char* const arguments[])
452 {
453         int child;
454         int nResult;
455         if (pipe(gravedigger_pipe) < 0) {
456                 perror("allocating pipe for gravedigger failed");
457                 goto error1;
458         }
459
460         if (pipe(stdin_pipe) < 0) {
461                 perror("allocating pipe for child input redirect failed");
462                 goto error1;
463         }
464
465         if (pipe(stdout_pipe) < 0) {
466                 perror("allocating pipe for child output redirect failed");
467                 goto error2;
468         }
469
470         if (pipe(stderr_pipe) < 0) {
471                 perror("allocating pipe for child output redirect failed");
472                 goto error3;
473         }
474
475         child = fork();
476         if (!child) {
477                 char ld_path[512];
478                 sprintf(ld_path, "/usr/lib/dbus-tests/lib/libdbuspolicy-tests/:");
479                 // redirect stdin
480                 if (dup2(stdin_pipe[PIPE_READ], STDIN_FILENO) == -1) {
481                         perror("redirecting stdin failed");
482                         return -1;
483                 }
484
485         // redirect stdout
486                 if (dup2(stdout_pipe[PIPE_WRITE], STDOUT_FILENO) == -1) {
487                         perror("redirecting stdout failed");
488                         return -1;
489                 }
490
491         // redirect stderr
492                 if (dup2(stderr_pipe[PIPE_WRITE], STDERR_FILENO) == -1) {
493                         perror("redirecting stderr failed");
494                         return -1;
495                 }
496
497         // all these are for use by parent only
498                 close(stdin_pipe[PIPE_READ]);
499                 close(stdin_pipe[PIPE_WRITE]);
500                 close(stdout_pipe[PIPE_READ]);
501                 close(stdout_pipe[PIPE_WRITE]);
502                 close(stderr_pipe[PIPE_READ]);
503                 close(stderr_pipe[PIPE_WRITE]);
504                 close(gravedigger_pipe[PIPE_READ]);
505
506                 char* ld_path_b = getenv("LD_LIBRARY_PATH");
507                 if (ld_path_b != NULL)
508                         memcpy(ld_path + strlen(ld_path), ld_path_b, strlen(ld_path_b)+1);
509                 setenv("LD_LIBRARY_PATH", ld_path, 1);
510                 // run child process image
511                 nResult = execv(path, arguments);
512
513                 // if we get here at all, an error occurred, but we are in the child
514                 // process, so just exit
515                 perror("exec of the child process  failed");
516                 exit(nResult);
517         } else if (child > 0) {
518                 // parent continues here
519
520                 // close unused file descriptors, these are for child only
521                 close(stdin_pipe[PIPE_READ]);
522                 close(stdout_pipe[PIPE_WRITE]);
523                 close(stderr_pipe[PIPE_WRITE]);
524                 close(gravedigger_pipe[PIPE_WRITE]);
525         } else {
526                 // failed to create child
527                 goto error4;
528         }
529
530         return child;
531
532 error4:
533         close(stderr_pipe[PIPE_READ]);
534         close(stderr_pipe[PIPE_WRITE]);
535 error3:
536         close(stdout_pipe[PIPE_READ]);
537         close(stdout_pipe[PIPE_WRITE]);
538 error2:
539         close(stdin_pipe[PIPE_READ]);
540         close(stdin_pipe[PIPE_WRITE]);
541 error1:
542         return -1;
543 }
544
545 static void run_test(const struct binary* b, const char* test_name)
546 {
547         int res = -1;
548         char** arg;
549         char test_id[MAX_COMMENT];
550
551         assert(b);
552         assert(b->name);
553         assert(b->path);
554         assert(test_name);
555
556         arg = b->prepare_args(b, test_name);
557
558         if (b->init)
559                 if (!b->init()) {
560                         add_test_result(get_test_id(test_id, b, test_name), "ERROR", "Cannot init test", 0);
561                         return;
562                 }
563
564         res = create_child(b->path, arg);
565         if (res > 0)
566                 parse_output_with_timeout(b, res, test_name);
567         else
568                 add_test_result(get_test_id(test_id, b, test_name), "ERROR", "Cannot start test", 0);
569
570         if (b->clean)
571                 b->clean();
572 }
573
574 static void parse_run_test(const char* tc) {
575         unsigned int i = 0;
576         for (i = 0;i < sizeof(tests)/sizeof(struct binary); i++) {
577                 int len = strlen(tests[i].name);
578                 if (strncmp(tc, tests[i].name, len) == 0) {
579                         if (tc[len] == '*' || tc[len] == '\0')
580                                 run_test(&tests[i], "");
581             else
582                                 run_test(&tests[i], tc + len);
583                 }
584         }
585 }
586
587 static int parse_option(int argc, char* argv[])
588 {
589         int ch = 0;
590         int c = 0;
591         while ((ch = getopt_long(argc, argv, "lr:d:", long_options, NULL)) != -1) {
592                 switch (ch) {
593                 case 'l':
594                         print_list(NULL);
595                         return 1;
596                 case 'r':
597                         if (c >= MAX_TC_NUM - 1) //NULL at the end
598                                 return 0;
599
600                         if (optarg)
601                                 requested_tc[c++] = optarg;
602
603                         break;
604                 case 'd':
605                         print_list(optarg);
606                         return 1;
607                 }
608         }
609         return 0;
610 }
611
612 void add_test_result(const char* test_id, const char* result, const char* comment, int res)
613 {
614         printf("%s;%s;%s\n", test_id, result, comment);
615         fflush(stdout);
616 }
617
618 int main(int argc, char* argv[])
619 {
620         unsigned int i;
621         signal(SIGPIPE, SIG_IGN);
622         if (parse_option(argc, argv))
623                 return 0;
624
625         if (!requested_tc[0]) {
626                 for (i = 0;i < sizeof(tests)/sizeof(struct binary); i++)
627                     run_test(&tests[i], "");
628         } else {
629                 i = 0;
630                 while(requested_tc[i]) {
631                     parse_run_test(requested_tc[i]);
632                         i++;
633                 }
634         }
635
636         return 0;
637 }