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