summaryrefslogtreecommitdiff
path: root/tools/zunitc/src
diff options
context:
space:
mode:
Diffstat (limited to 'tools/zunitc/src')
-rw-r--r--tools/zunitc/src/main.c58
-rw-r--r--tools/zunitc/src/zuc_base_logger.c404
-rw-r--r--tools/zunitc/src/zuc_base_logger.h38
-rw-r--r--tools/zunitc/src/zuc_collector.c427
-rw-r--r--tools/zunitc/src/zuc_collector.h58
-rw-r--r--tools/zunitc/src/zuc_context.h58
-rw-r--r--tools/zunitc/src/zuc_event.h86
-rw-r--r--tools/zunitc/src/zuc_event_listener.h174
-rw-r--r--tools/zunitc/src/zuc_types.h80
-rw-r--r--tools/zunitc/src/zunitc_impl.c1572
10 files changed, 2955 insertions, 0 deletions
diff --git a/tools/zunitc/src/main.c b/tools/zunitc/src/main.c
new file mode 100644
index 00000000..81798071
--- /dev/null
+++ b/tools/zunitc/src/main.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "config.h"
+
+/*
+ * Common main() for test programs.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "zunitc/zunitc.h"
+
+int
+main(int argc, char* argv[])
+{
+ bool helped = false;
+ int rc = zuc_initialize(&argc, argv, &helped);
+
+ if ((rc == EXIT_SUCCESS) && !helped) {
+ /* Stop if any unrecognized parameters were encountered. */
+ if (argc > 1) {
+ printf("%s: unrecognized option '%s'\n",
+ argv[0], argv[1]);
+ printf("Try '%s --help' for more information.\n",
+ argv[0]);
+ rc = EXIT_FAILURE;
+ } else {
+ rc = ZUC_RUN_TESTS();
+ }
+ }
+
+ zuc_cleanup();
+ return rc;
+}
diff --git a/tools/zunitc/src/zuc_base_logger.c b/tools/zunitc/src/zuc_base_logger.c
new file mode 100644
index 00000000..1beb60d2
--- /dev/null
+++ b/tools/zunitc/src/zuc_base_logger.c
@@ -0,0 +1,404 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "zuc_base_logger.h"
+
+#include <memory.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "zuc_event_listener.h"
+#include "zuc_types.h"
+
+#include "shared/zalloc.h"
+
+/* a few sequences for rudimentary ANSI graphics. */
+#define CSI_GRN "\x1b[0;32m"
+#define CSI_RED "\x1b[0;31m"
+#define CSI_YLW "\x1b[0;33m"
+#define CSI_RST "\x1b[m"
+
+/**
+ * Logical mappings of style levels.
+ */
+enum style_level {
+ STYLE_GOOD,
+ STYLE_WARN,
+ STYLE_BAD
+};
+
+/**
+ * Structure for internal context.
+ */
+struct base_data {
+ bool use_color;
+};
+
+/**
+ * Prints a formatted string with optional ANSI coloring.
+ *
+ * @param use_color true to colorize the output, false to output normally.
+ * @param slevel the logical type to color for.
+ * @param fmt the format string to print with.
+ */
+static void
+styled_printf(bool use_color, enum style_level slevel, const char *fmt, ...);
+
+static void
+destroy(void *data);
+
+static void
+pre_run(void *data, int pass_count, int pass_num, int seed, const char *filter);
+
+static void
+run_started(void *data, int live_case_count,
+ int live_test_count, int disabled_count);
+
+static void
+run_ended(void *data, int case_count, struct zuc_case **cases,
+ int live_case_count, int live_test_count, int total_passed,
+ int total_failed, int total_disabled, long total_elapsed);
+
+static void
+case_started(void *data, struct zuc_case *test_case, int live_test_count,
+ int disabled_count);
+
+static void
+case_ended(void *data, struct zuc_case *test_case);
+
+static void
+test_started(void *data, struct zuc_test *test);
+
+static void
+test_ended(void *data, struct zuc_test *test);
+
+static void
+check_triggered(void *data, char const *file, int line,
+ enum zuc_fail_state state, enum zuc_check_op op,
+ enum zuc_check_valtype valtype,
+ intptr_t val1, intptr_t val2,
+ const char *expr1, const char *expr2);
+
+struct zuc_event_listener *
+zuc_base_logger_create(void)
+{
+ struct zuc_event_listener *listener =
+ zalloc(sizeof(struct zuc_event_listener));
+
+ listener->data = zalloc(sizeof(struct base_data));
+ listener->destroy = destroy;
+ listener->pre_run = pre_run;
+ listener->run_started = run_started;
+ listener->run_ended = run_ended;
+ listener->case_started = case_started;
+ listener->case_ended = case_ended;
+ listener->test_started = test_started;
+ listener->test_ended = test_ended;
+ listener->check_triggered = check_triggered;
+
+ return listener;
+}
+
+void
+styled_printf(bool use_color, enum style_level slevel, const char *fmt, ...)
+{
+ va_list argp;
+
+ if (use_color)
+ switch (slevel) {
+ case STYLE_GOOD:
+ printf(CSI_GRN);
+ break;
+ case STYLE_WARN:
+ printf(CSI_YLW);
+ break;
+ case STYLE_BAD:
+ printf(CSI_RED);
+ break;
+ default:
+ break;
+ }
+
+ va_start(argp, fmt);
+ vprintf(fmt, argp);
+ va_end(argp);
+
+ if (use_color)
+ printf(CSI_RST);
+}
+
+void
+destroy(void *data)
+{
+ free(data);
+}
+
+void
+pre_run(void *data, int pass_count, int pass_num, int seed, const char *filter)
+{
+ struct base_data *bdata = data;
+
+ bdata->use_color = isatty(fileno(stdout))
+ && getenv("TERM") && strcmp(getenv("TERM"), "dumb");
+
+ if (pass_count > 1)
+ printf("\nRepeating all tests (iteration %d) . . .\n\n",
+ pass_num);
+
+ if (filter && filter[0])
+ styled_printf(bdata->use_color, STYLE_WARN,
+ "Note: test filter = %s\n",
+ filter);
+
+ if (seed > 0)
+ styled_printf(bdata->use_color, STYLE_WARN,
+ "Note: Randomizing tests' orders"
+ " with a seed of %u .\n",
+ seed);
+}
+
+void
+run_started(void *data, int live_case_count,
+ int live_test_count, int disabled_count)
+{
+ struct base_data *bdata = data;
+
+ styled_printf(bdata->use_color, STYLE_GOOD, "[==========]");
+ printf(" Running %d %s from %d test %s.\n",
+ live_test_count,
+ (live_test_count == 1) ? "test" : "tests",
+ live_case_count,
+ (live_case_count == 1) ? "case" : "cases");
+}
+
+void
+run_ended(void *data, int case_count, struct zuc_case **cases,
+ int live_case_count, int live_test_count, int total_passed,
+ int total_failed, int total_disabled, long total_elapsed)
+{
+ struct base_data *bdata = data;
+ styled_printf(bdata->use_color, STYLE_GOOD, "[==========]");
+ printf(" %d %s from %d test %s ran. (%ld ms)\n",
+ live_test_count,
+ (live_test_count == 1) ? "test" : "tests",
+ live_case_count,
+ (live_case_count == 1) ? "case" : "cases",
+ total_elapsed);
+
+ if (total_passed) {
+ styled_printf(bdata->use_color, STYLE_GOOD, "[ PASSED ]");
+ printf(" %d %s.\n", total_passed,
+ (total_passed == 1) ? "test" : "tests");
+ }
+
+ if (total_failed) {
+ int case_num;
+ styled_printf(bdata->use_color, STYLE_BAD, "[ FAILED ]");
+ printf(" %d %s, listed below:\n",
+ total_failed, (total_failed == 1) ? "test" : "tests");
+
+ for (case_num = 0; case_num < case_count; ++case_num) {
+ int i;
+ for (i = 0; i < cases[case_num]->test_count; ++i) {
+ struct zuc_test *curr =
+ cases[case_num]->tests[i];
+ if (curr->failed || curr->fatal) {
+ styled_printf(bdata->use_color,
+ STYLE_BAD,
+ "[ FAILED ]");
+ printf(" %s.%s\n",
+ cases[case_num]->name,
+ curr->name);
+ }
+ }
+ }
+ }
+
+ if (total_failed || total_disabled)
+ printf("\n");
+
+ if (total_failed)
+ printf(" %d FAILED %s\n",
+ total_failed,
+ (total_failed == 1) ? "TEST" : "TESTS");
+
+ if (total_disabled)
+ styled_printf(bdata->use_color, STYLE_WARN,
+ " YOU HAVE %d DISABLED %s\n",
+ total_disabled,
+ (total_disabled == 1) ? "TEST" : "TESTS");
+}
+
+void
+case_started(void *data, struct zuc_case *test_case, int live_test_count,
+ int disabled_count)
+{
+ struct base_data *bdata = data;
+ styled_printf(bdata->use_color, STYLE_GOOD, "[----------]");
+ printf(" %d %s from %s.\n",
+ live_test_count,
+ (live_test_count == 1) ? "test" : "tests",
+ test_case->name);
+
+}
+
+void
+case_ended(void *data, struct zuc_case *test_case)
+{
+ struct base_data *bdata = data;
+ styled_printf(bdata->use_color, STYLE_GOOD, "[----------]");
+ printf(" %d %s from %s (%ld ms)\n",
+ test_case->test_count,
+ (test_case->test_count == 1) ? "test" : "tests",
+ test_case->name,
+ test_case->elapsed);
+ printf("\n");
+}
+
+void
+test_started(void *data, struct zuc_test *test)
+{
+ struct base_data *bdata = data;
+ styled_printf(bdata->use_color, STYLE_GOOD, "[ RUN ]");
+ printf(" %s.%s\n", test->test_case->name, test->name);
+}
+
+void
+test_ended(void *data, struct zuc_test *test)
+{
+ struct base_data *bdata = data;
+ if (test->failed || test->fatal) {
+ styled_printf(bdata->use_color, STYLE_BAD, "[ FAILED ]");
+ printf(" %s.%s (%ld ms)\n",
+ test->test_case->name, test->name, test->elapsed);
+ } else {
+ styled_printf(bdata->use_color, STYLE_GOOD, "[ OK ]");
+ printf(" %s.%s (%ld ms)\n",
+ test->test_case->name, test->name, test->elapsed);
+ }
+}
+
+const char *
+zuc_get_opstr(enum zuc_check_op op)
+{
+ switch (op) {
+ case ZUC_OP_EQ:
+ return "=";
+ break;
+ case ZUC_OP_NE:
+ return "!=";
+ break;
+ case ZUC_OP_GE:
+ return ">=";
+ break;
+ case ZUC_OP_GT:
+ return ">";
+ break;
+ case ZUC_OP_LE:
+ return "<=";
+ break;
+ case ZUC_OP_LT:
+ return "<";
+ break;
+ default:
+ return "???";
+ }
+}
+
+void
+check_triggered(void *data, char const *file, int line,
+ enum zuc_fail_state state, enum zuc_check_op op,
+ enum zuc_check_valtype valtype,
+ intptr_t val1, intptr_t val2,
+ const char *expr1, const char *expr2)
+{
+ switch (op) {
+ case ZUC_OP_TRUE:
+ printf("%s:%d: error: Value of: %s\n", file, line, expr1);
+ printf(" Actual: false\n");
+ printf("Expected: true\n");
+ break;
+ case ZUC_OP_FALSE:
+ printf("%s:%d: error: Value of: %s\n", file, line, expr1);
+ printf(" Actual: true\n");
+ printf("Expected: false\n");
+ break;
+ case ZUC_OP_NULL:
+ printf("%s:%d: error: Value of: %s\n", file, line, expr1);
+ printf(" Actual: %p\n", (void *)val1);
+ printf("Expected: %p\n", NULL);
+ break;
+ case ZUC_OP_NOT_NULL:
+ printf("%s:%d: error: Value of: %s\n", file, line, expr1);
+ printf(" Actual: %p\n", (void *)val1);
+ printf("Expected: not %p\n", NULL);
+ break;
+ case ZUC_OP_EQ:
+ if (valtype == ZUC_VAL_CSTR) {
+ printf("%s:%d: error: Value of: %s\n", file, line,
+ expr2);
+ printf(" Actual: %s\n", (const char *)val2);
+ printf("Expected: %s\n", expr1);
+ printf("Which is: %s\n", (const char *)val1);
+ } else {
+ printf("%s:%d: error: Value of: %s\n", file, line,
+ expr2);
+ printf(" Actual: %ld\n", val2);
+ printf("Expected: %s\n", expr1);
+ printf("Which is: %ld\n", val1);
+ }
+ break;
+ case ZUC_OP_NE:
+ if (valtype == ZUC_VAL_CSTR) {
+ printf("%s:%d: error: ", file, line);
+ printf("Expected: (%s) %s (%s), actual: %s == %s\n",
+ expr1, zuc_get_opstr(op), expr2,
+ (char *)val1, (char *)val2);
+ } else {
+ printf("%s:%d: error: ", file, line);
+ printf("Expected: (%s) %s (%s), actual: %ld vs %ld\n",
+ expr1, zuc_get_opstr(op), expr2, val1, val2);
+ }
+ break;
+ case ZUC_OP_TERMINATE: {
+ char const *level = (val1 == 0) ? "error"
+ : (val1 == 1) ? "warning"
+ : "note";
+ printf("%s:%d: %s: %s\n", file, line, level, expr1);
+ break;
+ }
+ case ZUC_OP_TRACEPOINT:
+ printf("%s:%d: note: %s\n", file, line, expr1);
+ break;
+ default:
+ printf("%s:%d: error: ", file, line);
+ printf("Expected: (%s) %s (%s), actual: %ld vs %ld\n",
+ expr1, zuc_get_opstr(op), expr2, val1, val2);
+ }
+}
diff --git a/tools/zunitc/src/zuc_base_logger.h b/tools/zunitc/src/zuc_base_logger.h
new file mode 100644
index 00000000..80b108e4
--- /dev/null
+++ b/tools/zunitc/src/zuc_base_logger.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef ZUC_BASE_LOGGER_H
+#define ZUC_BASE_LOGGER_H
+
+struct zuc_event_listener;
+
+/**
+ * Creates a new logger that outputs data to console in the default
+ * format.
+ */
+struct zuc_event_listener *
+zuc_base_logger_create(void);
+
+#endif /* ZUC_BASE_LOGGER_H */
diff --git a/tools/zunitc/src/zuc_collector.c b/tools/zunitc/src/zuc_collector.c
new file mode 100644
index 00000000..e17231cd
--- /dev/null
+++ b/tools/zunitc/src/zuc_collector.c
@@ -0,0 +1,427 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "zuc_collector.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "shared/zalloc.h"
+#include "zuc_event_listener.h"
+#include "zunitc/zunitc_impl.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+/**
+ * @file
+ * General handling of collecting events during testing to pass back to
+ * main tracking of fork()'d tests.
+ *
+ * @note implementation of zuc_process_message() is included here so that
+ * all child->parent IPC is in a single source file for easier maintenance
+ * and updating.
+ */
+
+/**
+ * Internal data struct for processing.
+ */
+struct collector_data
+{
+ int *fd; /**< file descriptor to output to. */
+ struct zuc_test *test; /**< current test, or NULL. */
+};
+
+/**
+ * Stores an int32_t into the given buffer.
+ *
+ * @param ptr the buffer to store to.
+ * @param val the value to store.
+ * @return a pointer to the position in the buffer after the stored value.
+ */
+static char *
+pack_int32(char *ptr, int32_t val);
+
+/**
+ * Stores an intptr_t into the given buffer.
+ *
+ * @param ptr the buffer to store to.
+ * @param val the value to store.
+ * @return a pointer to the position in the buffer after the stored value.
+ */
+static char *
+pack_intptr_t(char *ptr, intptr_t val);
+
+/**
+ * Extracts a int32_t from the given buffer.
+ *
+ * @param ptr the buffer to extract from.
+ * @param val the value to set.
+ * @return a pointer to the position in the buffer after the extracted
+ * value.
+ */
+static char const *
+unpack_int32(char const *ptr, int32_t *val);
+
+/**
+ * Extracts a intptr_t from the given buffer.
+ *
+ * @param ptr the buffer to extract from.
+ * @param val the value to set.
+ * @return a pointer to the position in the buffer after the extracted
+ * value.
+ */
+static char const *
+unpack_intptr_t(char const *ptr, intptr_t *val);
+
+/**
+ * Extracts a length-prefixed string from the given buffer.
+ *
+ * @param ptr the buffer to extract from.
+ * @param str the value to set.
+ * @return a pointer to the position in the buffer after the extracted
+ * value.
+ */
+static char const *
+unpack_string(char const *ptr, char **str);
+
+/**
+ * Extracts an event from the given buffer.
+ *
+ * @param ptr the buffer to extract from.
+ * @param len the length of the given buffer.
+ * @return an event that was packed in the buffer
+ */
+static struct zuc_event *
+unpack_event(char const *ptr, int32_t len);
+
+/**
+ * Handles an event by either attaching it directly or sending it over IPC
+ * as needed.
+ */
+static void
+store_event(struct collector_data *cdata,
+ enum zuc_event_type event_type, char const *file, int line,
+ enum zuc_fail_state state, enum zuc_check_op op,
+ enum zuc_check_valtype valtype,
+ intptr_t val1, intptr_t val2, const char *expr1, const char *expr2);
+
+static void
+destroy(void *data);
+
+static void
+test_started(void *data, struct zuc_test *test);
+
+static void
+test_ended(void *data, struct zuc_test *test);
+
+static void
+check_triggered(void *data, char const *file, int line,
+ enum zuc_fail_state state, enum zuc_check_op op,
+ enum zuc_check_valtype valtype,
+ intptr_t val1, intptr_t val2,
+ const char *expr1, const char *expr2);
+
+static void
+collect_event(void *data, char const *file, int line, const char *expr1);
+
+struct zuc_event_listener *
+zuc_collector_create(int *pipe_fd)
+{
+ struct zuc_event_listener *listener =
+ zalloc(sizeof(struct zuc_event_listener));
+
+ listener->data = zalloc(sizeof(struct collector_data));
+ ((struct collector_data *)listener->data)->fd = pipe_fd;
+ listener->destroy = destroy;
+ listener->test_started = test_started;
+ listener->test_ended = test_ended;
+ listener->check_triggered = check_triggered;
+ listener->collect_event = collect_event;
+
+ return listener;
+}
+
+char *
+pack_int32(char *ptr, int32_t val)
+{
+ memcpy(ptr, &val, sizeof(val));
+ return ptr + sizeof(val);
+}
+
+char *
+pack_intptr_t(char *ptr, intptr_t val)
+{
+ memcpy(ptr, &val, sizeof(val));
+ return ptr + sizeof(val);
+}
+
+static char *
+pack_cstr(char *ptr, intptr_t val, int len)
+{
+ if (val == 0) { /* a null pointer */
+ ptr = pack_int32(ptr, -1);
+ } else {
+ ptr = pack_int32(ptr, len);
+ memcpy(ptr, (const char *)val, len);
+ ptr += len;
+ }
+ return ptr;
+}
+
+void
+destroy(void *data)
+{
+ free(data);
+}
+
+void
+test_started(void *data, struct zuc_test *test)
+{
+ struct collector_data *cdata = data;
+ cdata->test = test;
+}
+
+void
+test_ended(void *data, struct zuc_test *test)
+{
+ struct collector_data *cdata = data;
+ cdata->test = NULL;
+}
+
+void
+check_triggered(void *data, char const *file, int line,
+ enum zuc_fail_state state, enum zuc_check_op op,
+ enum zuc_check_valtype valtype,
+ intptr_t val1, intptr_t val2,
+ const char *expr1, const char *expr2)
+{
+ struct collector_data *cdata = data;
+ if (op != ZUC_OP_TRACEPOINT)
+ store_event(cdata, ZUC_EVENT_IMMEDIATE, file, line, state, op,
+ valtype,
+ val1, val2, expr1, expr2);
+}
+
+void
+collect_event(void *data, char const *file, int line, const char *expr1)
+{
+ struct collector_data *cdata = data;
+ store_event(cdata, ZUC_EVENT_DEFERRED, file, line, ZUC_CHECK_OK,
+ ZUC_OP_TRACEPOINT, ZUC_VAL_INT,
+ 0, 0, expr1, "");
+}
+
+void
+store_event(struct collector_data *cdata,
+ enum zuc_event_type event_type, char const *file, int line,
+ enum zuc_fail_state state, enum zuc_check_op op,
+ enum zuc_check_valtype valtype,
+ intptr_t val1, intptr_t val2, const char *expr1, const char *expr2)
+{
+ struct zuc_event *event = zalloc(sizeof(*event));
+ event->file = strdup(file);
+ event->line = line;
+ event->state = state;
+ event->op = op;
+ event->valtype = valtype;
+ event->val1 = val1;
+ event->val2 = val2;
+ if (valtype == ZUC_VAL_CSTR) {
+ if (val1)
+ event->val1 = (intptr_t)strdup((const char *)val1);
+ if (val2)
+ event->val2 = (intptr_t)strdup((const char *)val2);
+ }
+ event->expr1 = strdup(expr1);
+ event->expr2 = strdup(expr2);
+
+ zuc_attach_event(cdata->test, event, event_type, false);
+
+ if (*cdata->fd == -1) {
+ } else {
+ /* Need to pass it back */
+ int sent;
+ int count;
+ int expr1_len = strlen(expr1);
+ int expr2_len = strlen(expr2);
+ int val1_len =
+ ((valtype == ZUC_VAL_CSTR) && val1) ?
+ strlen((char *)val1) : 0;
+ int val2_len =
+ ((valtype == ZUC_VAL_CSTR) && val2) ?
+ strlen((char *)val2) : 0;
+ int file_len = strlen(file);
+ int len = (sizeof(int32_t) * 9) + file_len
+ + (sizeof(intptr_t) * 2)
+ + ((valtype == ZUC_VAL_CSTR) ?
+ (sizeof(int32_t) * 2) + val1_len + val2_len : 0)
+ + expr1_len + expr2_len;
+ char *buf = zalloc(len);
+
+ char *ptr = pack_int32(buf, len - 4);
+ ptr = pack_int32(ptr, event_type);
+ ptr = pack_int32(ptr, file_len);
+ memcpy(ptr, file, file_len);
+ ptr += file_len;
+ ptr = pack_int32(ptr, line);
+ ptr = pack_int32(ptr, state);
+ ptr = pack_int32(ptr, op);
+ ptr = pack_int32(ptr, valtype);
+ if (valtype == ZUC_VAL_CSTR) {
+ ptr = pack_cstr(ptr, val1, val1_len);
+ ptr = pack_cstr(ptr, val2, val2_len);
+ } else {
+ ptr = pack_intptr_t(ptr, val1);
+ ptr = pack_intptr_t(ptr, val2);
+ }
+
+ ptr = pack_int32(ptr, expr1_len);
+ if (expr1_len) {
+ memcpy(ptr, expr1, expr1_len);
+ ptr += expr1_len;
+ }
+ ptr = pack_int32(ptr, expr2_len);
+ if (expr2_len) {
+ memcpy(ptr, expr2, expr2_len);
+ ptr += expr2_len;
+ }
+
+
+ sent = 0;
+ while (sent < len) {
+ count = write(*cdata->fd, buf, len);
+ if (count == -1)
+ break;
+ sent += count;
+ }
+
+ free(buf);
+ }
+}
+
+char const *
+unpack_int32(char const *ptr, int32_t *val)
+{
+ memcpy(val, ptr, sizeof(*val));
+ return ptr + sizeof(*val);
+}
+
+char const *
+unpack_intptr_t(char const *ptr, intptr_t *val)
+{
+ memcpy(val, ptr, sizeof(*val));
+ return ptr + sizeof(*val);
+}
+
+char const *
+unpack_string(char const *ptr, char **str)
+{
+ int32_t len = 0;
+ ptr = unpack_int32(ptr, &len);
+ *str = zalloc(len + 1);
+ if (len)
+ memcpy(*str, ptr, len);
+ ptr += len;
+ return ptr;
+}
+
+static char const *
+unpack_cstr(char const *ptr, char **str)
+{
+ int32_t len = 0;
+ ptr = unpack_int32(ptr, &len);
+ if (len < 0) {
+ *str = NULL;
+ } else {
+ *str = zalloc(len + 1);
+ if (len)
+ memcpy(*str, ptr, len);
+ ptr += len;
+ }
+ return ptr;
+}
+
+struct zuc_event *
+unpack_event(char const *ptr, int32_t len)
+{
+ int32_t val = 0;
+ struct zuc_event *evt = zalloc(sizeof(*evt));
+ char const *tmp = unpack_string(ptr, &evt->file);
+ tmp = unpack_int32(tmp, &evt->line);
+
+ tmp = unpack_int32(tmp, &val);
+ evt->state = val;
+ tmp = unpack_int32(tmp, &val);
+ evt->op = val;
+ tmp = unpack_int32(tmp, &val);
+ evt->valtype = val;
+
+ if (evt->valtype == ZUC_VAL_CSTR) {
+ char *ptr = NULL;
+ tmp = unpack_cstr(tmp, &ptr);
+ evt->val1 = (intptr_t)ptr;
+ tmp = unpack_cstr(tmp, &ptr);
+ evt->val2 = (intptr_t)ptr;
+ } else {
+ tmp = unpack_intptr_t(tmp, &evt->val1);
+ tmp = unpack_intptr_t(tmp, &evt->val2);
+ }
+
+ tmp = unpack_string(tmp, &evt->expr1);
+ tmp = unpack_string(tmp, &evt->expr2);
+
+ return evt;
+}
+
+int
+zuc_process_message(struct zuc_test *test, int fd)
+{
+ char buf[4] = {};
+ int got = read(fd, buf, 4);
+ if (got == 4) {
+ enum zuc_event_type event_type = ZUC_EVENT_IMMEDIATE;
+ int32_t val = 0;
+ int32_t len = 0;
+ const char *tmp = NULL;
+ char *raw = NULL;
+ unpack_int32(buf, &len);
+ raw = zalloc(len);
+ got = read(fd, raw, len);
+
+ tmp = unpack_int32(raw, &val);
+ event_type = val;
+
+ struct zuc_event *evt = unpack_event(tmp, len - (tmp - raw));
+ zuc_attach_event(test, evt, event_type, true);
+ free(raw);
+ }
+ return got;
+}
diff --git a/tools/zunitc/src/zuc_collector.h b/tools/zunitc/src/zuc_collector.h
new file mode 100644
index 00000000..f21e670c
--- /dev/null
+++ b/tools/zunitc/src/zuc_collector.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef ZUC_COLLECTOR_H
+#define ZUC_COLLECTOR_H
+
+struct zuc_event_listener;
+struct zuc_test;
+
+/**
+ * Creates a new instance of an even collector that will attatch events
+ * to the current test directly or via connection from child to parent.
+ *
+ * @param pipe_fd pointer to the file descriptor to use for communication if
+ * needed. If the value is -1 the events will be attached directly to the
+ * current test. Otherwise events will be passed back via IPC over this
+ * pipe with the expectation that the payload will be handled in the parent
+ * process via zuc_process_message().
+ * @return a new collector intance.
+ * @see zuc_process_message()
+ */
+struct zuc_event_listener *
+zuc_collector_create(int *pipe_fd);
+
+/**
+ * Reads events from the given pipe and processes them.
+ *
+ * @param test the currently active test to attache events for.
+ * @param pipe_fd the file descriptor of the pipe to read from.
+ * @return a positive value if a message was received, 0 if the end has
+ * been reached and -1 if an error has occurred.
+ */
+int
+zuc_process_message(struct zuc_test *test, int pipe_fd);
+
+#endif /* ZUC_COLLECTOR_H */
diff --git a/tools/zunitc/src/zuc_context.h b/tools/zunitc/src/zuc_context.h
new file mode 100644
index 00000000..609f34bd
--- /dev/null
+++ b/tools/zunitc/src/zuc_context.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef ZUC_CONTEXT_H
+#define ZUC_CONTEXT_H
+
+#include "zuc_types.h"
+
+struct zuc_slinked;
+
+/**
+ * Internal context for processing.
+ * Collecting data members here minimizes use of globals.
+ */
+struct zuc_context {
+ int case_count;
+ struct zuc_case **cases;
+
+ bool fatal;
+ int repeat;
+ int random;
+ unsigned int seed;
+ bool spawn;
+ bool break_on_failure;
+ bool output_tap;
+ bool output_junit;
+ int fds[2];
+ char *filter;
+
+ struct zuc_slinked *listeners;
+
+ struct zuc_case *curr_case;
+ struct zuc_test *curr_test;
+};
+
+#endif /* ZUC_CONTEXT_H */
diff --git a/tools/zunitc/src/zuc_event.h b/tools/zunitc/src/zuc_event.h
new file mode 100644
index 00000000..ccb2f7b6
--- /dev/null
+++ b/tools/zunitc/src/zuc_event.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef ZUC_EVENT_H
+#define ZUC_EVENT_H
+
+#include <stdint.h>
+
+#include "zunitc/zunitc_impl.h"
+
+/**
+ *
+ */
+enum zuc_event_type
+{
+ ZUC_EVENT_IMMEDIATE,
+ ZUC_EVENT_DEFERRED
+};
+
+/**
+ * Status enum for posted events.
+ */
+enum zuc_fail_state
+{
+ ZUC_CHECK_OK, /**< no problem. */
+ ZUC_CHECK_SKIP, /**< runtime skip directive encountered. */
+ ZUC_CHECK_FAIL, /**< non-fatal check fails. */
+ ZUC_CHECK_FATAL, /**< fatal assertion/check fails. */
+ ZUC_CHECK_ERROR /**< internal level problem. */
+};
+
+/**
+ * Record of an event that occurs during testing such as assert macro
+ * failures.
+ */
+struct zuc_event
+{
+ char *file;
+ int32_t line;
+ enum zuc_fail_state state;
+ enum zuc_check_op op;
+ enum zuc_check_valtype valtype;
+ intptr_t val1;
+ intptr_t val2;
+ char *expr1;
+ char *expr2;
+
+ struct zuc_event *next;
+};
+
+/**
+ * Attaches an event to the specified test.
+ *
+ * @param test the test to attach to.
+ * @param event the event to attach.
+ * @param event_type of event (immediate or deferred) to attach.
+ * @param transferred true if the event has been processed elsewhere and
+ * is being transferred for storage, false otherwise.
+ */
+void
+zuc_attach_event(struct zuc_test *test, struct zuc_event *event,
+ enum zuc_event_type event_type, bool transferred);
+
+#endif /* ZUC_EVENT_H */
diff --git a/tools/zunitc/src/zuc_event_listener.h b/tools/zunitc/src/zuc_event_listener.h
new file mode 100644
index 00000000..41c5fbdf
--- /dev/null
+++ b/tools/zunitc/src/zuc_event_listener.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef ZUC_EVENT_HANDLER_H
+#define ZUC_EVENT_HANDLER_H
+
+#include <stdint.h>
+
+#include "zuc_context.h"
+#include "zuc_event.h"
+
+struct zuc_test;
+struct zuc_case;
+
+/**
+ * Interface to allow components to process testing events as they occur.
+ *
+ * Event listeners that will stream output as testing progresses are often
+ * named "*_logger" whereas those that produce their output upon test run
+ * completion are named "*_reporter".
+ */
+struct zuc_event_listener {
+ /**
+ * User data pointer.
+ */
+ void *data;
+
+ /**
+ * Destructor.
+ * @param data the user data associated with this instance.
+ */
+ void (*destroy)(void *data);
+
+ /**
+ * Handler for simple pre-run state.
+ *
+ * @param pass_count total number of expected test passes.
+ * @param pass_num current pass iteration number.
+ * @param seed random seed being used, or 0 for no randomization.
+ * @param filter filter string used for tests, or NULL/blank for none.
+ */
+ void (*pre_run)(void *data,
+ int pass_count,
+ int pass_num,
+ int seed,
+ const char *filter);
+
+ /**
+ * Handler for test runs starting.
+ *
+ * @param data the user data associated with this instance.
+ * @param live_case_count number of active test cases in this run.
+ * @param live_test_count number of active tests in this run.
+ * @param disabled_count number of disabled tests in this run.
+ */
+ void (*run_started)(void *data,
+ int live_case_count,
+ int live_test_count,
+ int disabled_count);
+
+ /**
+ * Handler for test runs ending.
+ *
+ * @param data the user data associated with this instance.
+ */
+ void (*run_ended)(void *data,
+ int case_count,
+ struct zuc_case** cases,
+ int live_case_count,
+ int live_test_count,
+ int total_passed,
+ int total_failed,
+ int total_disabled,
+ long total_elapsed);
+
+ /**
+ * Handler for test case starting.
+ *
+ * @param data the user data associated with this instance.
+ */
+ void (*case_started)(void *data,
+ struct zuc_case *test_case,
+ int live_test_count,
+ int disabled_count);
+
+ /**
+ * Handler for test case ending.
+ *
+ * @param data the user data associated with this instance.
+ */
+ void (*case_ended)(void *data,
+ struct zuc_case *test_case);
+
+ /**
+ * Handler for test starting.
+ *
+ * @param data the user data associated with this instance.
+ */
+ void (*test_started)(void *data,
+ struct zuc_test *test);
+
+ /**
+ * Handler for test ending.
+ *
+ * @param data the user data associated with this instance.
+ */
+ void (*test_ended)(void *data,
+ struct zuc_test *test);
+
+ /**
+ * Handler for disabled test notification.
+ *
+ * @param data the user data associated with this instance.
+ */
+ void (*test_disabled)(void *data,
+ struct zuc_test *test);
+
+ /**
+ * Handler for check/assertion fired due to failure, warning, etc.
+ *
+ * @param data the user data associated with this instance.
+ */
+ void (*check_triggered)(void *data,
+ char const *file,
+ int line,
+ enum zuc_fail_state state,
+ enum zuc_check_op op,
+ enum zuc_check_valtype valtype,
+ intptr_t val1,
+ intptr_t val2,
+ const char *expr1,
+ const char *expr2);
+
+ /**
+ * Handler for tracepoints and such that may be displayed later.
+ *
+ * @param data the user data associated with this instance.
+ */
+ void (*collect_event)(void *data,
+ char const *file,
+ int line,
+ const char *expr1);
+};
+
+/**
+ * Registers an event listener instance to be called.
+ */
+void
+zuc_add_event_listener(struct zuc_event_listener *event_listener);
+
+
+#endif /* ZUC_EVENT_HANDLER_H */
diff --git a/tools/zunitc/src/zuc_types.h b/tools/zunitc/src/zuc_types.h
new file mode 100644
index 00000000..4ed93427
--- /dev/null
+++ b/tools/zunitc/src/zuc_types.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef ZUC_TYPES_H
+#define ZUC_TYPES_H
+
+#include "zunitc/zunitc_impl.h"
+
+struct zuc_case;
+
+/**
+ * Represents a specific test.
+ */
+struct zuc_test
+{
+ int order;
+ struct zuc_case *test_case;
+ zucimpl_test_fn fn;
+ zucimpl_test_fn_f fn_f;
+ char *name;
+ int disabled;
+ int skipped;
+ int failed;
+ int fatal;
+ long elapsed;
+ struct zuc_event *events;
+ struct zuc_event *deferred;
+};
+
+/**
+ * Represents a test case that can hold a collection of tests.
+ */
+struct zuc_case
+{
+ int order;
+ char *name;
+ const struct zuc_fixture* fxt;
+ int disabled;
+ int skipped;
+ int failed;
+ int fatal;
+ int passed;
+ long elapsed;
+ int test_count;
+ struct zuc_test **tests;
+};
+
+/**
+ * Returns a human-readable version of the comparison opcode.
+ *
+ * @param op the opcode to get a string version of.
+ * @return a human-readable string of the opcode.
+ * (This value should not be freed)
+ */
+const char *
+zuc_get_opstr(enum zuc_check_op op);
+
+#endif /* ZUC_TYPES_H */
diff --git a/tools/zunitc/src/zunitc_impl.c b/tools/zunitc/src/zunitc_impl.c
new file mode 100644
index 00000000..b8ab0b2c
--- /dev/null
+++ b/tools/zunitc/src/zunitc_impl.c
@@ -0,0 +1,1572 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "zunitc/zunitc_impl.h"
+#include "zunitc/zunitc.h"
+
+#include "zuc_base_logger.h"
+#include "zuc_collector.h"
+#include "zuc_context.h"
+#include "zuc_event_listener.h"
+
+#include "shared/config-parser.h"
+#include "shared/helpers.h"
+#include "shared/zalloc.h"
+
+/*
+ * If CLOCK_MONOTONIC is present on the system it will give us reliable
+ * results under certain edge conditions that normally require manual
+ * admin actions to trigger. If not, CLOCK_REALTIME is a reasonable
+ * fallback.
+ */
+#if _POSIX_MONOTONIC_CLOCK
+static const clockid_t TARGET_TIMER = CLOCK_MONOTONIC;
+#else
+static const clockid_t TARGET_TIMER = CLOCK_REALTIME;
+#endif
+
+static char const DISABLED_PREFIX[] = "DISABLED_";
+
+#define MS_PER_SEC 1000L
+#define NANO_PER_MS 1000000L
+
+/**
+ * Simple single-linked list structure.
+ */
+struct zuc_slinked {
+ void *data;
+ struct zuc_slinked *next;
+};
+
+static struct zuc_context g_ctx = {
+ .case_count = 0,
+ .cases = NULL,
+
+ .fatal = false,
+ .repeat = 0,
+ .random = 0,
+ .spawn = true,
+ .break_on_failure = false,
+ .fds = {-1, -1},
+
+ .listeners = NULL,
+
+ .curr_case = NULL,
+ .curr_test = NULL,
+};
+
+static char *g_progname = NULL;
+static char *g_progbasename = NULL;
+
+typedef int (*comp_pred2)(intptr_t lhs, intptr_t rhs);
+
+static bool
+test_has_skip(struct zuc_test *test)
+{
+ return test->skipped;
+}
+
+static bool
+test_has_failure(struct zuc_test *test)
+{
+ return test->fatal || test->failed;
+}
+
+bool
+zuc_has_skip(void)
+{
+ return g_ctx.curr_test ?
+ test_has_skip(g_ctx.curr_test) : false;
+}
+
+bool
+zuc_has_failure(void)
+{
+ return g_ctx.curr_test ?
+ test_has_failure(g_ctx.curr_test) : false;
+}
+
+void
+zuc_set_filter(const char *filter)
+{
+ g_ctx.filter = strdup(filter);
+}
+
+void
+zuc_set_repeat(int repeat)
+{
+ g_ctx.repeat = repeat;
+}
+
+void
+zuc_set_random(int random)
+{
+ g_ctx.random = random;
+}
+
+void
+zuc_set_spawn(bool spawn)
+{
+ g_ctx.spawn = spawn;
+}
+
+void
+zuc_set_break_on_failure(bool break_on_failure)
+{
+ g_ctx.break_on_failure = break_on_failure;
+}
+
+const char *
+zuc_get_program_name(void)
+{
+ return g_progname;
+}
+
+const char *
+zuc_get_program_basename(void)
+{
+ return g_progbasename;
+}
+
+static struct zuc_test *
+create_test(int order, zucimpl_test_fn fn, zucimpl_test_fn_f fn_f,
+ char const *case_name, char const *test_name,
+ struct zuc_case *parent)
+{
+ struct zuc_test *test = zalloc(sizeof(struct zuc_test));
+ ZUC_ASSERTG_NOT_NULL(test, out);
+ test->order = order;
+ test->fn = fn;
+ test->fn_f = fn_f;
+ test->name = strdup(test_name);
+ if ((!fn && !fn_f) ||
+ (strncmp(DISABLED_PREFIX,
+ test_name, sizeof(DISABLED_PREFIX) - 1) == 0))
+ test->disabled = 1;
+
+ test->test_case = parent;
+
+out:
+ return test;
+}
+
+static int
+compare_regs(const void *lhs, const void *rhs)
+{
+ int rc = strcmp((*(struct zuc_registration **)lhs)->tcase,
+ (*(struct zuc_registration **)rhs)->tcase);
+ if (rc == 0)
+ rc = strcmp((*(struct zuc_registration **)lhs)->test,
+ (*(struct zuc_registration **)rhs)->test);
+
+ return rc;
+}
+
+/* gcc-specific markers for auto test case registration: */
+extern const struct zuc_registration __start_zuc_tsect;
+extern const struct zuc_registration __stop_zuc_tsect;
+
+static void
+register_tests(void)
+{
+ size_t case_count = 0;
+ size_t count = &__stop_zuc_tsect - &__start_zuc_tsect;
+ size_t i;
+ int idx = 0;
+ const char *last_name = NULL;
+ void **array = zalloc(sizeof(void *) * count);
+ ZUC_ASSERT_NOT_NULL(array);
+ for (i = 0; i < count; ++i)
+ array[i] = (void *)(&__start_zuc_tsect + i);
+
+ qsort(array, count, sizeof(array[0]), compare_regs);
+
+ /* Count transitions to get number of test cases. */
+ last_name = NULL;
+ for (i = 0; i < count; ++i) {
+ const struct zuc_registration *reg =
+ (const struct zuc_registration *)array[i];
+ if (!last_name || (strcmp(last_name, reg->tcase))) {
+ last_name = reg->tcase;
+ case_count++;
+ }
+ }
+
+ /* Create test case data items. */
+ struct zuc_case **case_array =
+ zalloc(sizeof(struct zuc_case *) * case_count);
+ ZUC_ASSERT_NOT_NULL(case_array);
+ struct zuc_case *last_case = NULL;
+ size_t case_num = 0;
+ for (i = 0; i < count; ++i) {
+ const struct zuc_registration *reg =
+ (const struct zuc_registration *)array[i];
+ if (!last_case || (strcmp(last_case->name, reg->tcase))) {
+ last_case = zalloc(sizeof(struct zuc_case));
+ ZUC_ASSERT_NOT_NULL(last_case);
+ last_case->order = count;
+ last_case->name = strdup(reg->tcase);
+ last_case->fxt = reg->fxt;
+ last_case->test_count = i;
+ if (case_num > 0) {
+ int tcount = i
+ - case_array[case_num - 1]->test_count;
+ case_array[case_num - 1]->test_count = tcount;
+ }
+ case_array[case_num++] = last_case;
+ }
+ }
+ case_array[case_count - 1]->test_count = count
+ - case_array[case_count - 1]->test_count;
+
+ /* Reserve space for tests per test case. */
+ for (case_num = 0; case_num < case_count; ++case_num) {
+ case_array[case_num]->tests =
+ zalloc(case_array[case_num]->test_count
+ * sizeof(struct zuc_test *));
+ ZUC_ASSERT_NOT_NULL(case_array[case_num]->tests);
+ }
+
+ last_name = NULL;
+ case_num = -1;
+ for (i = 0; i < count; ++i) {
+ const struct zuc_registration *reg =
+ (const struct zuc_registration *)array[i];
+ int order = count - (1 + (reg - &__start_zuc_tsect));
+
+ if (!last_name || (strcmp(last_name, reg->tcase))) {
+ last_name = reg->tcase;
+ case_num++;
+ idx = 0;
+ }
+ if (order < case_array[case_num]->order)
+ case_array[case_num]->order = order;
+ case_array[case_num]->tests[idx] =
+ create_test(order, reg->fn, reg->fn_f,
+ reg->tcase, reg->test,
+ case_array[case_num]);
+
+ if (case_array[case_num]->fxt != reg->fxt)
+ printf("%s:%d: error: Mismatched fixtures for '%s'\n",
+ __FILE__, __LINE__, case_array[case_num]->name);
+
+ idx++;
+ }
+ free(array);
+
+ g_ctx.case_count = case_count;
+ g_ctx.cases = case_array;
+}
+
+static int
+compare_case_order(const void *lhs, const void *rhs)
+{
+ return (*(struct zuc_case **)lhs)->order
+ - (*(struct zuc_case **)rhs)->order;
+}
+
+static int
+compare_test_order(const void *lhs, const void *rhs)
+{
+ return (*(struct zuc_test **)lhs)->order
+ - (*(struct zuc_test **)rhs)->order;
+}
+
+static void
+order_cases(int count, struct zuc_case **cases)
+{
+ int i;
+ qsort(cases, count, sizeof(*cases), compare_case_order);
+ for (i = 0; i < count; ++i) {
+ qsort(cases[i]->tests, cases[i]->test_count,
+ sizeof(*cases[i]->tests), compare_test_order);
+ }
+}
+
+static void
+free_events(struct zuc_event **events)
+{
+ struct zuc_event *evt = *events;
+ *events = NULL;
+ while (evt) {
+ struct zuc_event *old = evt;
+ evt = evt->next;
+ free(old->file);
+ if (old->valtype == ZUC_VAL_CSTR) {
+ free((void *)old->val1);
+ free((void *)old->val2);
+ }
+ free(old->expr1);
+ free(old->expr2);
+ free(old);
+ }
+}
+
+static void
+free_test(struct zuc_test *test)
+{
+ free(test->name);
+ free_events(&test->events);
+ free_events(&test->deferred);
+ free(test);
+}
+
+static void
+free_test_case(struct zuc_case *test_case)
+{
+ int i;
+ free(test_case->name);
+ for (i = test_case->test_count - 1; i >= 0; --i) {
+ free_test(test_case->tests[i]);
+ test_case->tests[i] = NULL;
+ }
+ free(test_case->tests);
+ free(test_case);
+}
+
+/**
+ * A very simple matching that is compatible with the algorithm used in
+ * Google Test.
+ *
+ * @param wildcard sequence of '?', '*' or normal characters to match.
+ * @param str string to check for matching.
+ * @return true if the wildcard matches the entire string, false otherwise.
+ */
+static bool
+wildcard_matches(char const *wildcard, char const *str)
+{
+ switch (*wildcard) {
+ case '\0':
+ return !*str;
+ case '?':
+ return *str && wildcard_matches(wildcard + 1, str + 1);
+ case '*':
+ return (*str && wildcard_matches(wildcard, str + 1))
+ || wildcard_matches(wildcard + 1, str);
+ default:
+ return (*wildcard == *str)
+ && wildcard_matches(wildcard + 1, str + 1);
+ };
+}
+
+static char**
+segment_str(char *str)
+{
+ int count = 1;
+ char **parts = NULL;
+ char *saved = NULL;
+ char *tok = NULL;
+ int i = 0;
+ for (i = 0; str[i]; ++i)
+ if (str[i] == ':')
+ count++;
+ parts = zalloc(sizeof(char*) * (count + 1));
+ ZUC_ASSERTG_NOT_NULL(parts, out);
+ tok = strtok_r(str, ":", &saved);
+ i = 0;
+ parts[i++] = tok;
+ while (tok) {
+ tok = strtok_r(NULL, ":", &saved);
+ parts[i++] = tok;
+ }
+out:
+ return parts;
+}
+
+static void
+filter_cases(int *count, struct zuc_case **cases, char const *filter)
+{
+ int i = 0;
+ int j = 0;
+ int num_pos = 0;
+ int negative = -1;
+ char *buf = strdup(filter);
+ char **parts = segment_str(buf);
+
+ for (i = 0; parts[i]; ++i) {
+ if (parts[i][0] == '-') {
+ parts[i]++;
+ negative = i;
+ break;
+ }
+ num_pos++;
+ }
+
+ for (i = 0; i < *count; ++i) {
+ /* Walk backwards for easier pruning. */
+ for (j = cases[i]->test_count - 1; j >= 0; --j) {
+ int x;
+ bool keep = num_pos == 0;
+ char *name = NULL;
+ struct zuc_test *test = cases[i]->tests[j];
+ if (asprintf(&name, "%s.%s", cases[i]->name,
+ test->name) < 0) {
+ printf("%s:%d: error: %d\n", __FILE__, __LINE__,
+ errno);
+ exit(EXIT_FAILURE);
+ }
+ for (x = 0; (x < num_pos) && !keep; ++x)
+ keep = wildcard_matches(parts[x], name);
+ if (keep && (negative >= 0))
+ for (x = negative; parts[x] && keep; ++x)
+ keep &= !wildcard_matches(parts[x],
+ name);
+ if (!keep) {
+ int w;
+ free_test(test);
+ for (w = j + 1; w < cases[i]->test_count; w++)
+ cases[i]->tests[w - 1] =
+ cases[i]->tests[w];
+ cases[i]->test_count--;
+ }
+
+ free(name);
+ }
+ }
+ free(parts);
+ parts = NULL;
+ free(buf);
+ buf = NULL;
+
+ /* Prune any cases with no more tests. */
+ for (i = *count - 1; i >= 0; --i) {
+ if (cases[i]->test_count < 1) {
+ free_test_case(cases[i]);
+ for (j = i + 1; j < *count; ++j)
+ cases[j - 1] = cases[j];
+ cases[*count - 1] = NULL;
+ (*count)--;
+ }
+ }
+}
+
+static unsigned int
+get_seed_from_time(void)
+{
+ time_t sec = time(NULL);
+ unsigned int seed = (unsigned int) sec % 100000;
+ if (seed < 2)
+ seed = 2;
+
+ return seed;
+}
+
+static void
+initialize(void)
+{
+ static bool init = false;
+ if (init)
+ return;
+
+ init = true;
+ register_tests();
+ if (g_ctx.fatal)
+ return;
+
+ if (g_ctx.random > 1) {
+ g_ctx.seed = g_ctx.random;
+ } else if (g_ctx.random == 1) {
+ g_ctx.seed = get_seed_from_time();
+ }
+
+ if (g_ctx.case_count) {
+ order_cases(g_ctx.case_count, g_ctx.cases);
+ if (g_ctx.filter && g_ctx.filter[0])
+ filter_cases(&g_ctx.case_count, g_ctx.cases,
+ g_ctx.filter);
+ }
+}
+
+int
+zuc_initialize(int *argc, char *argv[], bool *help_flagged)
+{
+ int rc = EXIT_FAILURE;
+ int opt_help = 0;
+ int opt_nofork = 0;
+ int opt_list = 0;
+ int opt_repeat = 0;
+ int opt_random = 0;
+ int opt_break_on_failure = 0;
+ char *opt_filter = NULL;
+
+ char *help_param = NULL;
+ int argc_in = *argc;
+
+ const struct weston_option options[] = {
+ { WESTON_OPTION_BOOLEAN, "zuc-nofork", 0, &opt_nofork },
+ { WESTON_OPTION_BOOLEAN, "zuc-list-tests", 0, &opt_list },
+ { WESTON_OPTION_INTEGER, "zuc-repeat", 0, &opt_repeat },
+ { WESTON_OPTION_INTEGER, "zuc-random", 0, &opt_random },
+ { WESTON_OPTION_BOOLEAN, "zuc-break-on-failure", 0,
+ &opt_break_on_failure },
+ { WESTON_OPTION_STRING, "zuc-filter", 0, &opt_filter },
+ };
+
+ /*
+ *If a test binary is linked to our libzunitcmain it might want
+ * to access the program 'name' from argv[0]
+ */
+ free(g_progname);
+ g_progname = NULL;
+ free(g_progbasename);
+ g_progbasename = NULL;
+ if ((*argc > 0) && argv) {
+ char *path = NULL;
+ char *base = NULL;
+
+ g_progname = strdup(argv[0]);
+
+ /* basename() might modify the input, so needs a writeable
+ * string.
+ * It also may return a statically allocated buffer subject to
+ * being overwritten so needs to be dup'd.
+ */
+ path = strdup(g_progname);
+ base = basename(path);
+ g_progbasename = strdup(base);
+ free(path);
+ } else {
+ g_progname = strdup("");
+ printf("%s:%d: warning: No valid argv[0] for initialization\n",
+ __FILE__, __LINE__);
+ }
+
+
+ initialize();
+ if (g_ctx.fatal)
+ return EXIT_FAILURE;
+
+ if (help_flagged)
+ *help_flagged = false;
+
+ {
+ /* Help param will be a special case and need restoring. */
+ int i = 0;
+ char **argv_in = NULL;
+ const struct weston_option help_options[] = {
+ { WESTON_OPTION_BOOLEAN, "help", 'h', &opt_help },
+ };
+ argv_in = zalloc(sizeof(char *) * argc_in);
+ if (!argv_in) {
+ printf("%s:%d: error: alloc failed.\n",
+ __FILE__, __LINE__);
+ return EXIT_FAILURE;
+ }
+ for (i = 0; i < argc_in; ++i)
+ argv_in[i] = argv[i];
+
+ parse_options(help_options, ARRAY_LENGTH(help_options),
+ argc, argv);
+ if (*argc < argc_in) {
+ for (i = 1; (i < argc_in) && !help_param; ++i) {
+ bool found = false;
+ int j = 0;
+ for (j = 0; (j < *argc) && !found; ++j)
+ found = (argv[j] == argv_in[i]);
+
+ if (!found)
+ help_param = argv_in[i];
+ }
+ }
+ free(argv_in);
+ }
+
+ parse_options(options, ARRAY_LENGTH(options), argc, argv);
+
+ if (help_param && (*argc < argc_in))
+ argv[(*argc)++] = help_param;
+
+ if (opt_filter) {
+ zuc_set_filter(opt_filter);
+ free(opt_filter);
+ }
+
+ if (opt_help) {
+ printf("Usage: %s [OPTIONS]\n"
+ " --zuc-break-on-failure\n"
+ " --zuc-filter=FILTER\n"
+ " --zuc-list-tests\n"
+ " --zuc-nofork\n"
+ " --zuc-random=N [0|1|<seed number>]\n"
+ " --zuc-repeat=N\n"
+ " --help\n",
+ argv[0]);
+ if (help_flagged)
+ *help_flagged = true;
+ rc = EXIT_SUCCESS;
+ } else if (opt_list) {
+ zuc_list_tests();
+ rc = EXIT_FAILURE;
+ } else {
+ zuc_set_repeat(opt_repeat);
+ zuc_set_random(opt_random);
+ zuc_set_spawn(!opt_nofork);
+ zuc_set_break_on_failure(opt_break_on_failure);
+ rc = EXIT_SUCCESS;
+ }
+
+ return rc;
+}
+
+static void
+dispatch_pre_run(struct zuc_context *ctx, int pass_count, int pass_num,
+ int seed, const char *filter)
+{
+ struct zuc_slinked *curr;
+ for (curr = ctx->listeners; curr; curr = curr->next) {
+ struct zuc_event_listener *listener = curr->data;
+ if (listener->pre_run)
+ listener->pre_run(listener->data,
+ pass_count,
+ pass_num,
+ seed,
+ filter);
+ }
+}
+
+static void
+dispatch_run_started(struct zuc_context *ctx, int live_case_count,
+ int live_test_count, int disabled_count)
+{
+ struct zuc_slinked *curr;
+ for (curr = ctx->listeners; curr; curr = curr->next) {
+ struct zuc_event_listener *listener = curr->data;
+ if (listener->run_started)
+ listener->run_started(listener->data,
+ live_case_count,
+ live_test_count,
+ disabled_count);
+ }
+}
+
+static void
+dispatch_run_ended(struct zuc_context *ctx,
+ int live_case_count, int live_test_count, int total_passed,
+ int total_failed, int total_disabled, long total_elapsed)
+{
+ struct zuc_slinked *curr;
+ for (curr = ctx->listeners; curr; curr = curr->next) {
+ struct zuc_event_listener *listener = curr->data;
+ if (listener->run_ended)
+ listener->run_ended(listener->data,
+ ctx->case_count,
+ ctx->cases,
+ live_case_count,
+ live_test_count,
+ total_passed,
+ total_failed,
+ total_disabled,
+ total_elapsed);
+ }
+}
+
+static void
+dispatch_case_started(struct zuc_context *ctx,struct zuc_case *test_case,
+ int live_test_count, int disabled_count)
+{
+ struct zuc_slinked *curr;
+ for (curr = ctx->listeners; curr; curr = curr->next) {
+ struct zuc_event_listener *listener = curr->data;
+ if (listener->case_started)
+ listener->case_started(listener->data,
+ test_case,
+ live_test_count,
+ disabled_count);
+ }
+}
+
+static void
+dispatch_case_ended(struct zuc_context *ctx, struct zuc_case *test_case)
+{
+ struct zuc_slinked *curr;
+ for (curr = ctx->listeners; curr; curr = curr->next) {
+ struct zuc_event_listener *listener = curr->data;
+ if (listener->case_ended)
+ listener->case_ended(listener->data, test_case);
+ }
+}
+
+static void
+dispatch_test_started(struct zuc_context *ctx, struct zuc_test *test)
+{
+ struct zuc_slinked *curr;
+ for (curr = ctx->listeners; curr; curr = curr->next) {
+ struct zuc_event_listener *listener = curr->data;
+ if (listener->test_started)
+ listener->test_started(listener->data, test);
+ }
+}
+
+static void
+dispatch_test_ended(struct zuc_context *ctx, struct zuc_test *test)
+{
+ struct zuc_slinked *curr;
+ for (curr = ctx->listeners; curr; curr = curr->next) {
+ struct zuc_event_listener *listener = curr->data;
+ if (listener->test_ended)
+ listener->test_ended(listener->data, test);
+ }
+}
+
+static void
+dispatch_test_disabled(struct zuc_context *ctx, struct zuc_test *test)
+{
+ struct zuc_slinked *curr;
+ for (curr = ctx->listeners; curr; curr = curr->next) {
+ struct zuc_event_listener *listener = curr->data;
+ if (listener->test_disabled)
+ listener->test_disabled(listener->data, test);
+ }
+}
+
+static void
+dispatch_check_triggered(struct zuc_context *ctx, char const *file, int line,
+ enum zuc_fail_state state, enum zuc_check_op op,
+ enum zuc_check_valtype valtype,
+ intptr_t val1, intptr_t val2,
+ const char *expr1, const char *expr2)
+{
+ struct zuc_slinked *curr;
+ for (curr = ctx->listeners; curr; curr = curr->next) {
+ struct zuc_event_listener *listener = curr->data;
+ if (listener->check_triggered)
+ listener->check_triggered(listener->data,
+ file, line,
+ state, op, valtype,
+ val1, val2,
+ expr1, expr2);
+ }
+}
+
+static void
+dispatch_collect_event(struct zuc_context *ctx, char const *file, int line,
+ const char *expr1)
+{
+ struct zuc_slinked *curr;
+ for (curr = ctx->listeners; curr; curr = curr->next) {
+ struct zuc_event_listener *listener = curr->data;
+ if (listener->collect_event)
+ listener->collect_event(listener->data,
+ file, line, expr1);
+ }
+}
+
+static void
+migrate_deferred_events(struct zuc_test *test, bool transferred)
+{
+ struct zuc_event *evt = test->deferred;
+ if (!evt)
+ return;
+
+ test->deferred = NULL;
+ if (test->events) {
+ struct zuc_event *old = test->events;
+ while (old->next)
+ old = old->next;
+ old->next = evt;
+ } else {
+ test->events = evt;
+ }
+ while (evt && !transferred) {
+ dispatch_check_triggered(&g_ctx,
+ evt->file, evt->line,
+ evt->state, evt->op,
+ evt->valtype,
+ evt->val1, evt->val2,
+ evt->expr1, evt->expr2);
+ evt = evt->next;
+ }
+}
+
+static void
+mark_single_failed(struct zuc_test *test, enum zuc_fail_state state)
+{
+ switch (state) {
+ case ZUC_CHECK_OK:
+ /* no internal state to change */
+ break;
+ case ZUC_CHECK_SKIP:
+ if (test)
+ test->skipped = 1;
+ break;
+ case ZUC_CHECK_FAIL:
+ if (test)
+ test->failed = 1;
+ break;
+ case ZUC_CHECK_ERROR:
+ case ZUC_CHECK_FATAL:
+ if (test)
+ test->fatal = 1;
+ break;
+ }
+
+ if (g_ctx.break_on_failure)
+ raise(SIGABRT);
+
+}
+
+static void
+mark_failed(struct zuc_test *test, enum zuc_fail_state state)
+{
+ if (!test && g_ctx.curr_test)
+ test = g_ctx.curr_test;
+
+ if (test) {
+ mark_single_failed(test, state);
+ } else if (g_ctx.curr_case) {
+ /* In setup or tear-down of test suite */
+ int i;
+ for (i = 0; i < g_ctx.curr_case->test_count; ++i)
+ mark_single_failed(g_ctx.curr_case->tests[i], state);
+ }
+ if ((state == ZUC_CHECK_FATAL) || (state == ZUC_CHECK_ERROR))
+ g_ctx.fatal = true;
+}
+
+void
+zuc_attach_event(struct zuc_test *test, struct zuc_event *event,
+ enum zuc_event_type event_type, bool transferred)
+{
+ if (!test) {
+ /*
+ * consider adding events directly to the case.
+ * would be for use during per-suite setup and teardown.
+ */
+ printf("%s:%d: error: No current test.\n", __FILE__, __LINE__);
+ } else if (event_type == ZUC_EVENT_DEFERRED) {
+ if (test->deferred) {
+ struct zuc_event *curr = test->deferred;
+ while (curr->next)
+ curr = curr->next;
+ curr->next = event;
+ } else {
+ test->deferred = event;
+ }
+ } else {
+ if (test)
+ migrate_deferred_events(test, transferred);
+
+ if (test->events) {
+ struct zuc_event *curr = test->events;
+ while (curr->next)
+ curr = curr->next;
+ curr->next = event;
+ } else {
+ test->events = event;
+ }
+ mark_failed(test, event->state);
+ }
+}
+
+void
+zuc_add_event_listener(struct zuc_event_listener *event_listener)
+{
+ if (!event_listener) /* ensure null entries are not added */
+ return;
+
+ if (!g_ctx.listeners) {
+ g_ctx.listeners = zalloc(sizeof(struct zuc_slinked));
+ ZUC_ASSERT_NOT_NULL(g_ctx.listeners);
+ g_ctx.listeners->data = event_listener;
+ } else {
+ struct zuc_slinked *curr = g_ctx.listeners;
+ while (curr->next)
+ curr = curr->next;
+ curr->next = zalloc(sizeof(struct zuc_slinked));
+ ZUC_ASSERT_NOT_NULL(curr->next);
+ curr->next->data = event_listener;
+ }
+}
+
+
+void
+zuc_cleanup(void)
+{
+ int i;
+
+ free(g_ctx.filter);
+ g_ctx.filter = 0;
+ for (i = 0; i < 2; ++i)
+ if (g_ctx.fds[i] != -1) {
+ close(g_ctx.fds[i]);
+ g_ctx.fds[i] = -1;
+ }
+
+ if (g_ctx.listeners) {
+ struct zuc_slinked *curr = g_ctx.listeners;
+ while (curr) {
+ struct zuc_slinked *old = curr;
+ struct zuc_event_listener *listener = curr->data;
+ if (listener->destroy)
+ listener->destroy(listener->data);
+ free(listener);
+ curr = curr->next;
+ free(old);
+ }
+ g_ctx.listeners = NULL;
+ }
+
+ for (i = g_ctx.case_count - 1; i >= 0; --i) {
+ free_test_case(g_ctx.cases[i]);
+ g_ctx.cases[i] = NULL;
+ }
+ free(g_ctx.cases);
+ g_ctx.cases = NULL;
+
+ free(g_progname);
+ g_progname = NULL;
+ free(g_progbasename);
+ g_progbasename = NULL;
+}
+
+static void
+shuffle_cases(int count, struct zuc_case **cases,
+ unsigned int seed)
+{
+ int i;
+ unsigned int rseed = seed;
+ for (i = 0; i < count; ++i) {
+ int j;
+ for (j = cases[i]->test_count - 1; j > 0 ; --j) {
+ int val = rand_r(&rseed);
+ int b = ((val / (double)RAND_MAX) * j + 0.5);
+ if (j != b) {
+ struct zuc_test *tmp = cases[i]->tests[j];
+ cases[i]->tests[j] = cases[i]->tests[b];
+ cases[i]->tests[b] = tmp;
+ }
+ }
+ }
+
+ for (i = count - 1; i > 0; --i) {
+ int val = rand_r(&rseed);
+ int j = ((val / (double)RAND_MAX) * i + 0.5);
+
+ if (i != j) {
+ struct zuc_case *tmp = cases[i];
+ cases[i] = cases[j];
+ cases[j] = tmp;
+ }
+ }
+}
+
+void
+zuc_list_tests(void)
+{
+ int i;
+ int j;
+ initialize();
+ if (g_ctx.fatal)
+ return;
+ for (i = 0; i < g_ctx.case_count; ++i) {
+ printf("%s.\n", g_ctx.cases[i]->name);
+ for (j = 0; j < g_ctx.cases[i]->test_count; ++j) {
+ printf(" %s\n", g_ctx.cases[i]->tests[j]->name);
+ }
+ }
+}
+
+static void
+spawn_test(struct zuc_test *test, void *test_data,
+ void (*cleanup_fn)(void *data), void *cleanup_data)
+{
+ pid_t pid = -1;
+
+ if (!test || (!test->fn && !test->fn_f))
+ return;
+
+ if (pipe2(g_ctx.fds, O_CLOEXEC)) {
+ printf("%s:%d: error: Unable to create pipe: %d\n",
+ __FILE__, __LINE__, errno);
+ mark_failed(test, ZUC_CHECK_ERROR);
+ return;
+ }
+
+ fflush(NULL); /* important. avoid duplication of output */
+ pid = fork();
+ switch (pid) {
+ case -1: /* Error forking */
+ printf("%s:%d: error: Problem with fork: %d\n",
+ __FILE__, __LINE__, errno);
+ mark_failed(test, ZUC_CHECK_ERROR);
+ close(g_ctx.fds[0]);
+ g_ctx.fds[0] = -1;
+ close(g_ctx.fds[1]);
+ g_ctx.fds[1] = -1;
+ break;
+ case 0: { /* child */
+ int rc = EXIT_SUCCESS;
+ close(g_ctx.fds[0]);
+ g_ctx.fds[0] = -1;
+
+ if (test->fn_f)
+ test->fn_f(test_data);
+ else
+ test->fn();
+
+ if (test_has_failure(test))
+ rc = EXIT_FAILURE;
+ else if (test_has_skip(test))
+ rc = ZUC_EXIT_SKIP;
+
+ /* Avoid confusing memory tools like valgrind */
+ if (cleanup_fn)
+ cleanup_fn(cleanup_data);
+
+ zuc_cleanup();
+ exit(rc);
+ break;
+ }
+ default: { /* parent */
+ ssize_t rc = 0;
+ siginfo_t info = {};
+
+ close(g_ctx.fds[1]);
+ g_ctx.fds[1] = -1;
+
+ do {
+ rc = zuc_process_message(g_ctx.curr_test,
+ g_ctx.fds[0]);
+ } while (rc > 0);
+ close(g_ctx.fds[0]);
+ g_ctx.fds[0] = -1;
+
+ if (waitid(P_ALL, 0, &info, WEXITED)) {
+ printf("%s:%d: error: waitid failed. (%d)\n",
+ __FILE__, __LINE__, errno);
+ mark_failed(test, ZUC_CHECK_ERROR);
+ } else {
+ switch (info.si_code) {
+ case CLD_EXITED: {
+ int exit_code = info.si_status;
+ switch(exit_code) {
+ case EXIT_SUCCESS:
+ break;
+ case ZUC_EXIT_SKIP:
+ if (!test_has_skip(g_ctx.curr_test) &&
+ !test_has_failure(g_ctx.curr_test))
+ ZUC_SKIP("Child exited SKIP");
+ break;
+ default:
+ /* unexpected failure */
+ if (!test_has_failure(g_ctx.curr_test))
+ ZUC_ASSERT_EQ(0, exit_code);
+ }
+ break;
+ }
+ case CLD_KILLED:
+ case CLD_DUMPED:
+ printf("%s:%d: error: signaled: %d\n",
+ __FILE__, __LINE__, info.si_status);
+ mark_failed(test, ZUC_CHECK_ERROR);
+ break;
+ }
+ }
+ }
+ }
+}
+
+static void
+run_single_test(struct zuc_test *test,const struct zuc_fixture *fxt,
+ void *case_data, bool spawn)
+{
+ long elapsed = 0;
+ struct timespec begin;
+ struct timespec end;
+ void *test_data = NULL;
+ void *cleanup_data = NULL;
+ void (*cleanup_fn)(void *data) = NULL;
+ memset(&begin, 0, sizeof(begin));
+ memset(&end, 0, sizeof(end));
+
+ g_ctx.curr_test = test;
+ dispatch_test_started(&g_ctx, test);
+
+ cleanup_fn = fxt ? fxt->tear_down : NULL;
+ cleanup_data = NULL;
+
+ if (fxt && fxt->set_up) {
+ test_data = fxt->set_up(case_data);
+ cleanup_data = test_data;
+ } else {
+ test_data = case_data;
+ }
+
+ clock_gettime(TARGET_TIMER, &begin);
+
+ /* Need to re-check these, as fixtures might have changed test state. */
+ if (!test->fatal && !test->skipped) {
+ if (spawn) {
+ spawn_test(test, test_data,
+ cleanup_fn, cleanup_data);
+ } else {
+ if (test->fn_f)
+ test->fn_f(test_data);
+ else
+ test->fn();
+ }
+ }
+
+ clock_gettime(TARGET_TIMER, &end);
+
+ elapsed = (end.tv_sec - begin.tv_sec) * MS_PER_SEC;
+ if (end.tv_sec != begin.tv_sec) {
+ elapsed -= (begin.tv_nsec) / NANO_PER_MS;
+ elapsed += (end.tv_nsec) / NANO_PER_MS;
+ } else {
+ elapsed += (end.tv_nsec - begin.tv_nsec) / NANO_PER_MS;
+ }
+ test->elapsed = elapsed;
+
+ if (cleanup_fn)
+ cleanup_fn(cleanup_data);
+
+ if (test->deferred) {
+ if (test_has_failure(test))
+ migrate_deferred_events(test, false);
+ else
+ free_events(&test->deferred);
+ }
+
+ dispatch_test_ended(&g_ctx, test);
+
+ g_ctx.curr_test = NULL;
+}
+
+static void
+run_single_case(struct zuc_case *test_case)
+{
+ int count_live = test_case->test_count - test_case->disabled;
+ g_ctx.curr_case = test_case;
+ if (count_live) {
+ int i = 0;
+ const struct zuc_fixture *fxt = test_case->fxt;
+ void *case_data = fxt ? (void *)fxt->data : NULL;
+
+ dispatch_case_started(&g_ctx, test_case,
+ count_live, test_case->disabled);
+
+ if (fxt && fxt->set_up_test_case)
+ case_data = fxt->set_up_test_case(fxt->data);
+
+ for (i = 0; i < test_case->test_count; ++i) {
+ struct zuc_test *curr = test_case->tests[i];
+ if (curr->disabled) {
+ dispatch_test_disabled(&g_ctx, curr);
+ } else {
+ run_single_test(curr, fxt, case_data,
+ g_ctx.spawn);
+ if (curr->skipped)
+ test_case->skipped++;
+ if (curr->failed)
+ test_case->failed++;
+ if (curr->fatal)
+ test_case->fatal++;
+ if (!curr->failed && !curr->fatal)
+ test_case->passed++;
+ test_case->elapsed += curr->elapsed;
+ }
+ }
+
+ if (fxt && fxt->tear_down_test_case)
+ fxt->tear_down_test_case(case_data);
+
+ dispatch_case_ended(&g_ctx, test_case);
+ }
+ g_ctx.curr_case = NULL;
+}
+
+static void
+reset_test_values(struct zuc_case **cases, int case_count)
+{
+ int i;
+ for (i = 0; i < case_count; ++i) {
+ int j;
+ cases[i]->disabled = 0;
+ cases[i]->skipped = 0;
+ cases[i]->failed = 0;
+ cases[i]->fatal = 0;
+ cases[i]->passed = 0;
+ cases[i]->elapsed = 0;
+ for (j = 0; j < cases[i]->test_count; ++j) {
+ struct zuc_test *test = cases[i]->tests[j];
+ if (test->disabled)
+ cases[i]->disabled++;
+ test->skipped = 0;
+ test->failed = 0;
+ test->fatal = 0;
+ test->elapsed = 0;
+
+ free_events(&test->events);
+ free_events(&test->deferred);
+ }
+ }
+}
+
+static int
+run_single_pass(void)
+{
+ long total_elapsed = 0;
+ int total_passed = 0;
+ int total_failed = 0;
+ int total_skipped = 0;
+ int live_case_count = 0;
+ int live_test_count = 0;
+ int disabled_test_count = 0;
+ int i;
+
+ reset_test_values(g_ctx.cases, g_ctx.case_count);
+ for (i = 0; i < g_ctx.case_count; ++i) {
+ int live = g_ctx.cases[i]->test_count
+ - g_ctx.cases[i]->disabled;
+ if (live) {
+ live_test_count += live;
+ live_case_count++;
+ }
+ if (g_ctx.cases[i]->disabled)
+ disabled_test_count++;
+ }
+
+ dispatch_run_started(&g_ctx, live_case_count, live_test_count,
+ disabled_test_count);
+
+ for (i = 0; i < g_ctx.case_count; ++i) {
+ run_single_case(g_ctx.cases[i]);
+ total_failed += g_ctx.cases[i]->test_count
+ - (g_ctx.cases[i]->passed + g_ctx.cases[i]->disabled);
+ total_passed += g_ctx.cases[i]->passed;
+ total_elapsed += g_ctx.cases[i]->elapsed;
+ total_skipped += g_ctx.cases[i]->skipped;
+ }
+
+ dispatch_run_ended(&g_ctx, live_case_count, live_test_count,
+ total_passed, total_failed, disabled_test_count,
+ total_elapsed);
+
+ if (total_failed)
+ return EXIT_FAILURE;
+ else if (total_skipped)
+ return ZUC_EXIT_SKIP;
+ else
+ return EXIT_SUCCESS;
+}
+
+int
+zucimpl_run_tests(void)
+{
+ int rc = EXIT_SUCCESS;
+ int i;
+ int limit = g_ctx.repeat > 0 ? g_ctx.repeat : 1;
+
+ initialize();
+ if (g_ctx.fatal)
+ return EXIT_FAILURE;
+
+ if (g_ctx.listeners == NULL) {
+ zuc_add_event_listener(zuc_collector_create(&(g_ctx.fds[1])));
+ zuc_add_event_listener(zuc_base_logger_create());
+ }
+
+ if (g_ctx.case_count < 1) {
+ printf("%s:%d: error: Setup error: test tree is empty\n",
+ __FILE__, __LINE__);
+ rc = EXIT_FAILURE;
+ }
+
+ for (i = 0; (i < limit) && (g_ctx.case_count > 0); ++i) {
+ int pass_code = EXIT_SUCCESS;
+ dispatch_pre_run(&g_ctx, limit, i + 1,
+ (g_ctx.random > 0) ? g_ctx.seed : 0,
+ g_ctx.filter);
+
+ order_cases(g_ctx.case_count, g_ctx.cases);
+ if (g_ctx.random > 0)
+ shuffle_cases(g_ctx.case_count, g_ctx.cases,
+ g_ctx.seed);
+
+ pass_code = run_single_pass();
+ if (pass_code == EXIT_FAILURE)
+ rc = EXIT_FAILURE;
+ else if ((pass_code == ZUC_EXIT_SKIP) && (rc == EXIT_SUCCESS))
+ rc = ZUC_EXIT_SKIP;
+
+ g_ctx.seed++;
+ }
+
+ return rc;
+}
+
+int
+zucimpl_tracepoint(char const *file, int line, char const *fmt, ...)
+{
+ int rc = -1;
+ va_list argp;
+ char *msg = NULL;
+
+
+ va_start(argp, fmt);
+ rc = vasprintf(&msg, fmt, argp);
+ if (rc == -1) {
+ msg = NULL;
+ }
+ va_end(argp);
+
+ dispatch_collect_event(&g_ctx,
+ file, line,
+ msg);
+
+ free(msg);
+
+ return rc;
+}
+
+void
+zucimpl_terminate(char const *file, int line,
+ bool fail, bool fatal, const char *msg)
+{
+ enum zuc_fail_state state = ZUC_CHECK_SKIP;
+ int level = 2;
+ if (fail && fatal) {
+ state = ZUC_CHECK_FATAL;
+ level = 0;
+ } else if (fail && !fatal) {
+ state = ZUC_CHECK_FAIL;
+ level = 0;
+ }
+
+ mark_failed(g_ctx.curr_test, state);
+
+ if ((state != ZUC_CHECK_OK) && g_ctx.curr_test)
+ migrate_deferred_events(g_ctx.curr_test, false);
+
+ dispatch_check_triggered(&g_ctx,
+ file, line,
+ state,
+ ZUC_OP_TERMINATE, ZUC_VAL_INT,
+ level, 0,
+ msg, "");
+}
+
+static void
+validate_types(enum zuc_check_op op, enum zuc_check_valtype valtype)
+{
+ bool is_valid = true;
+
+ switch (op) {
+ case ZUC_OP_NULL:
+ case ZUC_OP_NOT_NULL:
+ is_valid = is_valid && (valtype == ZUC_VAL_PTR);
+ break;
+ default:
+ ; /* all rest OK */
+ }
+
+ switch (valtype) {
+ case ZUC_VAL_CSTR:
+ is_valid = is_valid && ((op == ZUC_OP_EQ) || (op == ZUC_OP_NE));
+ break;
+ default:
+ ; /* all rest OK */
+ }
+
+ if (!is_valid)
+ printf("%s:%d: warning: Unexpected op+type %d/%d.\n",
+ __FILE__, __LINE__, op, valtype);
+}
+
+static int
+pred2_unknown(intptr_t lhs, intptr_t rhs)
+{
+ return 0;
+}
+
+static int
+pred2_true(intptr_t lhs, intptr_t rhs)
+{
+ return lhs;
+}
+
+static int
+pred2_false(intptr_t lhs, intptr_t rhs)
+{
+ return !lhs;
+}
+
+static int
+pred2_eq(intptr_t lhs, intptr_t rhs)
+{
+ return lhs == rhs;
+}
+
+static int
+pred2_streq(intptr_t lhs, intptr_t rhs)
+{
+ int status = 0;
+ const char *lhptr = (const char *)lhs;
+ const char *rhptr = (const char *)rhs;
+
+ if (!lhptr && !rhptr)
+ status = 1;
+ else if (lhptr && rhptr)
+ status = strcmp(lhptr, rhptr) == 0;
+
+ return status;
+}
+
+static int
+pred2_ne(intptr_t lhs, intptr_t rhs)
+{
+ return lhs != rhs;
+}
+
+static int
+pred2_strne(intptr_t lhs, intptr_t rhs)
+{
+ int status = 0;
+ const char *lhptr = (const char *)lhs;
+ const char *rhptr = (const char *)rhs;
+
+ if (lhptr != rhptr) {
+ if (!lhptr || !rhptr)
+ status = 1;
+ else
+ status = strcmp(lhptr, rhptr) != 0;
+ }
+
+ return status;
+}
+
+static int
+pred2_ge(intptr_t lhs, intptr_t rhs)
+{
+ return lhs >= rhs;
+}
+
+static int
+pred2_gt(intptr_t lhs, intptr_t rhs)
+{
+ return lhs > rhs;
+}
+
+static int
+pred2_le(intptr_t lhs, intptr_t rhs)
+{
+ return lhs <= rhs;
+}
+
+static int
+pred2_lt(intptr_t lhs, intptr_t rhs)
+{
+ return lhs < rhs;
+}
+
+static comp_pred2
+get_pred2(enum zuc_check_op op, enum zuc_check_valtype valtype)
+{
+ switch (op) {
+ case ZUC_OP_TRUE:
+ return pred2_true;
+ break;
+ case ZUC_OP_FALSE:
+ return pred2_false;
+ break;
+ case ZUC_OP_NULL:
+ return pred2_false;
+ break;
+ case ZUC_OP_NOT_NULL:
+ return pred2_true;
+ break;
+ case ZUC_OP_EQ:
+ if (valtype == ZUC_VAL_CSTR)
+ return pred2_streq;
+ else
+ return pred2_eq;
+ break;
+ case ZUC_OP_NE:
+ if (valtype == ZUC_VAL_CSTR)
+ return pred2_strne;
+ else
+ return pred2_ne;
+ break;
+ case ZUC_OP_GE:
+ return pred2_ge;
+ break;
+ case ZUC_OP_GT:
+ return pred2_gt;
+ break;
+ case ZUC_OP_LE:
+ return pred2_le;
+ break;
+ case ZUC_OP_LT:
+ return pred2_lt;
+ break;
+ default:
+ return pred2_unknown;
+ }
+}
+
+int
+zucimpl_expect_pred2(char const *file, int line,
+ enum zuc_check_op op, enum zuc_check_valtype valtype,
+ bool fatal,
+ intptr_t lhs, intptr_t rhs,
+ const char *lhs_str, const char* rhs_str)
+{
+ enum zuc_fail_state state = fatal ? ZUC_CHECK_FATAL : ZUC_CHECK_FAIL;
+ comp_pred2 pred = get_pred2(op, valtype);
+ int failed = !pred(lhs, rhs);
+
+ validate_types(op, valtype);
+
+ if (failed) {
+ mark_failed(g_ctx.curr_test, state);
+
+ if (g_ctx.curr_test)
+ migrate_deferred_events(g_ctx.curr_test, false);
+
+ dispatch_check_triggered(&g_ctx,
+ file, line,
+ fatal ? ZUC_CHECK_FATAL
+ : ZUC_CHECK_FAIL,
+ op, valtype,
+ lhs, rhs,
+ lhs_str, rhs_str);
+ }
+ return failed;
+}