Daniel Gollub (pthreads support)
Friedrich Beckmann (mingw and msvc port)
Roy Merkel (specified test exit value)
+ Robert Collins (subunit support)
Anybody who has contributed code to Check or Check's build system is
considered an author. Send patches to this file to
+In development:
+
+* Added CK_SUBUNIT support for outputting test information in the subunit wire
+ protocol. See the check manual for more information. (Contributed by Robert
+ Collins).
+
Mon, Dec 29, 2009: Released Check 0.9.6
based on r453 (2008-12-01 22:14:15).
AM_CONDITIONAL(NO_TIMEOUT_TESTS, test x"$enable_timeout_tests" = "xfalse")
+AC_ARG_ENABLE(subunit,
+AC_HELP_STRING([--enable-subunit],
+ [enable support for the subunit test protocol @<:@default=autodetect@:>@]),
+[case "${enableval}" in
+ yes)
+ enable_subunit=true
+ echo "Enabled subunit support"
+ ;;
+ no)
+ enable_subunit=false
+ echoo "Disabled subunit support"
+ ;;
+ autodetect)
+ echo "Subunit support will enable automatically."
+ ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-subunit) ;;
+esac],
+[echo "Subunit support will enable automatically."
+ enable_subunit=autodetect])
+
# Checks for programs.
AC_PROG_AWK
AC_PROG_CC
AC_HEADER_SYS_WAIT
AC_CHECK_HEADERS([fcntl.h stddef.h stdint.h stdlib.h string.h sys/time.h unistd.h])
+if test xfalse != x"$enable_subunit"; then
+AC_CHECK_LIB(subunit, subunit_test_start, ,
+[case "$enable_subunit" in
+ autodetect)
+ enable_subunit=false
+ ;;
+ true)
+ AC_MSG_ERROR([libsubunit is required for subunit protocol support. The homepage for subunit is https://launchpad.net/subunit/])
+ ;;
+ esac
+])
+fi
+if test xfalse != x"$enable_subunit"; then
+AC_CHECK_HEADER([subunit/child.h], ,
+[case "$enable_subunit" in
+ autodetect)
+ enable_subunit=false
+ ;;
+ true)
+ AC_MSG_ERROR([The header subunit/child.h could not be succesfully included and is required for subunit protocol support. The homepage for subunit is https://launchpad.net/subunit/])
+ ;;
+ esac
+])
+fi
+if test xfalse = x"$enable_subunit"; then
+ENABLE_SUBUNIT="0"
+else
+ENABLE_SUBUNIT="1"
+fi
+AC_SUBST(ENABLE_SUBUNIT)
+AC_DEFINE_UNQUOTED(ENABLE_SUBUNIT, $ENABLE_SUBUNIT, [Subunit protocol result output])
+
+AM_CONDITIONAL(SUBUNIT, test x"$enable_subunit" != "xfalse")
+
+
+
# Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_TYPE_PID_T
lib/Makefile
src/check.h
src/Makefile
- tests/Makefile])
+ tests/Makefile
+ tests/test_vars])
AC_OUTPUT
@author Chris Pickett
@author Fredrik Hugosson
@author Robert Lemmen
+@author Robert Collins
@c The following two commands start the copyright page.
@page
* Test Timeouts::
* Determining Test Coverage::
* Test Logging::
+* Subunit Support::
Test Fixtures
which can have the values "silent", "minimal", "normal", "verbose". If
the variable is not found or the value is not recognized, the print
mode is set to @code{CK_NORMAL}.
+
+@vindex CK_SUBUNIT
+@item CK_SUBUNIT
+Prints running progress through the @uref{https://launchpad.net/subunit/,
+subunit} test runner protocol. See 'subunit support' under the Advanced Features section for more information.
@end table
With the @code{CK_NORMAL} flag specified in our @code{main()}, let's
* Test Timeouts::
* Determining Test Coverage::
* Test Logging::
+* Subunit Support::
@end menu
@node Running Multiple Cases, No Fork Mode, Advanced Features, Advanced Features
you. For more information or help with other compilers, please refer
to the relevant manuals.
-@node Test Logging, , Determining Test Coverage, Advanced Features
+@node Test Logging, Subunit Support, Determining Test Coverage, Advanced Features
@section Test Logging
@findex srunner_set_log()
@end verbatim
@end example
+@node Subunit Support, , Test Logging, Advanced Features
+@section Subunit Support
+
+Check supports running test suites with subunit output. This can be useful to
+combine test results from multiple languages, or to perform programmatic
+analysis on the results of multiple check test suites or otherise handle test
+results in a programmatic manner. Using subunit with check is very straight
+forward. There are two steps:
+1) In your check test suite driver pass 'CK_SUBUNIT' as the output mode
+for your srunner.
+@example
+@verbatim
+SRunner *sr;
+sr = srunner_create (make_s1_suite ());
+srunner_add_suite (sr, make_s2_suite ());
+srunner_run_all (sr, CK_SUBUNIT);
+@end verbatim
+@end example
+2) Setup your main language test runner to run your check based test
+executable. For instance using python:
+@example
+@verbatim
+
+import subunit
+
+class ShellTests(subunit.ExecTestCase):
+ """Run some tests from the C codebase."""
+
+ def test_group_one(self):
+ """./foo/check_driver"""
+
+ def test_group_two(self):
+ """./foo/other_driver"""
+@end verbatim
+@end example
+
+In this example, running the test suite ShellTests in python (using any test
+runner - unittest.py, tribunal, trial, nose or others) will run
+./foo/check_driver and ./foo/other_driver and report on their result.
+
+Subunit is hosted on launchpad - the @uref{https://launchpad.net/subunit/,
+subunit} project there contains bug tracker, future plans, and source code
+control details.
+
@node Conclusion and References, AM_PATH_CHECK, Advanced Features, Top
@chapter Conclusion and References
The tutorial and description of advanced features has provided an
CK_NORMAL, /* All failed tests */
CK_VERBOSE, /* All tests */
CK_ENV, /* Look at environment var */
+#if @ENABLE_SUBUNIT@
+ CK_SUBUNIT, /* Run as a subunit child process */
+#endif
CK_LAST
};
CLSTART_S,
CLEND_SR,
CLEND_S,
+ CLSTART_T, /* A test case is about to run */
CLEND_T
};
#include <stdlib.h>
#include <stdio.h>
#include <check.h>
+#if HAVE_SUBUNIT_CHILD_H
+#include <subunit/child.h>
+#endif
#include "check_error.h"
#include "check_list.h"
#include "check_impl.h"
#include "check_log.h"
#include "check_print.h"
+#include "check_str.h"
static void srunner_send_evt (SRunner *sr, void *obj, enum cl_event evt);
srunner_send_evt (sr, s, CLEND_S);
}
+void log_test_start (SRunner *sr, TCase * tc, TF * tfun)
+{
+ char buffer[100];
+ snprintf(buffer, 99, "%s:%s", tc->name, tfun->name);
+ srunner_send_evt (sr, buffer, CLSTART_T);
+}
+
void log_test_end (SRunner *sr, TestResult *tr)
{
srunner_send_evt (sr, tr, CLEND_T);
void stdout_lfun (SRunner *sr, FILE *file, enum print_output printmode,
void *obj, enum cl_event evt)
{
- TestResult *tr;
Suite *s;
if (printmode == CK_ENV) {
case CLEND_S:
s = obj;
break;
+ case CLSTART_T:
+ break;
case CLEND_T:
- tr = obj;
break;
default:
eprintf("Bad event type received in stdout_lfun", __FILE__, __LINE__);
case CLEND_S:
s = obj;
break;
+ case CLSTART_T:
+ break;
case CLEND_T:
tr = obj;
tr_fprint(file, tr, CK_VERBOSE);
break;
default:
- eprintf("Bad event type received in stdout_lfun", __FILE__, __LINE__);
+ eprintf("Bad event type received in lfile_lfun", __FILE__, __LINE__);
}
fprintf(file, " </suite>\n");
s = obj;
break;
+ case CLSTART_T:
+ break;
case CLEND_T:
tr = obj;
tr_xmlprint(file, tr, CK_VERBOSE);
}
+#if ENABLE_SUBUNIT
+void subunit_lfun (SRunner *sr, FILE *file, enum print_output printmode,
+ void *obj, enum cl_event evt)
+{
+ TestResult *tr;
+ Suite *s;
+ char const * name;
+
+ /* assert(printmode == CK_SUBUNIT); */
+
+ switch (evt) {
+ case CLINITLOG_SR:
+ break;
+ case CLENDLOG_SR:
+ break;
+ case CLSTART_SR:
+ break;
+ case CLSTART_S:
+ s = obj;
+ break;
+ case CLEND_SR:
+ if (printmode > CK_SILENT) {
+ fprintf (file, "\n");
+ srunner_fprint (file, sr, printmode);
+ }
+ break;
+ case CLEND_S:
+ s = obj;
+ break;
+ case CLSTART_T:
+ name = obj;
+ subunit_test_start(name);
+ break;
+ case CLEND_T:
+ tr = obj;
+ {
+ char *name = ck_strdup_printf ("%s:%s", tr->tcname, tr->tname);
+ char *msg = tr_short_str (tr);
+ switch (tr->rtype) {
+ case CK_PASS:
+ subunit_test_pass(name);
+ break;
+ case CK_FAILURE:
+ subunit_test_fail(name, msg);
+ break;
+ case CK_ERROR:
+ subunit_test_error(name, msg);
+ break;
+ default:
+ eprintf("Bad result type in subunit_lfun", __FILE__, __LINE__);
+ free(name);
+ free(msg);
+ }
+ }
+ break;
+ default:
+ eprintf("Bad event type received in subunit_lfun", __FILE__, __LINE__);
+ }
+}
+#endif
FILE *srunner_open_lfile (SRunner *sr)
{
{
FILE *f;
sr->loglst = check_list_create();
- srunner_register_lfun (sr, stdout, 0, stdout_lfun, print_mode);
+#if ENABLE_SUBUNIT
+ if (print_mode != CK_SUBUNIT)
+#endif
+ srunner_register_lfun (sr, stdout, 0, stdout_lfun, print_mode);
+#if ENABLE_SUBUNIT
+ else
+ srunner_register_lfun (sr, stdout, 0, subunit_lfun, print_mode);
+#endif
f = srunner_open_lfile (sr);
if (f) {
srunner_register_lfun (sr, f, 1, lfile_lfun, print_mode);
void log_suite_start (SRunner *sr, Suite *s);
void log_suite_end (SRunner *sr, Suite *s);
void log_test_end (SRunner *sr, TestResult *tr);
+void log_test_start (SRunner *sr, TCase *tc, TF *tfun);
void stdout_lfun (SRunner *sr, FILE *file, enum print_output,
void *obj, enum cl_event evt);
void xml_lfun (SRunner *sr, FILE *file, enum print_output,
void *obj, enum cl_event evt);
+void subunit_lfun (SRunner *sr, FILE *file, enum print_output,
+ void *obj, enum cl_event evt);
+
void srunner_register_lfun (SRunner *sr, FILE *lfile, int close,
LFun lfun, enum print_output);
static void srunner_fprint_summary (FILE *file, SRunner *sr,
enum print_output print_mode)
{
+#if ENABLE_SUBUNIT
+ if (print_mode == CK_SUBUNIT)
+ return;
+#endif
+
if (print_mode >= CK_MINIMAL) {
char *str;
enum print_output print_mode)
{
List *resultlst;
+
+#if ENABLE_SUBUNIT
+ if (print_mode == CK_SUBUNIT)
+ return;
+#endif
resultlst = sr->resultlst;
for (i = tfun->loop_start; i < tfun->loop_end; i++)
{
+ log_test_start (sr, tc, tfun);
switch (srunner_fork_status(sr)) {
case CK_FORK:
#ifdef _POSIX_VERSION
return rstr;
}
+char *tr_short_str (TestResult *tr)
+{
+ const char *exact_msg;
+ char *rstr;
+
+ exact_msg = (tr->rtype == CK_ERROR) ? "(after this point) ": "";
+
+ rstr = ck_strdup_printf ("%s:%d: %s%s",
+ tr->file, tr->line,
+ exact_msg, tr->msg);
+
+ return rstr;
+}
+
char *sr_stat_str (SRunner *sr)
{
char *str;
value has been malloc'd, and must be freed by the caller */
char *tr_str (TestResult *tr);
+/* Return a string representation of the given TestResult message
+ without the test id or result type. This is suitable for separate
+ formatting of the test and the message. Return value has been
+ malloc'd, and must be freed by the caller */
+char *tr_short_str (TestResult *tr);
+
/* Return a string representation of the given SRunner's run
statistics (% passed, num run, passed, errors, failures). Return
value has been malloc'd, and must be freed by the caller
ex_xml_output \
ex_log_output
-EXTRA_DIST = test_output.sh test_log_output.sh test_xml_output.sh
+EXTRA_DIST = test_output.sh test_log_output.sh test_vars.in test_xml_output.sh
if NO_TIMEOUT_TESTS
check_check_CFLAGS = -DTIMEOUT_TESTS_ENABLED=0
check_check_export_LDADD = $(top_builddir)/src/libcheck.la $(top_builddir)/lib/libcompat.la
check_check_SOURCES = \
- check_check.h \
- check_list.c \
- check_check_sub.c \
- check_check_master.c \
- check_check_msg.c \
- check_check_log.c \
- check_check_limit.c \
- check_check_fork.c \
- check_check_fixture.c \
- check_check_pack.c \
- check_check_exit.c \
+ check_check.h \
+ check_list.c \
+ check_check_sub.c \
+ check_check_master.c \
+ check_check_msg.c \
+ check_check_log.c \
+ check_check_log_internal.c \
+ check_check_limit.c \
+ check_check_fork.c \
+ check_check_fixture.c \
+ check_check_pack.c \
+ check_check_exit.c \
check_check_main.c
check_check_LDADD = $(top_builddir)/src/libcheckinternal.la $(top_builddir)/lib/libcompat.la
Suite *make_list_suite(void);
Suite *make_msg_suite(void);
Suite *make_log_suite(void);
+Suite *make_log_internal_suite(void);
Suite *make_limit_suite(void);
Suite *make_fork_suite(void);
Suite *make_fixture_suite(void);
--- /dev/null
+#include "../lib/libcompat.h"
+
+/* Tests for log related stuff in check which need non-exported functions. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <check.h>
+#include <check_list.h>
+#include <check_impl.h>
+#include <check_log.h>
+#include "check_check.h"
+
+
+#if ENABLE_SUBUNIT
+START_TEST(test_init_logging_subunit)
+{
+ /* init_logging with CK_SUBUNIT sets stdout
+ * to a subunit function, not any log.
+ */
+ Log * first_log = NULL;
+ Suite *s = suite_create("Suite");
+ SRunner *sr = srunner_create(s);
+ srunner_init_logging(sr, CK_SUBUNIT);
+ list_front (sr->loglst);
+ fail_if (list_at_end(sr->loglst), "No entries in log list");
+ first_log = list_val(sr->loglst);
+ fail_if (first_log == NULL, "log is NULL");
+ list_advance(sr->loglst);
+ fail_unless(list_at_end(sr->loglst), "More than one entry in log list");
+ fail_unless(first_log->lfun == subunit_lfun,
+ "Log function is not the subunit lfun.");
+ srunner_end_logging(sr);
+ srunner_free(sr);
+}
+END_TEST
+#endif
+
+Suite *make_log_internal_suite(void)
+{
+
+ Suite *s;
+ TCase *tc_core_subunit;
+
+ s = suite_create("Log");
+ tc_core_subunit = tcase_create("Core SubUnit");
+
+#if ENABLE_SUBUNIT
+ suite_add_tcase(s, tc_core_subunit);
+ tcase_add_test(tc_core_subunit, test_init_logging_subunit);
+#endif
+
+ return s;
+}
+
srunner_add_suite(sr, make_list_suite());
srunner_add_suite(sr, make_msg_suite());
srunner_add_suite(sr, make_log_suite());
+ srunner_add_suite(sr, make_log_internal_suite());
srunner_add_suite(sr, make_limit_suite());
srunner_add_suite(sr, make_fork_suite());
srunner_add_suite(sr, make_fixture_suite());
#include <stdio.h>
#include <string.h>
#include <check.h>
+#include "config.h"
START_TEST(test_pass)
{
srunner_free(sr);
}
+static void print_usage(void)
+{
+ printf ("Usage: ex_output (CK_SILENT | CK_MINIMAL | CK_NORMAL | CK_VERBOSE");
+#if ENABLE_SUBUNIT
+ printf (" | CK_SUBUNIT");
+#endif
+ printf (")\n");
+}
+
int main (int argc, char **argv)
{
if (argc != 2) {
- printf ("Usage: ex_output (CK_SILENT | CK_MINIMAL | CK_NORMAL | CK_VERBOSE)\n");
+ print_usage();
return EXIT_FAILURE;
}
run_tests(CK_NORMAL);
else if (strcmp (argv[1], "CK_VERBOSE") == 0)
run_tests(CK_VERBOSE);
+#if ENABLE_SUBUNIT
+ else if (strcmp (argv[1], "CK_SUBUNIT") == 0)
+ run_tests(CK_SUBUNIT);
+#endif
else {
- printf ("Usage: ex_output (CK_SILENT | CK_MINIMAL | CK_NORMAL | CK_VERBOSE)\n");
+ print_usage();
return EXIT_FAILURE;
}
#!/bin/sh
+. ./test_vars
+
if [ "${srcdir}" = "." ]; then
lsrc=""
else
33%: Checks: 3, Failures: 1, Errors: 1"
t2="xRunning suite(s): Master
33%: Checks: 3, Failures: 1, Errors: 1
-${lsrc}ex_output.c:16:F:Core:test_fail:0: Failure
-${lsrc}ex_output.c:20:E:Core:test_exit:0: (after this point) Early exit with return value 1"
+${lsrc}ex_output.c:17:F:Core:test_fail:0: Failure
+${lsrc}ex_output.c:21:E:Core:test_exit:0: (after this point) Early exit with return value 1"
t3="xRunning suite(s): Master
33%: Checks: 3, Failures: 1, Errors: 1
-${lsrc}ex_output.c:10:P:Core:test_pass:0: Passed
-${lsrc}ex_output.c:16:F:Core:test_fail:0: Failure
-${lsrc}ex_output.c:20:E:Core:test_exit:0: (after this point) Early exit with return value 1"
+${lsrc}ex_output.c:11:P:Core:test_pass:0: Passed
+${lsrc}ex_output.c:17:F:Core:test_fail:0: Failure
+${lsrc}ex_output.c:21:E:Core:test_exit:0: (after this point) Early exit with return value 1"
+t4="xtest: Core:test_pass
+success: Core:test_pass
+test: Core:test_fail
+failure: Core:test_fail [
+${lsrc}ex_output.c:17: Failure
+]
+test: Core:test_exit
+error: Core:test_exit [
+${lsrc}ex_output.c:21: (after this point) Early exit with return value 1
+]"
op0=`./ex_output CK_SILENT`
op1=`./ex_output CK_MINIMAL`
op2=`./ex_output CK_NORMAL`
op3=`./ex_output CK_VERBOSE`
+if test 1 -eq $ENABLE_SUBUNIT; then
+op4=`./ex_output CK_SUBUNIT`
+fi
test_output ( ) {
test_output "$t1" x"$op1" "CK_MINIMAL";
test_output "$t2" x"$op2" "CK_NORMAL";
test_output "$t3" x"$op3" "CK_VERBOSE";
+if test 1 -eq $ENABLE_SUBUNIT; then
+test_output "$t4" x"$op4" "CK_SUBUNIT";
+fi
exit 0
--- /dev/null
+# defined to 1 if subunit is enabled
+export ENABLE_SUBUNIT=@ENABLE_SUBUNIT@
+# defined to 1 if subunit is enabled
+export ENABLE_SUBUNIT=@ENABLE_SUBUNIT@