summaryrefslogtreecommitdiff
path: root/test-runner.c
diff options
context:
space:
mode:
Diffstat (limited to 'test-runner.c')
-rw-r--r--test-runner.c654
1 files changed, 654 insertions, 0 deletions
diff --git a/test-runner.c b/test-runner.c
new file mode 100644
index 00000000..66400c00
--- /dev/null
+++ b/test-runner.c
@@ -0,0 +1,654 @@
+/* This file contains test-runner
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
+ * Author: Kazimierz Krosman <k.krosman@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <ctype.h>
+#include <time.h>
+#include <sys/select.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <assert.h>
+#include <stdbool.h>
+
+#define MAX_TC_NUM 1024
+#define MAX_BUFFER (64*1024)
+#define MAX_COMMENT 1024
+
+enum {
+ INIT_TEST,
+ NEW_STDOUT,
+ NEW_STDERR,
+ RESULT_CODE,
+ RESULT_SIGNAL,
+ RESULT_ERROR,
+ RESULT_TIMEOUT
+};
+
+struct test_result {
+ bool is_positive;
+ char comment[MAX_COMMENT];
+ char result[MAX_COMMENT];
+ char name[MAX_COMMENT];
+};
+
+struct test_case {
+ const char* name;
+ const char* description;
+};
+
+struct binary {
+ const char* path;
+ const char* name;
+ struct test_case* test_cases;
+ int timeout;
+
+ char** (*prepare_args) (const struct binary* b, const char* test_name);
+ void (*parse) (const struct binary* b, const char* test_name, char* buffer, int state_change, int state_option);
+ int (*init)(void);
+ int (*clean)(void);
+};
+
+char* get_test_id(char* dest, const struct binary* b, const char* test_name);
+void add_test_result(const char* test_id, const char* result, const char* comment, int res);
+enum {
+ PIPE_READ,
+ PIPE_WRITE,
+};
+
+void parse_binary_outputs(const struct binary* b, const char* test_name, char* buffer, int state_change, int state_option);
+char** prepare_args_for_binary(const struct binary* b, const char* test_name);
+char** prepare_args_for_dir_iter(const struct binary* b, const char* test_name);
+int init_environment_vars();
+
+static struct test_case desc_1[] = {
+ {"", "Simple sanity-check for authentication and authorization."}, {NULL, NULL}
+};
+static struct test_case desc_2[] = {
+ {"", "Testing DBus ability to iterate over directory contents."}, {NULL, NULL}
+};
+static struct test_case desc_3[] = {
+ {"", "Simple manual tcp check."}, {NULL, NULL}
+};
+static struct test_case desc_4[] = {
+ {"", "Test for being disconnected by a corrupt message."}, {NULL, NULL}
+};
+static struct test_case desc_5[] = {
+ {"", "Integration tests for the dbus-daemon."}, {NULL, NULL}
+};
+static struct test_case desc_6[] = {
+ {"", "Checks DBus daemon eavesdropping ability."}, {NULL, NULL}
+};
+static struct test_case desc_7[] = {
+ {"", "Tests passing various ammounts of fds(If supported on given platform)."}, {NULL, NULL}
+};
+static struct test_case desc_8[] = {
+ {"", "Simple sanity-check for loopback through TCP and Unix sockets."}, {NULL, NULL}
+};
+static struct test_case desc_9[] = {
+ {"", "Simple sanity-check for D-Bus message serialization."}, {NULL, NULL}
+};
+static struct test_case desc_10[] = {
+ {"", "Integration tests for monitor-mode D-Bus connections."}, {NULL, NULL}
+};
+static struct test_case desc_11[] = {
+ {"", "Test for _dbus_printf_string_upper_bound."}, {NULL, NULL}
+};
+static struct test_case desc_12[] = {
+ {"", "Test for thread-safe reference-counting."}, {NULL, NULL}
+};
+static struct test_case desc_13[] = {
+ {"", "Test for passing unmodified messages between connections."}, {NULL, NULL}
+};
+static struct test_case desc_14[] = {
+ {"", "Unit tests for systemd activation."}, {NULL, NULL}
+};
+static struct test_case desc_15[] = {
+ {"", "Test for shell commands."}, {NULL, NULL}
+};
+static struct test_case desc_16[] = {
+ {"", "Test dor D-bus syntax validation."}, {NULL, NULL}
+};
+static struct test_case desc_17[] = {
+ {"", "Manual test for syslog support."}, {NULL, NULL}
+};
+static struct test_case desc_18[] = {
+ {"", "Integration tests for the dbus-daemon's uid-based hardening."}, {NULL, NULL}
+};
+
+/* This table is used to start binaries */
+struct binary tests[] = {
+/*path, name, TC_table, timeout in us, prepare_args_handler, parse_function_handler, init_handler, clean_handler*/
+ {"/usr/lib/dbus-tests/test-suites/dbus-tests/manual-authz",
+ "manual-authz", desc_1, 1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
+ {"/usr/lib/dbus-tests/test-suites/dbus-tests/manual-dir-iter",
+ "manual-dir-iter", desc_2, 1000*1000, prepare_args_for_dir_iter, parse_binary_outputs, init_environment_vars, NULL},
+ {"/usr/lib/dbus-tests/test-suites/dbus-tests/manual-tcp",
+ "manual-tcp", desc_3, 1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
+ {"/usr/lib/dbus-tests/test-suites/dbus-tests/test-corrupt",
+ "test-corrupt", desc_4, 10*1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
+ {"/usr/lib/dbus-tests/test-suites/dbus-tests/test-dbus-daemon",
+ "test-dbus-daemon", desc_5, 15*1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
+ {"/usr/lib/dbus-tests/test-suites/dbus-tests/test-dbus-daemon-eavesdrop",
+ "test-dbus-daemon-eavesdrop", desc_6, 1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
+ {"/usr/lib/dbus-tests/test-suites/dbus-tests/test-fdpass",
+ "test-fdpass", desc_7, 3*1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
+ {"/usr/lib/dbus-tests/test-suites/dbus-tests/test-loopback",
+ "test-loopback", desc_8, 1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
+ {"/usr/lib/dbus-tests/test-suites/dbus-tests/test-marshal",
+ "test-marshal", desc_9, 1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
+ {"/usr/lib/dbus-tests/test-suites/dbus-tests/test-monitor",
+ "test-monitor", desc_10, 3*1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
+ {"/usr/lib/dbus-tests/test-suites/dbus-tests/test-printf",
+ "test-printf", desc_11, 2*1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
+ {"/usr/lib/dbus-tests/test-suites/dbus-tests/test-refs",
+ "test-refs", desc_12, 90*1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
+ {"/usr/lib/dbus-tests/test-suites/dbus-tests/test-relay",
+ "test-relay", desc_13, 6*1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
+ {"/usr/lib/dbus-tests/test-suites/dbus-tests/test-sd-activation",
+ "test-sd-activation", desc_14, 90*1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
+ {"/usr/lib/dbus-tests/test-suites/dbus-tests/test-shell",
+ "test-shell", desc_15, 1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
+ {"/usr/lib/dbus-tests/test-suites/dbus-tests/test-syntax",
+ "test-syntax", desc_16, 1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
+ {"/usr/lib/dbus-tests/test-suites/dbus-tests/test-syslog",
+ "test-syslog", desc_17, 1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL},
+ {"/usr/lib/dbus-tests/test-suites/dbus-tests/test-uid-permissions",
+ "test-uid-permissions", desc_18, 1000*1000, prepare_args_for_binary, parse_binary_outputs, init_environment_vars, NULL}
+};
+
+
+
+static const char result_pattern[] = "[r][%0]%1";
+static struct state {
+ unsigned int i; // index of input buffer
+ unsigned int j; // index in results[n]
+ unsigned int n; //index in results buffer (0 or 1)
+ char results[2][MAX_COMMENT];
+} g_state;
+
+static void sm_reset(void)
+{
+ memset(&g_state, 0, sizeof (g_state));
+}
+
+static int sm_update(char* buffer, int i)
+{
+ int l = strlen(buffer) + 1;
+ while (i < l && g_state.i < sizeof(result_pattern) - 1) {
+ if (result_pattern[g_state.i] == '%') {
+ g_state.n = result_pattern[g_state.i+1] - '0';
+ if (g_state.n > 1) {
+ sm_reset();
+ i--;
+ } else if (isalnum(buffer[i])) {
+ g_state.results[g_state.n][g_state.j++] = buffer[i];
+ } else if (buffer[i] == result_pattern[g_state.i+2] || buffer[i] == '\n') {
+ g_state.results[g_state.n][g_state.j] = 0;
+ g_state.i += 3;
+ g_state.j = 0;
+ if (g_state.n == 1)
+ return i;
+ } else {
+ g_state.i = 0;
+ g_state.j = 0;
+ }
+ } else if (result_pattern[g_state.i] == buffer[i]) {
+ g_state.i++;
+ } else {
+ sm_reset();
+ }
+ i++;
+ }
+
+ if (g_state.i >= sizeof(result_pattern) - 1) {
+ g_state.results[g_state.n][g_state.j] = 0;
+ g_state.i += 3;
+ g_state.j = 0;
+ if (g_state.n == 1)
+ return i;
+ }
+
+ return 0;
+}
+
+static const char* sm_get_result(int i)
+{
+ return g_state.results[i];
+}
+
+static char* args[3];
+char** prepare_args_for_binary(const struct binary* b, const char* test_name)
+{
+ args[0] = (char*)b->name;
+ if (!test_name[0])
+ args[1] = NULL;
+ else {
+ args[1] = (char*)test_name;
+ args[2] = NULL;
+ }
+ return args;
+}
+
+char** prepare_args_for_dir_iter(const struct binary* b, const char* test_name)
+{
+ static char* args_dir[2];
+ args_dir[0] = (char*)b->name;
+ args_dir[1] = "/usr/lib/dbus-tests/test-suites/dbus-tests/data";
+ return args_dir;
+}
+
+int init_environment_vars()
+{
+ return !(putenv("DBUS_TEST_DATA=/usr/lib/dbus-tests/test-suites/dbus-tests/data"));
+}
+
+void parse_binary_outputs(const struct binary* b, const char* test_name, char* buffer, int state_change, int state_option)
+{
+ char test_id[MAX_COMMENT];
+
+ switch(state_change) {
+ case INIT_TEST:
+ break;
+ case NEW_STDOUT:
+ buffer[state_option] = 0;
+ get_test_id(test_id, b, test_name);
+ fprintf(stderr, "[stdout][%s]%s\n",test_id, buffer);
+ break;
+ case NEW_STDERR:
+ buffer[state_option] = 0;
+ get_test_id(test_id, b, test_name);
+ fprintf(stderr, "[stderr][%s]%s\n",test_id, buffer);
+ break;
+ case RESULT_CODE:
+ get_test_id(test_id, b, test_name);
+ if (state_option != 0)
+ add_test_result(test_id, "FAIL", "", 0);
+ else if (state_option == 77)
+ add_test_result(test_id, "SKIP", "", 0);
+ else
+ add_test_result(test_id, "PASS", "", 1);
+ break;
+ case RESULT_SIGNAL:
+ get_test_id(test_id, b, test_name);
+ add_test_result(test_id, "FAIL", "Finished by SIGNAL", 0);
+ break;
+ case RESULT_TIMEOUT:
+ get_test_id(test_id, b, test_name);
+ add_test_result(test_id, "FAIL", "Test TIMEOUT", 0);
+ break;
+ }
+}
+
+static struct option long_options[] = {
+ {"list", no_argument, 0, 'l'},
+ {"run", required_argument, 0, 'r'},
+ {"description", required_argument, 0, 'd'},
+ {0, 0, 0, 0 }
+};
+
+static int stdin_pipe[2];
+static int stdout_pipe[2];
+static int stderr_pipe[2];
+static int gravedigger_pipe[2];
+static struct test_result test_results[MAX_TC_NUM];
+static int test_results_i;
+static char buffer[MAX_BUFFER];
+static const char* requested_tc[MAX_TC_NUM];
+
+char* get_test_id(char* dest, const struct binary* b, const char* test_name)
+{
+ int len = strlen(b->name);
+ memcpy(dest, b->name, len);
+ memcpy(dest + len, test_name, strlen(test_name)+1);
+ return dest;
+}
+
+static void print_description(const char* name, const char* description)
+{
+ printf("%s;%s\n",name, description);
+}
+
+static void print_list(const char* test_name)
+{
+ unsigned int i;
+ char full_name[MAX_COMMENT];
+ for (i = 0;i < sizeof(tests)/sizeof(struct binary); i++) {
+ int j = 0;
+ int l = strlen(tests[i].name);
+ memcpy(full_name, tests[i].name, l+1);
+ if (test_name && strncmp(test_name, full_name, l) != 0)
+ continue;
+
+ while (tests[i].test_cases[j].name) {
+ memcpy(full_name + l, tests[i].test_cases[j].name, strlen(tests[i].test_cases[j].name) + 1);
+ if (!test_name || strcmp(full_name, test_name) == 0)
+ print_description(full_name,tests[i].test_cases[j].description);
+ j++;
+ }
+ }
+}
+
+static void stop_binary(const struct binary* b, pid_t pid, const char* test_name, int w_res)
+{
+ int status = 0;
+ int res = 0;
+ if (w_res == 0)
+ res = waitpid(pid, &status, WNOHANG);
+ else
+ res = waitpid(pid, &status, 0);
+
+ if (res == 0) {
+ //timeouted
+ kill(pid, SIGKILL);
+ res = waitpid(pid, &status, WNOHANG);
+ b->parse(b, test_name, buffer, RESULT_TIMEOUT, res);
+ } else if (res < 0) {
+ //errno check
+ kill(pid, SIGKILL);
+ res = waitpid(pid, &status, WNOHANG);
+ b->parse(b, test_name, buffer, RESULT_ERROR, res);
+ } else if (res > 0) {
+ if (WIFEXITED(status)) {
+ b->parse(b, test_name, buffer, RESULT_CODE, WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ b->parse(b, test_name, buffer, RESULT_SIGNAL, WTERMSIG(status));
+ } else if (WIFSTOPPED(status)) {
+ b->parse(b, test_name, buffer, RESULT_SIGNAL, WSTOPSIG(status));
+ } else if (WIFCONTINUED(status)) {
+ kill(pid, SIGKILL);
+ b->parse(b, test_name, buffer, RESULT_SIGNAL, -1);
+ }
+ }
+}
+
+
+static void parse_output_with_timeout(const struct binary* b, pid_t pid, const char* test_name)
+{
+ struct timeval tv;
+ fd_set rfds;
+ int nfds;
+ int res;
+ int w_res = 0;
+ tv.tv_sec = b->timeout/(1000*1000);
+ tv.tv_usec = (b->timeout-tv.tv_sec*1000*1000);
+ while (1) {
+ FD_ZERO(&rfds);
+ if (stdout_pipe[PIPE_READ] > -1) {
+ assert(stdout_pipe[PIPE_READ] > -1);
+ assert(stdout_pipe[PIPE_READ] < 1024);
+ FD_SET(stdout_pipe[PIPE_READ], &rfds);
+ }
+ if (stderr_pipe[PIPE_READ] > -1) {
+ assert(stderr_pipe[PIPE_READ] > -1);
+ assert(stderr_pipe[PIPE_READ] < 1024);
+ FD_SET(stderr_pipe[PIPE_READ], &rfds);
+ }
+ FD_SET(gravedigger_pipe[PIPE_READ], &rfds);
+
+ nfds = select(FD_SETSIZE, &rfds, NULL, NULL, &tv);
+ if (nfds == -1) {
+ if (errno != EINTR) {
+ w_res = 0;
+ break;
+ }
+ } else if (nfds > 0) {
+ if (stdout_pipe[PIPE_READ] > -1 && FD_ISSET(stdout_pipe[PIPE_READ], &rfds)) {
+ res = read(stdout_pipe[PIPE_READ], buffer, MAX_BUFFER-1);
+ if (res == 0 || (res < 0 && errno != EINTR)) {
+ close (stdout_pipe[PIPE_READ]);
+ stdout_pipe[PIPE_READ] = -1;
+ continue;
+ } else if (res >=0) {
+ b->parse(b, test_name, buffer, NEW_STDOUT, res);
+ }
+ }
+
+ if (stderr_pipe[PIPE_READ] > -1 && FD_ISSET(stderr_pipe[PIPE_READ], &rfds)) {
+ res = read(stderr_pipe[PIPE_READ], buffer, MAX_BUFFER-1);
+ if (res == 0 || (res < 0 && errno != EINTR)) {
+ close (stderr_pipe[PIPE_READ]);
+ stderr_pipe[PIPE_READ] = -1;
+ continue;
+ }
+ b->parse(b, test_name, buffer, NEW_STDERR, res);
+ }
+
+ if (FD_ISSET(gravedigger_pipe[PIPE_READ], &rfds)) {
+ w_res = 1;
+ break; //it has ended
+ }
+ } else {
+ //timeout
+ w_res = 0;
+ break;
+ }
+ }
+ stop_binary(b, pid, test_name, w_res);
+}
+
+static int create_child(const char* path, char* const arguments[])
+{
+ int child;
+ int nResult;
+ if (pipe(gravedigger_pipe) < 0) {
+ perror("allocating pipe for gravedigger failed");
+ goto error1;
+ }
+
+ if (pipe(stdin_pipe) < 0) {
+ perror("allocating pipe for child input redirect failed");
+ goto error1;
+ }
+
+ if (pipe(stdout_pipe) < 0) {
+ perror("allocating pipe for child output redirect failed");
+ goto error2;
+ }
+
+ if (pipe(stderr_pipe) < 0) {
+ perror("allocating pipe for child output redirect failed");
+ goto error3;
+ }
+
+ child = fork();
+ if (!child) {
+ char ld_path[512];
+ sprintf(ld_path, "/usr/lib/dbus-tests/lib/libdbuspolicy-tests/:");
+ // redirect stdin
+ if (dup2(stdin_pipe[PIPE_READ], STDIN_FILENO) == -1) {
+ perror("redirecting stdin failed");
+ return -1;
+ }
+
+ // redirect stdout
+ if (dup2(stdout_pipe[PIPE_WRITE], STDOUT_FILENO) == -1) {
+ perror("redirecting stdout failed");
+ return -1;
+ }
+
+ // redirect stderr
+ if (dup2(stderr_pipe[PIPE_WRITE], STDERR_FILENO) == -1) {
+ perror("redirecting stderr failed");
+ return -1;
+ }
+
+ // all these are for use by parent only
+ close(stdin_pipe[PIPE_READ]);
+ close(stdin_pipe[PIPE_WRITE]);
+ close(stdout_pipe[PIPE_READ]);
+ close(stdout_pipe[PIPE_WRITE]);
+ close(stderr_pipe[PIPE_READ]);
+ close(stderr_pipe[PIPE_WRITE]);
+ close(gravedigger_pipe[PIPE_READ]);
+
+ char* ld_path_b = getenv("LD_LIBRARY_PATH");
+ if (ld_path_b != NULL)
+ memcpy(ld_path + strlen(ld_path), ld_path_b, strlen(ld_path_b)+1);
+ setenv("LD_LIBRARY_PATH", ld_path, 1);
+ // run child process image
+ nResult = execv(path, arguments);
+
+ // if we get here at all, an error occurred, but we are in the child
+ // process, so just exit
+ perror("exec of the child process failed");
+ exit(nResult);
+ } else if (child > 0) {
+ // parent continues here
+
+ // close unused file descriptors, these are for child only
+ close(stdin_pipe[PIPE_READ]);
+ close(stdout_pipe[PIPE_WRITE]);
+ close(stderr_pipe[PIPE_WRITE]);
+ close(gravedigger_pipe[PIPE_WRITE]);
+ } else {
+ // failed to create child
+ goto error4;
+ }
+
+ return child;
+
+error4:
+ close(stderr_pipe[PIPE_READ]);
+ close(stderr_pipe[PIPE_WRITE]);
+error3:
+ close(stdout_pipe[PIPE_READ]);
+ close(stdout_pipe[PIPE_WRITE]);
+error2:
+ close(stdin_pipe[PIPE_READ]);
+ close(stdin_pipe[PIPE_WRITE]);
+error1:
+ return -1;
+}
+
+static void run_test(const struct binary* b, const char* test_name)
+{
+ int res = -1;
+ char** arg;
+ char test_id[MAX_COMMENT];
+
+ assert(b);
+ assert(b->name);
+ assert(b->path);
+ assert(test_name);
+
+ arg = b->prepare_args(b, test_name);
+
+ if (b->init)
+ if (!b->init()) {
+ add_test_result(get_test_id(test_id, b, test_name), "ERROR", "Cannot init test", 0);
+ return;
+ }
+
+ res = create_child(b->path, arg);
+ if (res > 0)
+ parse_output_with_timeout(b, res, test_name);
+ else
+ add_test_result(get_test_id(test_id, b, test_name), "ERROR", "Cannot start test", 0);
+
+ if (b->clean)
+ b->clean();
+}
+
+static void parse_run_test(const char* tc) {
+ unsigned int i = 0;
+ for (i = 0;i < sizeof(tests)/sizeof(struct binary); i++) {
+ int len = strlen(tests[i].name);
+ if (strncmp(tc, tests[i].name, len) == 0) {
+ if (tc[len] == '*' || tc[len] == '\0')
+ run_test(&tests[i], "");
+ else
+ run_test(&tests[i], tc + len);
+ }
+ }
+}
+
+static int parse_option(int argc, char* argv[])
+{
+ int ch = 0;
+ int c = 0;
+ while ((ch = getopt_long(argc, argv, "lr:d:", long_options, NULL)) != -1) {
+ switch (ch) {
+ case 'l':
+ print_list(NULL);
+ return 1;
+ case 'r':
+ if (c >= MAX_TC_NUM - 1) //NULL at the end
+ return 0;
+
+ if (optarg)
+ requested_tc[c++] = optarg;
+
+ break;
+ case 'd':
+ print_list(optarg);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void add_test_result(const char* test_id, const char* result, const char* comment, int res)
+{
+ test_results[test_results_i].is_positive = res;
+ strcpy(test_results[test_results_i].result, result);
+ strcpy(test_results[test_results_i].comment, comment);
+ strcpy(test_results[test_results_i++].name, test_id);
+}
+
+static void print_results()
+{
+ int i = 0;
+ for (i = 0; i < test_results_i; i++)
+ {
+ printf("%s;%s;%s\n", test_results[i].name, test_results[i].result, test_results[i].comment);
+ }
+}
+
+int main(int argc, char* argv[])
+{
+ unsigned int i;
+ signal(SIGPIPE, SIG_IGN);
+ if (parse_option(argc, argv))
+ return 0;
+
+ if (!requested_tc[0]) {
+ for (i = 0;i < sizeof(tests)/sizeof(struct binary); i++)
+ run_test(&tests[i], "");
+ } else {
+ i = 0;
+ while(requested_tc[i]) {
+ parse_run_test(requested_tc[i]);
+ i++;
+ }
+ }
+
+ print_results();
+ return 0;
+}