[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