From 331a1aa4433046d2cf2a487b9bac0068b37c02f7 Mon Sep 17 00:00:00 2001 From: Trever Shick Date: Sat, 18 Sep 2021 13:06:50 -0500 Subject: [PATCH 1/4] Add JUnit XML support * Added an srunner_set_xml_format function and also an CK_XML_FORMAT_NAME environment variable to select JUnit formatting * Test timings are not supported at this time * Add tests to cover new functionality * Update documentation The implementation was inspired by Glenn Washburn's original patches. references - Original Patches - https://sourceforge.net/p/check/mailman/message/2963561/ --- doc/check.texi | 57 ++++++++++++++++++++++ src/check.c | 6 ++- src/check.h.in | 42 ++++++++++++++++ src/check_impl.h | 4 +- src/check_log.c | 88 +++++++++++++++++++++++++++++++++- src/check_log.h | 3 ++ src/check_print.c | 73 +++++++++++++++++++++++++++- src/check_print.h | 1 + src/check_run.c | 26 +++++----- src/check_str.c | 2 +- tests/check_check_log.c | 55 +++++++++++++++++++++ tests/ex_output.c | 12 ++++- tests/test_junit_xml_output.sh | 31 ++++++++++++ tests/test_output.sh | 4 ++ tests/test_output_strings | 54 +++++++++++++++++++++ 15 files changed, 437 insertions(+), 21 deletions(-) create mode 100755 tests/test_junit_xml_output.sh diff --git a/doc/check.texi b/doc/check.texi index f6852bc4..bf91de67 100644 --- a/doc/check.texi +++ b/doc/check.texi @@ -1930,6 +1930,8 @@ of to a file. @findex srunner_set_xml @findex srunner_has_xml @findex srunner_xml_fname +@findex srunner_xml_format +@findex srunner_set_xml_format The log can also be written in XML. The following functions define the interface for XML logs: @example @@ -1937,6 +1939,8 @@ the interface for XML logs: void srunner_set_xml (SRunner *sr, const char *fname); int srunner_has_xml (SRunner *sr); const char *srunner_xml_fname (SRunner *sr); +enum xml_format srunner_xml_format(SRunner * sr); +void srunner_set_xml_format(SRunner * sr, enum xml_format format); @end verbatim @end example @@ -2048,6 +2052,59 @@ If both plain text and XML log files are specified, by any of above methods, then check will log to both files. In other words logging in plain text and XML format simultaneously is supported. +JUnit Support is also available. It is enabled by a call to +@code{srunner_set_xml_format(CK_XML_FORMAT_JUNIT)} before the tests are run. +It can also be enabled by environment variable as well. It is enabled by setting the +@code{CK_XML_FORMAT_NAME} environment variable to @code{junit}. + +Here is an example of the JUnit xml format: +@example +@verbatim + + + + + + + + Core:test_fail:0 + ex_output.c:37 + Failure + + + + + Core:test_exit:0 + ex_output.c:46 + Early exit with return value 1 + + + + + + + + + Core:test_loop:0 + ex_output.c:72 + Iteration 0 failed + + + + + + + Core:test_loop:2 + ex_output.c:72 + Iteration 2 failed + + + + +@end verbatim +@end example + + @node TAP Logging, , Test Logging, Test Logging @subsection TAP Logging diff --git a/src/check.c b/src/check.c index 89df3451..da39ac3b 100644 --- a/src/check.c +++ b/src/check.c @@ -156,6 +156,7 @@ TCase *tcase_create(const char *name) tc->unch_tflst = check_list_create(); tc->ch_tflst = check_list_create(); tc->tags = check_list_create(); + tc->s = NULL; return tc; } @@ -246,6 +247,7 @@ void suite_add_tcase(Suite * s, TCase * tc) { return; } + tc->s = s; check_list_add_end(s->tclst, tc); } @@ -531,7 +533,7 @@ static void tr_init(TestResult * tr) tr->rtype = CK_TEST_RESULT_INVALID; tr->msg = NULL; tr->file = NULL; - tr->tcname = NULL; + tr->tc = NULL; tr->tname = NULL; tr->duration = -1; } @@ -571,7 +573,7 @@ enum ck_result_ctx tr_ctx(TestResult * tr) const char *tr_tcname(TestResult * tr) { - return tr->tcname; + return tr->tc->name; } static enum fork_status _fstat = CK_FORK; diff --git a/src/check.h.in b/src/check.h.in index 6bee9d3c..849ca2bc 100644 --- a/src/check.h.in +++ b/src/check.h.in @@ -2261,6 +2261,48 @@ CK_DLL_EXP int CK_EXPORT srunner_has_tap(SRunner * sr); */ CK_DLL_EXP const char *CK_EXPORT srunner_tap_fname(SRunner * sr); +/** + * enum describing the specific XML format used for XML logging + */ +enum xml_format { + CK_XML_FORMAT_UNSPECIFIED, + CK_XML_FORMAT_DEFAULT, // the default (original) format + CK_XML_FORMAT_JUNIT, // output in JUnit compatible XML +}; + +/** + * Returns the XML format used if XML is to be logged. + * + * This value can be explicitly set via `srunner_set_xml_format` or can + * be set via the CK_XML_FORMAT_NAME environment variable. + * + * @return CK_XML_FORMAT_DEFAULT unless the format is explicitly set via + * `srunner_set_xml_format(sr, CK_XML_FORMAT_JUNIT)` or + * `getenv("CK_XML_FORMAT_NAME")` returns "unit" + * + * @param sr suite runner to check + * + * @since 0.15.3. + */ +CK_DLL_EXP enum xml_format CK_EXPORT srunner_xml_format(SRunner * sr); + +/** + * Set the suite runner to output the result in an XML format compatible + * with JUnit's XML format. + * + * Note: XML format setting is an initialize only operation -- it should + * be done immediately after SRunner creation, and the XML format can't be + * changed after being set. + * + * This setting does not afffect the other log output types. + * + * @param sr suite runner to log results of in XML format + * @param format the xml_format to use + * + * @since 0.15.3 +*/ +CK_DLL_EXP void CK_EXPORT srunner_set_xml_format(SRunner * sr, enum xml_format format); + /** * Enum describing the current fork usage. */ diff --git a/src/check_impl.h b/src/check_impl.h index f4e8c590..f8ca35a7 100644 --- a/src/check_impl.h +++ b/src/check_impl.h @@ -59,6 +59,7 @@ struct TCase { const char *name; struct timespec timeout; + struct Suite *s; List *tflst; /* list of test functions */ List *unch_sflst; List *unch_tflst; @@ -82,7 +83,7 @@ struct TestResult int line; /* Line number where the test occurred */ int iter; /* The iteration value for looping tests */ int duration; /* duration of this test in microseconds */ - const char *tcname; /* Test case that generated the result */ + TCase *tc; /* Test case that generated the result */ const char *tname; /* Test that generated the result */ char *msg; /* Failure message */ }; @@ -121,6 +122,7 @@ struct SRunner List *resultlst; /* List of unit test results */ const char *log_fname; /* name of log file */ const char *xml_fname; /* name of xml output file */ + enum xml_format xml_format; const char *tap_fname; /* name of tap output file */ List *loglst; /* list of Log objects */ enum fork_status fstat; /* controls if suites are forked or not diff --git a/src/check_log.c b/src/check_log.c index 08446610..9f6fb9ef 100644 --- a/src/check_log.c +++ b/src/check_log.c @@ -22,6 +22,7 @@ #include #include +#include #include #if ENABLE_SUBUNIT #include @@ -63,6 +64,27 @@ const char *srunner_log_fname(SRunner * sr) return getenv("CK_LOG_FILE_NAME"); } +enum xml_format srunner_xml_format(SRunner * sr) +{ + // if the format as been explicitly set already via + // `srunner_set_xml_format`, then use that value + if (sr->xml_format != CK_XML_FORMAT_UNSPECIFIED) + return sr->xml_format; + + // junit is the only value of CK_XML_FORMAT_NAME that will + // return something other than CK_XML_FORMAT_DEFAULT + const char *format_name = getenv("CK_XML_FORMAT_NAME"); + if (format_name && strcmp(format_name, "junit") == 0) + return CK_XML_FORMAT_JUNIT; + + return CK_XML_FORMAT_DEFAULT; +} + +void srunner_set_xml_format(SRunner * sr, enum xml_format format) +{ + sr->xml_format = format; +} + void srunner_set_xml(SRunner * sr, const char *fname) { @@ -337,6 +359,65 @@ void xml_lfun(SRunner * sr CK_ATTRIBUTE_UNUSED, FILE * file, } +void junit_lfun(SRunner * sr CK_ATTRIBUTE_UNUSED, FILE * file, + enum print_output printmode CK_ATTRIBUTE_UNUSED, void *obj, + enum cl_event evt) +{ + // we're only interested in the end of the full run. + if (evt != CLEND_SR) return; + + TestResult *tr; + Suite *s; + TestStats stats; + + fprintf(file, "\n"); + fprintf(file, + "\n", + sr->stats->n_checked, sr->stats->n_errors, sr->stats->n_failed); + + // iterate over the suites + for (check_list_front(sr->slst); !check_list_at_end(sr->slst); check_list_advance(sr->slst)) { + s = (Suite*) check_list_val(sr->slst); + + // calculate the stats + stats.n_checked = stats.n_errors = stats.n_failed = 0; + for (check_list_front(sr->resultlst); !check_list_at_end(sr->resultlst); + check_list_advance(sr->resultlst)) { + tr = (TestResult *)check_list_val(sr->resultlst); + if (tr->tc->s != s) + continue; + stats.n_checked++; + if (tr->rtype == CK_FAILURE) + stats.n_failed++; + else if (tr->rtype == CK_ERROR) + stats.n_errors++; + } + + fprintf(file, " name); + fprintf(file, + "\"" + " tests=\"%d\"" + " errors=\"%d\"" + " failures=\"%d\"" + ">\n", + stats.n_checked, stats.n_errors, stats.n_failed); + for (check_list_front(sr->resultlst); !check_list_at_end(sr->resultlst); + check_list_advance(sr->resultlst)) { + tr = (TestResult *)check_list_val(sr->resultlst); + if (tr->tc->s != s) + continue; + tr_junitprint(file, tr, CK_VERBOSE); + } + fprintf(file, " \n"); + } + fprintf(file, "\n"); +} + void tap_lfun(SRunner * sr CK_ATTRIBUTE_UNUSED, FILE * file, enum print_output printmode CK_ATTRIBUTE_UNUSED, void *obj, enum cl_event evt) @@ -372,7 +453,7 @@ void tap_lfun(SRunner * sr CK_ATTRIBUTE_UNUSED, FILE * file, tr = (TestResult *)obj; fprintf(file, "%s %d - %s:%s:%s: %s\n", tr->rtype == CK_PASS ? "ok" : "not ok", num_tests_run, - tr->file, tr->tcname, tr->tname, tr->msg); + tr->file, tr->tc->name, tr->tname, tr->msg); fflush(file); break; default: @@ -520,7 +601,10 @@ void srunner_init_logging(SRunner * sr, enum print_output print_mode) f = srunner_open_xmlfile(sr); if(f) { - srunner_register_lfun(sr, f, f != stdout, xml_lfun, print_mode); + if (srunner_xml_format(sr) == CK_XML_FORMAT_JUNIT) + srunner_register_lfun(sr, f, f != stdout, junit_lfun, print_mode); + else + srunner_register_lfun(sr, f, f != stdout, xml_lfun, print_mode); } f = srunner_open_tapfile(sr); if(f) diff --git a/src/check_log.h b/src/check_log.h index 7223b987..4b47c96e 100644 --- a/src/check_log.h +++ b/src/check_log.h @@ -37,6 +37,9 @@ void lfile_lfun(SRunner * sr, FILE * file, enum print_output, void xml_lfun(SRunner * sr, FILE * file, enum print_output, void *obj, enum cl_event evt); +void junit_lfun(SRunner * sr, FILE * file, enum print_output, + void *obj, enum cl_event evt); + void tap_lfun(SRunner * sr, FILE * file, enum print_output, void *obj, enum cl_event evt); diff --git a/src/check_print.c b/src/check_print.c index a9f3aea4..559ded5c 100644 --- a/src/check_print.c +++ b/src/check_print.c @@ -223,7 +223,7 @@ void tr_xmlprint(FILE * file, TestResult * tr, tr->duration < 0 ? -1 : tr->duration / US_PER_SEC, tr->duration < 0 ? 0 : tr->duration % US_PER_SEC); fprintf(file, " "); - fprint_xml_esc(file, tr->tcname); + fprint_xml_esc(file, tr->tc->name); fprintf(file, "\n"); fprintf(file, " "); fprint_xml_esc(file, tr->msg); @@ -233,6 +233,77 @@ void tr_xmlprint(FILE * file, TestResult * tr, free(path_name); } +void tr_junitprint(FILE * file, TestResult * tr, + enum print_output print_mode CK_ATTRIBUTE_UNUSED) +{ + char status[10]; + char type[10]; + char *path_name = NULL; + char *file_name = NULL; + char *slash = NULL; + switch (tr->rtype) + { + case CK_PASS: + snprintf(status, sizeof(status), "%s", "success"); + break; + case CK_FAILURE: + snprintf(status, sizeof(status), "%s", "failure"); + break; + case CK_ERROR: + snprintf(status, sizeof(status), "%s", "error"); + break; + case CK_TEST_RESULT_INVALID: + default: + abort(); + break; + } + + if(tr->file) + { + slash = strrchr(tr->file, '/'); + if(slash == NULL) + { + slash = strrchr(tr->file, '\\'); + } + + if(slash == NULL) + { + path_name = strdup("."); + file_name = tr->file; + } + else + { + path_name = strdup(tr->file); + path_name[slash - tr->file] = 0; /* Terminate the temporary string. */ + file_name = slash + 1; + } + } + + fprintf(file, " tc->name); + fprintf(file, + "\"" + " name=\"%s\"" + ">\n", + tr->tname); + + if (tr->rtype == CK_FAILURE || tr->rtype == CK_ERROR) { + fprintf(file, " <%s message=\"", status); + fprint_xml_esc(file, tr->msg); + fprintf(file, "\">\n"); + fprintf(file, " "); + fprint_xml_esc(file, tr->tc->name); + fprintf(file, ":%s:%d\n", tr->tname, tr->iter); + fprintf(file, " %s:%d\n", file_name, tr->line); + fprintf(file, " "); + fprint_xml_esc(file, tr->msg); + fprintf(file, "\n"); + fprintf(file, " \n", status); + } + fprintf(file, " \n"); +} + enum print_output get_env_printmode(void) { char *env = getenv("CK_VERBOSITY"); diff --git a/src/check_print.h b/src/check_print.h index eabd8b9a..bd06afed 100644 --- a/src/check_print.h +++ b/src/check_print.h @@ -25,6 +25,7 @@ void fprint_xml_esc(FILE * file, const char *str); void tr_fprint(FILE * file, TestResult * tr, enum print_output print_mode); void tr_xmlprint(FILE * file, TestResult * tr, enum print_output print_mode); +void tr_junitprint(FILE * file, TestResult * tr, enum print_output print_mode); void srunner_fprint(FILE * file, SRunner * sr, enum print_output print_mode); enum print_output get_env_printmode(void); diff --git a/src/check_run.c b/src/check_run.c index 5f160e50..1692fe7b 100644 --- a/src/check_run.c +++ b/src/check_run.c @@ -70,7 +70,7 @@ static void srunner_iterate_suites(SRunner * sr, static void srunner_iterate_tcase_tfuns(SRunner * sr, TCase * tc); static void srunner_add_failure(SRunner * sr, TestResult * tf); static TestResult * srunner_run_setup(List * func_list, - enum fork_status fork_usage, const char * test_name, + enum fork_status fork_usage, struct TCase *tc, const char * setup_name); static int srunner_run_unchecked_setup(SRunner * sr, TCase * tc); static TestResult *tcase_run_checked_setup(SRunner * sr, TCase * tc); @@ -80,7 +80,7 @@ static void tcase_run_checked_teardown(TCase * tc); static void srunner_run_tcase(SRunner * sr, TCase * tc); static TestResult *tcase_run_tfun_nofork(SRunner * sr, TCase * tc, TF * tf, int i); -static TestResult *receive_result_info_nofork(const char *tcname, +static TestResult *receive_result_info_nofork(struct TCase *tc, const char *tname, int iter, int duration); static void set_nofork_info(TestResult * tr); @@ -89,7 +89,7 @@ static char *pass_msg(void); #if defined(HAVE_FORK) && HAVE_FORK==1 static TestResult *tcase_run_tfun_fork(SRunner * sr, TCase * tc, TF * tf, int i); -static TestResult *receive_result_info_fork(const char *tcname, +static TestResult *receive_result_info_fork(struct TCase *tc, const char *tname, int iter, int status, int expected_signal, signed char allowed_exit_value); @@ -288,7 +288,7 @@ static void srunner_add_failure(SRunner * sr, TestResult * tr) } static TestResult * srunner_run_setup(List * fixture_list, enum fork_status fork_usage, - const char * test_name, const char * setup_name) + struct TCase *tc, const char * setup_name) { TestResult *tr = NULL; @@ -312,7 +312,7 @@ static TestResult * srunner_run_setup(List * fixture_list, enum fork_status fork } /* Stop the setup and return the failure in nofork mode. */ - tr = receive_result_info_nofork(test_name, setup_name, 0, -1); + tr = receive_result_info_nofork(tc, setup_name, 0, -1); if(tr->rtype != CK_PASS) { break; @@ -338,7 +338,7 @@ static int srunner_run_unchecked_setup(SRunner * sr, TCase * tc) int rval = 1; set_fork_status(CK_NOFORK); - tr = srunner_run_setup(tc->unch_sflst, CK_NOFORK, tc->name, "unchecked_setup"); + tr = srunner_run_setup(tc->unch_sflst, CK_NOFORK, tc, "unchecked_setup"); set_fork_status(srunner_fork_status(sr)); if(tr != NULL && tr->rtype != CK_PASS) @@ -353,7 +353,7 @@ static int srunner_run_unchecked_setup(SRunner * sr, TCase * tc) static TestResult *tcase_run_checked_setup(SRunner * sr, TCase * tc) { TestResult *tr = srunner_run_setup(tc->ch_sflst, srunner_fork_status(sr), - tc->name, "checked_setup"); + tc, "checked_setup"); return tr; } @@ -421,14 +421,14 @@ static TestResult *tcase_run_tfun_nofork(SRunner * sr, TCase * tc, TF * tfun, } clock_gettime(check_get_clockid(), &ts_end); tcase_run_checked_teardown(tc); - return receive_result_info_nofork(tc->name, tfun->ttest->name, i, + return receive_result_info_nofork(tc, tfun->ttest->name, i, DIFF_IN_USEC(ts_start, ts_end)); } return tr; } -static TestResult *receive_result_info_nofork(const char *tcname, +static TestResult *receive_result_info_nofork(struct TCase *tc, const char *tname, int iter, int duration) { @@ -441,7 +441,7 @@ static TestResult *receive_result_info_nofork(const char *tcname, } else { - tr->tcname = tcname; + tr->tc = tc; tr->tname = tname; tr->iter = iter; tr->duration = duration; @@ -538,11 +538,11 @@ static TestResult *tcase_run_tfun_fork(SRunner * sr, TCase * tc, TF * tfun, killpg(pid, SIGKILL); /* Kill remaining processes. */ - return receive_result_info_fork(tc->name, tfun->ttest->name, i, status, + return receive_result_info_fork(tc, tfun->ttest->name, i, status, tfun->signal, tfun->allowed_exit_value); } -static TestResult *receive_result_info_fork(const char *tcname, +static TestResult *receive_result_info_fork(struct TCase* tc, const char *tname, int iter, int status, int expected_signal, @@ -557,7 +557,7 @@ static TestResult *receive_result_info_fork(const char *tcname, } else { - tr->tcname = tcname; + tr->tc = tc; tr->tname = tname; tr->iter = iter; set_fork_info(tr, status, expected_signal, allowed_exit_value); diff --git a/src/check_str.c b/src/check_str.c index 8dabdcc6..e998b0f0 100644 --- a/src/check_str.c +++ b/src/check_str.c @@ -41,7 +41,7 @@ char *tr_str(TestResult * tr) rstr = ck_strdup_printf("%s:%d:%s:%s:%s:%d: %s%s", tr->file, tr->line, - tr_type_str(tr), tr->tcname, tr->tname, tr->iter, + tr_type_str(tr), tr->tc->name, tr->tname, tr->iter, exact_msg, tr->msg); return rstr; diff --git a/tests/check_check_log.c b/tests/check_check_log.c index ee36fde5..f6d2ff46 100644 --- a/tests/check_check_log.c +++ b/tests/check_check_log.c @@ -200,6 +200,55 @@ START_TEST(test_double_set_xml) } END_TEST +START_TEST(test_default_xml_format) { + Suite *s = suite_create("Suite"); + SRunner *sr = srunner_create(s); + ck_assert_msg(srunner_xml_format(sr) == CK_XML_FORMAT_DEFAULT, + "XML format is not default"); + srunner_free(sr); +} +END_TEST + +START_TEST(test_set_xml_format) { + Suite *s = suite_create("Suite"); + SRunner *sr = srunner_create(s); + srunner_set_xml_format(sr, CK_XML_FORMAT_JUNIT); + ck_assert_msg(srunner_xml_format(sr) == CK_XML_FORMAT_JUNIT, + "XML format is not Junit"); + srunner_free(sr); +} +END_TEST + +#if HAVE_DECL_SETENV +/* Test enabling JUnit XML format logging via environment variable */ +START_TEST(test_set_xml_format_env) +{ + const char *old_val; + Suite *s = suite_create("Suite"); + SRunner *sr = srunner_create(s); + + /* check that setting XML format via environment variable works */ + ck_assert_msg(save_set_env("CK_XML_FORMAT_NAME", "junit", &old_val) == 0, + "Failed to set environment variable"); + + ck_assert_msg(srunner_xml_format(sr) == CK_XML_FORMAT_JUNIT, + "SRunner not set to use Junit"); + + /* check that explicit call to srunner_set_xml_format() overrides environment + * variable */ + srunner_set_xml_format(sr, CK_XML_FORMAT_DEFAULT); + ck_assert_msg(srunner_xml_format(sr) == CK_XML_FORMAT_DEFAULT, + "SRunner not using explicitly set XML format"); + + /* restore old environment */ + ck_assert_msg(restore_env("CK_XML_FORMAT_NAME", old_val) == 0, + "Failed to restore environment variable"); + + srunner_free(sr); +} +END_TEST +#endif /* HAVE_DECL_SETENV */ + START_TEST(test_set_tap) { Suite *s = suite_create("Suite"); @@ -302,6 +351,12 @@ Suite *make_log_suite(void) tcase_add_test(tc_core_xml, test_no_set_xml); tcase_add_test(tc_core_xml, test_double_set_xml); + tcase_add_test(tc_core_xml, test_default_xml_format); + tcase_add_test(tc_core_xml, test_set_xml_format); +#if HAVE_DECL_SETENV + tcase_add_test(tc_core_xml, test_set_xml_format_env); +#endif + suite_add_tcase(s, tc_core_tap); tcase_add_test(tc_core_tap, test_set_tap); #if HAVE_DECL_SETENV diff --git a/tests/ex_output.c b/tests/ex_output.c index 89006cf8..0ded1d02 100644 --- a/tests/ex_output.c +++ b/tests/ex_output.c @@ -136,7 +136,7 @@ static void print_usage(void) printf(" | CK_SUBUNIT"); #endif printf(")\n"); - printf(" (STDOUT | STDOUT_DUMP | LOG | LOG_STDOUT | TAP | TAP_STDOUT | XML | XML_STDOUT)\n"); + printf(" (STDOUT | STDOUT_DUMP | LOG | LOG_STDOUT | TAP | TAP_STDOUT | XML | XML_STDOUT | JUNIT_XML | JUNIT_XML_STDOUT )\n"); printf(" (NORMAL | EXIT_TEST)\n"); printf(" If CK_ENV is used, the environment variable CK_VERBOSITY can be set to\n"); printf(" one of these: silent, minimal, or verbose. If it is not set to these, or\n"); @@ -190,6 +190,16 @@ static void run_tests(enum print_output printmode, char *log_type, int include_e { srunner_set_xml(sr, "-"); } + else if(strcmp(log_type, "JUNIT_XML") == 0) + { + srunner_set_xml(sr, "junit_test.xml"); + srunner_set_xml_format(sr, CK_XML_FORMAT_JUNIT); + } + else if(strcmp(log_type, "JUNIT_XML_STDOUT") == 0) + { + srunner_set_xml(sr, "-"); + srunner_set_xml_format(sr, CK_XML_FORMAT_JUNIT); + } else { print_usage(); diff --git a/tests/test_junit_xml_output.sh b/tests/test_junit_xml_output.sh new file mode 100755 index 00000000..eeed46fd --- /dev/null +++ b/tests/test_junit_xml_output.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env sh + +OUTPUT_FILE=junit_test.xml +CK_DEFAULT_TIMEOUT=4 + +. ./test_vars +. $(dirname $0)/test_output_strings + +rm -f ${OUTPUT_FILE} +export CK_DEFAULT_TIMEOUT +./ex_output${EXEEXT} CK_MINIMAL JUNIT_XML NORMAL > /dev/null +actual_junit_xml=`cat ${OUTPUT_FILE} | tr -d "\r" | grep -v \ | grep -v \ | grep -v \` +if [ x"${expected_junit_xml}" != x"${actual_junit_xml}" ]; then + echo "Problem with ex_xml_output${EXEEXT}"; + echo "Expected:"; + echo "${expected_junit_xml}"; + echo "Got:"; + echo "${actual_junit_xml}"; + exit 1; +fi + +if [ ! -z `which xmllint` ]; then + xmllint_output=`xmllint ${OUTPUT_FILE}` + if [ $? -ne 0 ]; then + echo "xmllint found an issue" + echo ${xmllint_output} + exit 1 + fi +fi + +exit 0 diff --git a/tests/test_output.sh b/tests/test_output.sh index c1b31bd0..5935d2c4 100755 --- a/tests/test_output.sh +++ b/tests/test_output.sh @@ -59,6 +59,8 @@ tap_stdout=` ./ex_output${EXEEXT} CK_SILENT TAP_STDO tap_env_stdout=`CK_TAP_LOG_FILE_NAME="-" ./ex_output${EXEEXT} CK_SILENT STDOUT NORMAL` xml_stdout=` ./ex_output${EXEEXT} CK_SILENT XML_STDOUT NORMAL | tr -d "\r" | grep -v \ | grep -v \ | grep -v \` xml_env_stdout=`CK_XML_LOG_FILE_NAME="-" ./ex_output${EXEEXT} CK_SILENT STDOUT NORMAL | tr -d "\r" | grep -v \ | grep -v \ | grep -v \` +junit_xml_stdout=` ./ex_output${EXEEXT} CK_SILENT JUNIT_XML_STDOUT NORMAL | tr -d "\r" | grep -v \ | grep -v \ | grep -v \` +junit_xml_env_stdout=`CK_XML_LOG_FILE_NAME="-" CK_XML_FORMAT_NAME="junit" ./ex_output${EXEEXT} CK_SILENT STDOUT NORMAL` test_output ( ) { if [ "x${1}" != "x${2}" ]; then @@ -92,6 +94,8 @@ test_output "${expected_log_log}" "${log_stdout}" "CK_SILENT LOG_STDOUT N test_output "${expected_log_log}" "${log_env_stdout}" "CK_SILENT STDOUT NORMAL (with log env = '-')" test_output "${expected_xml}" "${xml_stdout}" "CK_SILENT XML_STDOUT NORMAL" test_output "${expected_xml}" "${xml_env_stdout}" "CK_SILENT STDOUT NORMAL (with xml env = '-')" +test_output "${expected_junit_xml}" "${junit_xml_stdout}" "CK_SILENT JUNIT_XML_STDOUT NORMAL" +test_output "${expected_junit_xml}" "${junit_xml_env_stdout}" "CK_SILENT STDOUT NORMAL (with junit_xml env = '-')" test_output "${expected_normal_tap}" "${tap_stdout}" "CK_SILENT TAP_STDOUT NORMAL" test_output "${expected_normal_tap}" "${tap_env_stdout}" "CK_SILENT STDOUT NORMAL (with tap env = '-')" diff --git a/tests/test_output_strings b/tests/test_output_strings index 21751b61..2c4022b4 100644 --- a/tests/test_output_strings +++ b/tests/test_output_strings @@ -293,6 +293,60 @@ expected_xml=" expected_duration_count=8 fi +################## +# junit xml output +################## +expected_junit_xml=" + + + + + + + Core:test_fail:0 + ex_output.c:37 + Failure + + + + + Core:test_exit:0 + ex_output.c:46 + Early exit with return value 1 + + + + + + + + + Core:test_loop:0 + ex_output.c:72 + Iteration 0 failed + + + + + + + Core:test_loop:2 + ex_output.c:72 + Iteration 2 failed + + + + + + + description " ' < > & X end:test_xml_esc_fail_msg:0 + ex_output.c:78 + fail " ' < > & X message + + + +" + ################## # tap output ################## From f6d558998832107b2be878beb0e34722eca246b9 Mon Sep 17 00:00:00 2001 From: Trever Shick Date: Sat, 18 Sep 2021 20:49:21 -0500 Subject: [PATCH 2/4] add missing free, remove unused variable --- src/check_print.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/check_print.c b/src/check_print.c index 559ded5c..b0b7f8be 100644 --- a/src/check_print.c +++ b/src/check_print.c @@ -237,7 +237,6 @@ void tr_junitprint(FILE * file, TestResult * tr, enum print_output print_mode CK_ATTRIBUTE_UNUSED) { char status[10]; - char type[10]; char *path_name = NULL; char *file_name = NULL; char *slash = NULL; @@ -302,6 +301,7 @@ void tr_junitprint(FILE * file, TestResult * tr, fprintf(file, " \n", status); } fprintf(file, " \n"); + free(path_name); } enum print_output get_env_printmode(void) From ee51d1c08e3699f09f0c2abc8095f71946f87ccd Mon Sep 17 00:00:00 2001 From: Trever Shick Date: Fri, 29 Oct 2021 19:10:45 -0500 Subject: [PATCH 3/4] Initialize xml_format in srunner_create --- src/check.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/check.c b/src/check.c index da39ac3b..201c020e 100644 --- a/src/check.c +++ b/src/check.c @@ -414,6 +414,7 @@ SRunner *srunner_create(Suite * s) sr->resultlst = check_list_create(); sr->log_fname = NULL; sr->xml_fname = NULL; + sr->xml_format = CK_XML_FORMAT_UNSPECIFIED; sr->tap_fname = NULL; sr->loglst = NULL; From 2e7563c8ed5464f1d10628ba40a8d932ff868527 Mon Sep 17 00:00:00 2001 From: Trever Shick Date: Fri, 29 Oct 2021 19:12:56 -0500 Subject: [PATCH 4/4] Update documentation * JUnit Support -> JUnit XML Support * Combine two sentences in check.texi * xml->XML * Add a line in the environment appendix * Update the xml_format source code header comment * Add a comment to xml_format --- doc/check.texi | 11 ++++++----- src/check.h.in | 8 ++++++-- src/check_impl.h | 2 +- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/doc/check.texi b/doc/check.texi index bf91de67..c1e16ea7 100644 --- a/doc/check.texi +++ b/doc/check.texi @@ -2052,12 +2052,11 @@ If both plain text and XML log files are specified, by any of above methods, then check will log to both files. In other words logging in plain text and XML format simultaneously is supported. -JUnit Support is also available. It is enabled by a call to -@code{srunner_set_xml_format(CK_XML_FORMAT_JUNIT)} before the tests are run. -It can also be enabled by environment variable as well. It is enabled by setting the -@code{CK_XML_FORMAT_NAME} environment variable to @code{junit}. +JUnit XML Support is also available. It is enabled by a call to +@code{srunner_set_xml_format(CK_XML_FORMAT_JUNIT)} before the tests are run. +It can also be enabled by setting the environment variable @code{CK_XML_FORMAT_NAME} to @code{junit}. -Here is an example of the JUnit xml format: +Here is an example of the JUnit XML format: @example @verbatim @@ -2364,6 +2363,8 @@ CK_LOG_FILE_NAME: Filename to write logs to. See section @ref{Test Logging}. CK_XML_LOG_FILE_NAME: Filename to write XML log to. See section @ref{XML Logging}. +CK_XML_FORMAT_NAME: Name of the XML format to use. ``junit'' will output in JUnit XML format, all other values will yield the default XML format. @ref{XML Logging}. + CK_TAP_LOG_FILE_NAME: Filename to write TAP (Test Anything Protocol) output to. See section @ref{TAP Logging}. CK_MAX_MSG_SIZE: Maximal assertion message size. diff --git a/src/check.h.in b/src/check.h.in index 9410a4d1..6c504299 100644 --- a/src/check.h.in +++ b/src/check.h.in @@ -2285,11 +2285,15 @@ enum xml_format { * Returns the XML format used if XML is to be logged. * * This value can be explicitly set via `srunner_set_xml_format` or can - * be set via the CK_XML_FORMAT_NAME environment variable. + * be set via the CK_XML_FORMAT_NAME environment variable. * + * This setting does not conflict with the other log output types; + * all logging types can occur concurrently if configured. * @return CK_XML_FORMAT_DEFAULT unless the format is explicitly set via * `srunner_set_xml_format(sr, CK_XML_FORMAT_JUNIT)` or - * `getenv("CK_XML_FORMAT_NAME")` returns "unit" + * `getenv("CK_XML_FORMAT_NAME")` is set to a known value. + * Any value set explicitly via `srunner_set_xml_format` will take + * precedence over the environment variable. * * @param sr suite runner to check * diff --git a/src/check_impl.h b/src/check_impl.h index f8ca35a7..b45c134e 100644 --- a/src/check_impl.h +++ b/src/check_impl.h @@ -122,7 +122,7 @@ struct SRunner List *resultlst; /* List of unit test results */ const char *log_fname; /* name of log file */ const char *xml_fname; /* name of xml output file */ - enum xml_format xml_format; + enum xml_format xml_format; /* the xml format to use */ const char *tap_fname; /* name of tap output file */ List *loglst; /* list of Log objects */ enum fork_status fstat; /* controls if suites are forked or not