[PATCH weston v6 2/4] Enables output in the JUnit XML format.
Jon A. Cruz
jonc at osg.samsung.com
Thu Jul 2 23:36:45 PDT 2015
Adds basic support for optionally outputting in the XML format
commonly used by JUnit compatible tools.
This format is supported by default by many tools, including
the Jenkins build system. It also is more detailed and
captures more information than the more simplistic TAP
format.
Signed-off-by: Jon A. Cruz <jonc at osg.samsung.com>
---
Makefile.am | 9 +
configure.ac | 26 ++
tools/zunitc/src/zuc_junit_reporter.c | 488 ++++++++++++++++++++++++++++++++++
tools/zunitc/src/zuc_junit_reporter.h | 38 +++
tools/zunitc/src/zunitc_impl.c | 17 ++
5 files changed, 578 insertions(+)
create mode 100644 tools/zunitc/src/zuc_junit_reporter.c
create mode 100644 tools/zunitc/src/zuc_junit_reporter.h
diff --git a/Makefile.am b/Makefile.am
index 4f0a450..89d9e4c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -982,6 +982,8 @@ libzunitc_la_SOURCES = \
tools/zunitc/src/zuc_context.h \
tools/zunitc/src/zuc_event.h \
tools/zunitc/src/zuc_event_listener.h \
+ tools/zunitc/src/zuc_junit_reporter.c \
+ tools/zunitc/src/zuc_junit_reporter.h \
tools/zunitc/src/zuc_types.h \
tools/zunitc/src/zunitc_impl.c \
shared/helpers.h
@@ -993,6 +995,13 @@ libzunitc_la_CFLAGS = \
libzunitc_la_LIBADD = \
libshared.la
+if ENABLE_JUNIT_XML
+libzunitc_la_CFLAGS += \
+ $(LIBXML2_CFLAGS)
+libzunitc_la_LIBADD += \
+ $(LIBXML2_LIBS)
+endif
+
libzunitcmain_la_SOURCES = \
tools/zunitc/src/main.c
diff --git a/configure.ac b/configure.ac
index 404418e..b67df39 100644
--- a/configure.ac
+++ b/configure.ac
@@ -426,6 +426,31 @@ if test "x$enable_dbus" != "xno"; then
fi
AM_CONDITIONAL(ENABLE_DBUS, test "x$enable_dbus" = "xyes")
+# Note that other features might want libxml2, or this feature might use
+# alternative xml libraries at some point. Therefore the feature and
+# pre-requisite concepts are split.
+AC_ARG_ENABLE(junit_xml,
+ AS_HELP_STRING([--disable-junit-xml],
+ [do not build with JUnit XML output]),,
+ enable_junit_xml=auto)
+if test "x$enable_junit_xml" != "xno"; then
+ PKG_CHECK_MODULES(LIBXML2,
+ [libxml-2.0 >= 2.6],
+ have_libxml2=yes,
+ have_libxml2=no)
+ if test "x$have_libxml2" = "xno" -a "x$enable_junit_xml" = "xyes"; then
+ AC_MSG_ERROR([JUnit XML support explicitly requested, but libxml2 couldn't be found])
+ fi
+ if test "x$have_libxml2" = "xyes"; then
+ enable_junit_xml=yes
+ AC_DEFINE(HAVE_LIBXML2, [1], [Define if libxml2 is available])
+ AC_DEFINE(ENABLE_JUNIT_XML, [1], [Build Weston with JUnit output support])
+ else
+ enable_junit_xml=no
+ fi
+fi
+AM_CONDITIONAL(ENABLE_JUNIT_XML, test "x$enable_junit_xml" = "xyes")
+
# ivi-shell support
AC_ARG_ENABLE(ivi-shell,
AS_HELP_STRING([--disable-ivi-shell],
@@ -537,6 +562,7 @@ AC_MSG_RESULT([
FBDEV Compositor ${enable_fbdev_compositor}
RDP Compositor ${enable_rdp_compositor}
Screen Sharing ${enable_screen_sharing}
+ JUnit XML output ${enable_junit_xml}
Raspberry Pi BCM headers ${have_bcm_host}
diff --git a/tools/zunitc/src/zuc_junit_reporter.c b/tools/zunitc/src/zuc_junit_reporter.c
new file mode 100644
index 0000000..7e05fbf
--- /dev/null
+++ b/tools/zunitc/src/zuc_junit_reporter.c
@@ -0,0 +1,488 @@
+/*
+ * 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_junit_reporter.h"
+
+#if ENABLE_JUNIT_XML
+
+#include <fcntl.h>
+#include <libxml/parser.h>
+#include <memory.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "zuc_event_listener.h"
+#include "zuc_types.h"
+
+#include "shared/zalloc.h"
+
+#define XML_FNAME "test_detail.xml"
+
+#define ISO_8601_FORMAT "%Y-%m-%dT%H:%M:%SZ"
+
+/**
+ * Internal data.
+ */
+struct junit_data
+{
+ int fd;
+ time_t begin;
+};
+
+/**
+ * Formats a time in milliseconds to the normal JUnit elapsed form, or
+ * NULL if there is a problem.
+ * The caller should release this with free()
+ *
+ * @return the formatted time string upon success, NULL otherwise.
+ */
+static char *
+as_duration(long ms);
+
+/**
+ * Formats a time in milliseconds to the full ISO-8601 date/time string
+ * format, or NULL if there is a problem.
+ * The caller should release this with free()
+ *
+ * @return the formatted time string upon success, NULL otherwise.
+ */
+static char *
+as_iso_8601(time_t const *t);
+
+/**
+ * Returns the status string for the tests (run/notrun).
+ *
+ * @param test the test to check status of.
+ * @return the status string.
+ */
+static char const *
+get_test_status(struct zuc_test *test);
+
+/**
+ * Output the given event.
+ *
+ * @param parent the parent node to add new content to.
+ * @param event the event to write out.
+ */
+static void
+emit_event(xmlNodePtr parent, struct zuc_event *event);
+
+/**
+ * Output the given test.
+ *
+ * @param parent the parent node to add new content to.
+ * @param test the test to write out.
+ */
+static void
+emit_test(xmlNodePtr parent, struct zuc_test *test);
+
+/**
+ * Output the given test case.
+ *
+ * @param parent the parent node to add new content to.
+ * @param test_case the test case to write out.
+ */
+static void
+emit_case(xmlNodePtr parent, struct zuc_case *test_case);
+
+static void
+destroy(void *data);
+
+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);
+
+struct zuc_event_listener *
+zuc_junit_reporter_create(void)
+{
+ struct zuc_event_listener *listener =
+ zalloc(sizeof(struct zuc_event_listener));
+
+ struct junit_data *data = zalloc(sizeof(struct junit_data));
+ data->fd = -1;
+
+ listener->data = data;
+ listener->destroy = destroy;
+ listener->run_started = run_started;
+ listener->run_ended = run_ended;
+
+ return listener;
+}
+
+void
+destroy(void *data)
+{
+ xmlCleanupParser();
+
+ free(data);
+}
+
+void
+run_started(void *data, int live_case_count, int live_test_count,
+ int disabled_count)
+{
+ struct junit_data *jdata = data;
+ jdata->begin = time(NULL);
+ jdata->fd = open(XML_FNAME, O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
+}
+
+char const *
+get_test_status(struct zuc_test *test)
+{
+ if (test->disabled || test->skipped)
+ return "notrun";
+ else
+ return "run";
+}
+
+#define MAX_64BIT_STRLEN 20
+
+static void
+set_attribute(xmlNodePtr node, const char *name, int value)
+{
+ xmlChar scratch[MAX_64BIT_STRLEN + 1] = {};
+ xmlStrPrintf(scratch, sizeof(scratch), BAD_CAST "%d", value);
+ xmlSetProp(node, BAD_CAST name, scratch);
+}
+
+void
+emit_event(xmlNodePtr parent, struct zuc_event *event)
+{
+ char *msg = NULL;
+
+ switch (event->op) {
+ case ZUC_OP_TRUE:
+ if (asprintf(&msg, "%s:%d: error: Value of: %s\n"
+ " Actual: false\n"
+ "Expected: true\n", event->file, event->line,
+ event->expr1) < 0) {
+ msg = NULL;
+ }
+ break;
+ case ZUC_OP_FALSE:
+ if (asprintf(&msg, "%s:%d: error: Value of: %s\n"
+ " Actual: true\n"
+ "Expected: false\n", event->file, event->line,
+ event->expr1) < 0) {
+ msg = NULL;
+ }
+ break;
+ case ZUC_OP_NULL:
+ if (asprintf(&msg, "%s:%d: error: Value of: %s\n"
+ " Actual: %p\n"
+ "Expected: %p\n", event->file, event->line,
+ event->expr1, (void *)event->val1, NULL) < 0) {
+ msg = NULL;
+ }
+ break;
+ case ZUC_OP_NOT_NULL:
+ if (asprintf(&msg, "%s:%d: error: Value of: %s\n"
+ " Actual: %p\n"
+ "Expected: not %p\n", event->file, event->line,
+ event->expr1, (void *)event->val1, NULL) < 0) {
+ msg = NULL;
+ }
+ break;
+ case ZUC_OP_EQ:
+ if (event->valtype == ZUC_VAL_CSTR) {
+ if (asprintf(&msg, "%s:%d: error: Value of: %s\n"
+ " Actual: %s\n"
+ "Expected: %s\n"
+ "Which is: %s\n",
+ event->file, event->line, event->expr2,
+ (char *)event->val2, event->expr1,
+ (char *)event->val1) < 0) {
+ msg = NULL;
+ }
+ } else {
+ if (asprintf(&msg, "%s:%d: error: Value of: %s\n"
+ " Actual: %ld\n"
+ "Expected: %s\n"
+ "Which is: %ld\n",
+ event->file, event->line, event->expr2,
+ event->val2, event->expr1,
+ event->val1) < 0) {
+ msg = NULL;
+ }
+ }
+ break;
+ case ZUC_OP_NE:
+ if (event->valtype == ZUC_VAL_CSTR) {
+ if (asprintf(&msg, "%s:%d: error: "
+ "Expected: (%s) %s (%s),"
+ " actual: %s == %s\n",
+ event->file, event->line,
+ event->expr1, zuc_get_opstr(event->op),
+ event->expr2, (char *)event->val1,
+ (char *)event->val2) < 0) {
+ msg = NULL;
+ }
+ } else {
+ if (asprintf(&msg, "%s:%d: error: "
+ "Expected: (%s) %s (%s),"
+ " actual: %ld vs %ld\n",
+ event->file, event->line,
+ event->expr1, zuc_get_opstr(event->op),
+ event->expr2, event->val1,
+ event->val2) < 0) {
+ msg = NULL;
+ }
+ }
+ break;
+ case ZUC_OP_TERMINATE:
+ {
+ char const *level = (event->val1 == 0) ? "error"
+ : (event->val1 == 1) ? "warning"
+ : "note";
+ if (asprintf(&msg, "%s:%d: %s: %s\n",
+ event->file, event->line, level,
+ event->expr1) < 0) {
+ msg = NULL;
+ }
+ break;
+ }
+ case ZUC_OP_TRACEPOINT:
+ if (asprintf(&msg, "%s:%d: note: %s\n",
+ event->file, event->line, event->expr1) < 0) {
+ msg = NULL;
+ }
+ break;
+ default:
+ if (asprintf(&msg, "%s:%d: error: "
+ "Expected: (%s) %s (%s), actual: %ld vs %ld\n",
+ event->file, event->line,
+ event->expr1, zuc_get_opstr(event->op),
+ event->expr2, event->val1, event->val2) < 0) {
+ msg = NULL;
+ }
+ }
+
+ if ((event->op == ZUC_OP_TERMINATE) && (event->val1 > 1)) {
+ xmlNewChild(parent, NULL, BAD_CAST "skipped", NULL);
+ } else {
+ xmlNodePtr node = xmlNewChild(parent, NULL,
+ BAD_CAST "failure", NULL);
+
+ if (msg) {
+ xmlSetProp(node, BAD_CAST "message", BAD_CAST msg);
+ }
+ xmlSetProp(node, BAD_CAST "type", BAD_CAST "");
+ if (msg) {
+ xmlNodePtr cdata = xmlNewCDataBlock(node->doc,
+ BAD_CAST msg,
+ strlen(msg));
+ xmlAddChild(node, cdata);
+ }
+ }
+
+ free(msg);
+}
+
+void
+emit_test(xmlNodePtr parent, struct zuc_test *test)
+{
+ char *time_str = as_duration(test->elapsed);
+ xmlNodePtr node = xmlNewChild(parent, NULL, BAD_CAST "testcase", NULL);
+
+ xmlSetProp(node, BAD_CAST "name", BAD_CAST test->name);
+ xmlSetProp(node, BAD_CAST "status", BAD_CAST get_test_status(test));
+
+ if (time_str) {
+ xmlSetProp(node, BAD_CAST "time", BAD_CAST time_str);
+
+ free(time_str);
+ time_str = NULL;
+ }
+
+ xmlSetProp(node, BAD_CAST "classname", BAD_CAST test->test_case->name);
+
+ if ((test->failed || test->fatal || test->skipped) && test->events) {
+ struct zuc_event *evt;
+ for (evt = test->events; evt; evt = evt->next)
+ emit_event(node, evt);
+ }
+}
+
+void
+emit_case(xmlNodePtr parent, struct zuc_case *test_case)
+{
+ int i;
+ int skipped = 0;
+ int disabled = 0;
+ int failures = 0;
+ xmlNodePtr node = NULL;
+ char *time_str = as_duration(test_case->elapsed);
+
+ for (i = 0; i < test_case->test_count; ++i) {
+ if (test_case->tests[i]->disabled )
+ disabled++;
+ if (test_case->tests[i]->skipped )
+ skipped++;
+ if (test_case->tests[i]->failed
+ || test_case->tests[i]->fatal )
+ failures++;
+ }
+
+ node = xmlNewChild(parent, NULL, BAD_CAST "testsuite", NULL);
+ xmlSetProp(node, BAD_CAST "name", BAD_CAST test_case->name);
+
+ set_attribute(node, "tests", test_case->test_count);
+ set_attribute(node, "failures", failures);
+ set_attribute(node, "disabled", disabled);
+ set_attribute(node, "skipped", skipped);
+
+ if (time_str) {
+ xmlSetProp(node, BAD_CAST "time", BAD_CAST time_str);
+ free(time_str);
+ time_str = NULL;
+ }
+
+ for (i = 0; i < test_case->test_count; ++i)
+ emit_test(node, test_case->tests[i]);
+}
+
+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)
+{
+ int i;
+ long time = 0;
+ char *time_str = NULL;
+ char *timestamp = NULL;
+ xmlNodePtr root = NULL;
+ xmlDocPtr doc = NULL;
+ xmlChar *xmlchars = NULL;
+ int xmlsize = 0;
+ struct junit_data *jdata = data;
+
+ for (i = 0; i < case_count; ++i)
+ time += cases[i]->elapsed;
+
+ time_str = as_duration(time);
+ timestamp = as_iso_8601(&jdata->begin);
+
+ /* here would be where to add errors? */
+
+ doc = xmlNewDoc(BAD_CAST "1.0");
+ root = xmlNewNode(NULL, BAD_CAST "testsuites");
+ xmlDocSetRootElement(doc, root);
+
+ set_attribute(root, "tests", live_test_count);
+ set_attribute(root, "failures", total_failed);
+ set_attribute(root, "disabled", total_disabled);
+
+ if (timestamp) {
+ xmlSetProp(root, BAD_CAST "timestamp", BAD_CAST timestamp);
+ free(timestamp);
+ timestamp = NULL;
+ }
+
+ if (time_str) {
+ xmlSetProp(root, BAD_CAST "time", BAD_CAST time_str);
+ free(time_str);
+ time_str = NULL;
+ }
+
+ xmlSetProp(root, BAD_CAST "name", BAD_CAST "AllTests");
+
+ for (i = 0; i < case_count; ++i) {
+ emit_case(root, cases[i]);
+ }
+
+ xmlDocDumpFormatMemoryEnc(doc, &xmlchars, &xmlsize, "UTF-8", 1);
+ dprintf(jdata->fd, "%s", (char *) xmlchars);
+ xmlFree(xmlchars);
+ xmlchars = NULL;
+ xmlFreeDoc(doc);
+
+ if ((jdata->fd != fileno(stdout))
+ && (jdata->fd != fileno(stderr))
+ && (jdata->fd != -1)) {
+ close(jdata->fd);
+ jdata->fd = -1;
+ }
+}
+
+char *
+as_duration(long ms)
+{
+ char *str = NULL;
+ if (asprintf(&str, "%1.3f", ms / 1000.0) < 0) {
+ str = NULL;
+ } else {
+ if (!strcmp("0.000", str)) {
+ free(str);
+ str = strdup("0");
+ }
+ }
+ return str;
+}
+
+char *
+as_iso_8601(time_t const *t)
+{
+ char *result = NULL;
+ char buf[32] = {};
+ struct tm when;
+ if (gmtime_r(t, &when) != NULL)
+ if (strftime(buf, sizeof(buf), ISO_8601_FORMAT, &when))
+ result = strdup(buf);
+
+ return result;
+}
+
+#else /* ENABLE_JUNIT_XML */
+
+#include "shared/zalloc.h"
+#include "zuc_event_listener.h"
+
+/*
+ * Simple stub version if junit output (including libxml2 support) has
+ * been disabled.
+ */
+
+struct zuc_event_listener *
+zuc_junit_reporter_create(void)
+{
+ struct zuc_event_listener *listener = zalloc(sizeof(*listener));
+
+ /* Leaving all to default 0/NULL */
+
+ return listener;
+}
+
+#endif /* ENABLE_JUNIT_XML */
diff --git a/tools/zunitc/src/zuc_junit_reporter.h b/tools/zunitc/src/zuc_junit_reporter.h
new file mode 100644
index 0000000..66f3c7b
--- /dev/null
+++ b/tools/zunitc/src/zuc_junit_reporter.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_JUNIT_REPORTER_H
+#define ZUC_JUNIT_REPORTER_H
+
+struct zuc_event_listener;
+
+/**
+ * Creates an instance of a reporter that will write data in the JUnit
+ * XML format.
+ */
+struct zuc_event_listener *
+zuc_junit_reporter_create(void);
+
+#endif /* ZUC_JUNIT_REPORTER_H */
diff --git a/tools/zunitc/src/zunitc_impl.c b/tools/zunitc/src/zunitc_impl.c
index b8ab0b2..6f591f0 100644
--- a/tools/zunitc/src/zunitc_impl.c
+++ b/tools/zunitc/src/zunitc_impl.c
@@ -44,6 +44,7 @@
#include "zuc_collector.h"
#include "zuc_context.h"
#include "zuc_event_listener.h"
+#include "zuc_junit_reporter.h"
#include "shared/config-parser.h"
#include "shared/helpers.h"
@@ -152,6 +153,12 @@ zuc_set_break_on_failure(bool break_on_failure)
g_ctx.break_on_failure = break_on_failure;
}
+void
+zuc_set_output_junit(bool enable)
+{
+ g_ctx.output_junit = enable;
+}
+
const char *
zuc_get_program_name(void)
{
@@ -523,6 +530,7 @@ zuc_initialize(int *argc, char *argv[], bool *help_flagged)
int opt_repeat = 0;
int opt_random = 0;
int opt_break_on_failure = 0;
+ int opt_junit = 0;
char *opt_filter = NULL;
char *help_param = NULL;
@@ -535,6 +543,9 @@ zuc_initialize(int *argc, char *argv[], bool *help_flagged)
{ WESTON_OPTION_INTEGER, "zuc-random", 0, &opt_random },
{ WESTON_OPTION_BOOLEAN, "zuc-break-on-failure", 0,
&opt_break_on_failure },
+#if ENABLE_JUNIT_XML
+ { WESTON_OPTION_BOOLEAN, "zuc-output-xml", 0, &opt_junit },
+#endif
{ WESTON_OPTION_STRING, "zuc-filter", 0, &opt_filter },
};
@@ -623,6 +634,9 @@ zuc_initialize(int *argc, char *argv[], bool *help_flagged)
" --zuc-filter=FILTER\n"
" --zuc-list-tests\n"
" --zuc-nofork\n"
+#if ENABLE_JUNIT_XML
+ " --zuc-output-xml\n"
+#endif
" --zuc-random=N [0|1|<seed number>]\n"
" --zuc-repeat=N\n"
" --help\n",
@@ -638,6 +652,7 @@ zuc_initialize(int *argc, char *argv[], bool *help_flagged)
zuc_set_random(opt_random);
zuc_set_spawn(!opt_nofork);
zuc_set_break_on_failure(opt_break_on_failure);
+ zuc_set_output_junit(opt_junit);
rc = EXIT_SUCCESS;
}
@@ -1301,6 +1316,8 @@ zucimpl_run_tests(void)
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.output_junit)
+ zuc_add_event_listener(zuc_junit_reporter_create());
}
if (g_ctx.case_count < 1) {
--
2.1.0
More information about the wayland-devel
mailing list