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