[PATCH weston v3 4/5] Enables output in the JUnit XML format.
Bryce Harrington
bryce at osg.samsung.com
Fri May 29 11:52:48 PDT 2015
On Tue, May 26, 2015 at 04:06:40PM -0700, Jon A. Cruz wrote:
> Adds basic support fo optionally outputting in the XML format
for
> commonly used by JUnit compatible tools.
Can you add why we might care about having this format, in addition to
TAP?
> Signed-off-by: Jon A. Cruz <jonc at osg.samsung.com>
> ---
> Makefile.am | 2 +
> tools/zunitc/inc/zunitc/zunitc.h | 7 +
> tools/zunitc/src/zuc_junit_reporter.c | 414 ++++++++++++++++++++++++++++++++++
> tools/zunitc/src/zuc_junit_reporter.h | 34 +++
> tools/zunitc/src/zunitc_impl.c | 12 +
> 5 files changed, 469 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 7686a6d..b1b7c20 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -960,6 +960,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_tap_logger.c \
> tools/zunitc/src/zuc_tap_logger.h \
> tools/zunitc/src/zuc_types.h \
> diff --git a/tools/zunitc/inc/zunitc/zunitc.h b/tools/zunitc/inc/zunitc/zunitc.h
> index 53724d2..2294966 100644
> --- a/tools/zunitc/inc/zunitc/zunitc.h
> +++ b/tools/zunitc/inc/zunitc/zunitc.h
> @@ -198,6 +198,13 @@ void zuc_set_spawn(bool spawn);
> void zuc_set_output_tap(bool enable);
>
> /**
> + * Enables output in the JUnit XML format.
> + *
> + * @param enable true to generate JUnit XML output, false to disable.
> + */
> +void zuc_set_output_junit(bool enable);
> +
> +/**
> * Defines a test case that can be registered to run.
> */
> #define ZUC_TEST(tcase, test) \
> diff --git a/tools/zunitc/src/zuc_junit_reporter.c b/tools/zunitc/src/zuc_junit_reporter.c
> new file mode 100644
> index 0000000..537b1d7
> --- /dev/null
> +++ b/tools/zunitc/src/zuc_junit_reporter.c
> @@ -0,0 +1,414 @@
> +/*
> + * Copyright © 2015 Samsung Electronics Co., Ltd
> + *
> + * Permission to use, copy, modify, distribute, and sell this software and
> + * its documentation for any purpose is hereby granted without fee, provided
> + * that the above copyright notice appear in all copies and that both that
> + * copyright notice and this permission notice appear in supporting
> + * documentation, and that the name of the copyright holders not be used in
> + * advertising or publicity pertaining to distribution of the software
> + * without specific, written prior permission. The copyright holders make
> + * no representations about the suitability of this software for any
> + * purpose. It is provided "as is" without express or implied warranty.
> + *
> + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
> + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
> + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
> + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
> + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
> + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
> + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + */
> +
> +#include "config.h"
> +
> +#include "zuc_junit_reporter.h"
> +
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <memory.h>
> +#include <stdio.h>
> +#include <sys/stat.h>
> +#include <sys/types.h>
> +#include <unistd.h>
> +#include <time.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 string in escaped XML form.
> + *
> + * @param fd the file descriptor to write to.
> + * @param str the string to write an escaped form of.
> + */
> +static void emit_escaped(int fd, const char *str);
> +
> +/**
> + * Output the given event.
> + *
> + * @param fd the file descriptor to write to.
> + * @param event the event to write out.
> + */
> +static void emit_event(int fd, struct zuc_event *event);
> +
> +/**
> + * Output the given test.
> + *
> + * @param fd the file descriptor to write to.
> + * @param test the test to write out.
> + */
> +static void emit_test(int fd, struct zuc_test *test);
> +
> +/**
> + * Output the given test case.
> + *
> + * @param fd the file descriptor to write to.
> + * @param test_case the test case to write out.
> + */
> +static void emit_case(int fd, 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)
> +{
> + 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";
> +}
> +
> +void emit_escaped(int fd, const char *str)
> +{
> + const char *ptr = str;
> + while (*ptr) {
> + switch (*ptr) {
> + case '\'':
> + dprintf(fd, "'");
> + break;
> + case '\"':
> + dprintf(fd, """);
> + break;
> + case '<':
> + dprintf(fd, "<");
> + break;
> + case '>':
> + dprintf(fd, ">");
> + break;
> + case '&':
> + dprintf(fd, "&");
> + break;
> + default:
> + if ((*ptr >= ' ') && (*ptr < 0x7f)) {
> + dprintf(fd, "%c", *ptr);
> + } else {
> + dprintf(fd, "&#x%02X;", 0x0ff & *ptr);
> + }
> + }
> + ptr++;
> + }
> +}
> +
> +void emit_event(int fd, 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_EQ:
> + if (asprintf(&msg, "%s:%d: error: Value of: %s\n"
> + " Actual: %d\n"
> + "Expected: %s\n"
> + "Which is: %d\n",
> + event->file, event->line, event->expr2,
> + event->val2, event->expr1, event->val1) < 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: %d vs %d\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)) {
> + dprintf(fd, " <skipped/>\n");
> + } else {
> + dprintf(fd, " <failure");
> + if (msg) {
> + dprintf(fd, " message=\"");
> + emit_escaped(fd, msg);
> + dprintf(fd, "\"");
> + }
> + dprintf(fd, " type=\"\"");
> + if (msg) {
> + dprintf(fd, " >");
> + dprintf(fd, "<![CDATA[%s]]>", msg);
> + dprintf(fd, "</failure>\n");
> + } else {
> + dprintf(fd, " />");
> + }
> + }
> +
> + free(msg);
> +}
> +
> +void emit_test(int fd, struct zuc_test *test)
> +{
> + char *time_str = as_duration(test->elapsed);
> + dprintf(fd, " <testcase name=\"%s\" status=\"%s\"",
> + test->name, get_test_status(test));
> + if (time_str) {
> + dprintf(fd, " time=\"%s\"", time_str);
> + free(time_str);
> + time_str = NULL;
> + }
> + dprintf(fd, " classname=\"%s\"", test->test_case->name);
> + if ((test->failed || test->fatal || test->skipped) && test->events) {
> + struct zuc_event *evt;
> + dprintf(fd, ">\n");
> +
> + for (evt = test->events; evt; evt = evt->next)
> + emit_event(fd, evt);
> +
> + dprintf(fd, " </testcase>\n");
> + } else {
> + dprintf(fd, " />\n");
> + }
> +}
> +
> +void emit_case(int fd, struct zuc_case *test_case)
> +{
> + int i;
> + int skipped = 0;
> + int disabled = 0;
> + int failures = 0;
> + 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++;
> + }
> +
> + dprintf(fd,
> + " <testsuite name=\"%s\" tests=\"%d\" failures=\"%d\""
> + " disabled=\"%d\" skipped=\"%d\"",
> + test_case->name, test_case->test_count, failures, disabled,
> + skipped);
> + if (time_str) {
> + dprintf(fd, " time=\"%s\"", time_str);
> + free(time_str);
> + time_str = NULL;
> + }
> + dprintf(fd, ">\n");
> +
> + for (i = 0; i < test_case->test_count; ++i)
> + emit_test(fd, test_case->tests[i]);
> +
> +
> + dprintf(fd, " </testsuite>\n");
> +}
> +
> +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)
Maybe a test statistics struct would make sense here?
> +{
> + int i;
> + long time = 0;
> + char *time_str = NULL;
> + char *timestamp = NULL;
> + 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);
> +
> + dprintf(jdata->fd, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
> + /* here would be where to add errors? */
> + dprintf(jdata->fd,
> + "<testsuites tests=\"%d\" failures=\"%d\" disabled=\"%d\"",
> + live_test_count, total_failed, total_disabled);
> + if (timestamp) {
> + dprintf(jdata->fd, " timestamp=\"%s\"", timestamp);
> + free(timestamp);
> + timestamp = NULL;
> + }
> + if (time_str) {
> + dprintf(jdata->fd, " time=\"%s\"", time_str);
> + free(time_str);
> + time_str = NULL;
> + }
> + dprintf(jdata->fd, " name=\"AllTests\">\n");
> +
> + for (i = 0; i < case_count; ++i)
> + emit_case(jdata->fd, cases[i]);
> +
> + dprintf(jdata->fd, "</testsuites>\n");
> +
> + 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;
> +}
> diff --git a/tools/zunitc/src/zuc_junit_reporter.h b/tools/zunitc/src/zuc_junit_reporter.h
> new file mode 100644
> index 0000000..13f0409
> --- /dev/null
> +++ b/tools/zunitc/src/zuc_junit_reporter.h
> @@ -0,0 +1,34 @@
> +/*
> + * Copyright © 2015 Samsung Electronics Co., Ltd
> + *
> + * Permission to use, copy, modify, distribute, and sell this software and
> + * its documentation for any purpose is hereby granted without fee, provided
> + * that the above copyright notice appear in all copies and that both that
> + * copyright notice and this permission notice appear in supporting
> + * documentation, and that the name of the copyright holders not be used in
> + * advertising or publicity pertaining to distribution of the software
> + * without specific, written prior permission. The copyright holders make
> + * no representations about the suitability of this software for any
> + * purpose. It is provided "as is" without express or implied warranty.
> + *
> + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
> + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
> + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
> + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
> + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
> + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
> + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS 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 758987f..1fcb954 100644
> --- a/tools/zunitc/src/zunitc_impl.c
> +++ b/tools/zunitc/src/zunitc_impl.c
> @@ -43,6 +43,7 @@
> #include "zuc_collector.h"
> #include "zuc_context.h"
> #include "zuc_event_listener.h"
> +#include "zuc_junit_reporter.h"
> #include "zuc_tap_logger.h"
>
> #include "shared/config-parser.h"
> @@ -248,6 +249,11 @@ void zuc_set_output_tap(bool enable)
> g_ctx.output_tap = enable;
> }
>
> +void zuc_set_output_junit(bool enable)
> +{
> + g_ctx.output_junit = enable;
> +}
> +
> int zuc_initialize(int *argc, char *argv[], bool *help_flagged)
> {
> int rc = EXIT_FAILURE;
> @@ -258,6 +264,7 @@ int zuc_initialize(int *argc, char *argv[], bool *help_flagged)
> int opt_random = 0;
> int opt_break_on_failure = 0;
> int opt_tap = 0;
> + int opt_junit = 0;
> char *opt_filter = NULL;
>
> char *help_param = NULL;
> @@ -271,6 +278,7 @@ int zuc_initialize(int *argc, char *argv[], bool *help_flagged)
> { WESTON_OPTION_BOOLEAN, "zuc-break-on-failure", 0,
> &opt_break_on_failure },
> { WESTON_OPTION_BOOLEAN, "zuc-output-tap", 0, &opt_tap },
> + { WESTON_OPTION_BOOLEAN, "zuc-output-xml", 0, &opt_junit },
> { WESTON_OPTION_STRING, "zuc-filter", 0, &opt_filter },
> };
>
> @@ -330,6 +338,7 @@ int zuc_initialize(int *argc, char *argv[], bool *help_flagged)
> " --zuc-list-tests\n"
> " --zuc-nofork\n"
> " --zuc-output-tap\n"
> + " --zuc-output-xml\n"
> " --zuc-random=N [0|1|<seed number>]\n"
> " --zuc-repeat=N\n"
> " --help\n",
> @@ -346,6 +355,7 @@ int zuc_initialize(int *argc, char *argv[], bool *help_flagged)
> zuc_set_spawn(!opt_nofork);
> zuc_set_break_on_failure(opt_break_on_failure);
> zuc_set_output_tap(opt_tap);
> + zuc_set_output_junit(opt_junit);
> rc = EXIT_SUCCESS;
> }
>
> @@ -850,6 +860,8 @@ int zucimpl_run_tests(void)
> zuc_add_event_listener(zuc_base_logger_create());
> if (g_ctx.output_tap)
> zuc_add_event_listener(zuc_tap_logger_create());
> + if (g_ctx.output_junit)
> + zuc_add_event_listener(zuc_junit_reporter_create());
> }
>
> if (g_ctx.case_count < 1) {
> --
> 2.1.0
>
> _______________________________________________
> wayland-devel mailing list
> wayland-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/wayland-devel
Reviewed-by: Bryce Harrington <bryce at osg.samsung.com>
More information about the wayland-devel
mailing list