diff options
author | Russ Allbery <rra@stanford.edu> | 2010-08-25 15:08:05 -0700 |
---|---|---|
committer | Russ Allbery <rra@stanford.edu> | 2010-08-25 15:08:05 -0700 |
commit | 468ded4c2fae05a815bef91bdcb17d52f9cdcb2b (patch) | |
tree | b2b836734664367c03691a8594684187a1be4054 | |
parent | 32dc393016f0b6241dbf8d405638e18a33bb9b62 (diff) |
Update to rra-c-util 2.6 and C TAP Harness 1.5
Update to C TAP Harness 1.5:
* Better reporting of fatal errors in the test suite.
* Summarize results at the end of test execution.
* Add tests/HOWTO from docs/writing-tests in C TAP Harness.
Update to rra-c-util 2.6:
* Fix portability to bundled Heimdal on OpenBSD.
* Improve checking for krb5_kt_free_entry with older MIT Kerberos.
* Fix portability for missing krb5_get_init_creds_opt_free.
* Fix header guard for util/xwrite.h.
* Restore default compiler configuration after GSS-API library probe.
-rw-r--r-- | NEWS | 14 | ||||
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | m4/gssapi.m4 | 3 | ||||
-rw-r--r-- | m4/krb5.m4 | 39 | ||||
-rw-r--r-- | m4/remctl.m4 | 2 | ||||
-rw-r--r-- | portable/krb5-extra.c | 3 | ||||
-rw-r--r-- | portable/krb5.h | 31 | ||||
-rw-r--r-- | tests/portable/snprintf-t.c | 12 | ||||
-rw-r--r-- | tests/runtests.c | 403 | ||||
-rw-r--r-- | tests/tap/basic.c | 196 | ||||
-rw-r--r-- | tests/tap/basic.h | 41 | ||||
-rw-r--r-- | tests/tap/kerberos.c | 31 | ||||
-rw-r--r-- | tests/tap/kerberos.sh | 17 | ||||
-rw-r--r-- | tests/tap/libtap.sh | 32 | ||||
-rw-r--r-- | tests/tap/remctl.sh | 18 | ||||
-rw-r--r-- | tests/util/messages-t.c | 4 | ||||
-rw-r--r-- | tests/util/xmalloc.c | 5 | ||||
-rw-r--r-- | util/macros.h | 1 | ||||
-rw-r--r-- | util/messages.c | 50 | ||||
-rw-r--r-- | util/messages.h | 26 |
20 files changed, 631 insertions, 301 deletions
@@ -23,6 +23,20 @@ wallet 0.12 (unreleased) Add a help command to wallet-report, which returns a summary of all available commands. + Update to C TAP Harness 1.5: + + * Better reporting of fatal errors in the test suite. + * Summarize results at the end of test execution. + * Add tests/HOWTO from docs/writing-tests in C TAP Harness. + + Update to rra-c-util 2.6: + + * Fix portability to bundled Heimdal on OpenBSD. + * Improve checking for krb5_kt_free_entry with older MIT Kerberos. + * Fix portability for missing krb5_get_init_creds_opt_free. + * Fix header guard for util/xwrite.h. + * Restore default compiler configuration after GSS-API library probe. + wallet 0.11 (2010-03-08) When deleting an ACL on the server, verify that the ACL is not diff --git a/configure.ac b/configure.ac index 9f2d284..137e6ef 100644 --- a/configure.ac +++ b/configure.ac @@ -27,8 +27,10 @@ RRA_LIB_KRB5 RRA_LIB_KRB5_SWITCH AC_CHECK_FUNCS([krb5_get_init_creds_opt_alloc \ krb5_get_init_creds_opt_set_default_flags \ - krb5_kt_free_entry \ krb5_principal_get_realm]) +AC_CHECK_FUNCS([krb5_get_init_creds_opt_free], + [RRA_FUNC_KRB5_GET_INIT_CREDS_OPT_FREE_ARGS]) +AC_CHECK_DECLS([krb5_kt_free_entry]) AC_CHECK_MEMBERS([krb5_keytab_entry.keyblock], , , [#include <krb5.h>]) RRA_LIB_KRB5_RESTORE diff --git a/m4/gssapi.m4 b/m4/gssapi.m4 index 4b08569..0a657ff 100644 --- a/m4/gssapi.m4 +++ b/m4/gssapi.m4 @@ -57,7 +57,8 @@ AC_DEFUN([_RRA_LIB_GSSAPI_REDUCED], AC_CHECK_LIB([gssapi_krb5], [gss_import_name], [GSSAPI_LIBS="-lgssapi_krb5"], [AC_CHECK_LIB([gssapi], [gss_import_name], [GSSAPI_LIBS="-lgssapi"], [AC_CHECK_LIB([gss], [gss_import_name], [GSSAPI_LIBS="-lgss"], - [AC_MSG_ERROR([cannot find usable GSS-API library])])])])]) + [AC_MSG_ERROR([cannot find usable GSS-API library])])])]) + RRA_LIB_GSSAPI_RESTORE]) dnl Does the appropriate library checks for GSS-API linkage when we don't dnl have krb5-config or reduced dependencies. libgss is used as a last @@ -2,7 +2,7 @@ dnl Find the compiler and linker flags for Kerberos v5. dnl dnl Finds the compiler and linker flags for linking with Kerberos v5 dnl libraries. Provides the --with-krb5, --with-krb5-include, and -dnl --with-krb5-lib configure options to specify non-standards paths to the +dnl --with-krb5-lib configure options to specify non-standard paths to the dnl Kerberos libraries. Uses krb5-config where available unless reduced dnl dependencies is requested. dnl @@ -13,6 +13,9 @@ dnl Kerberos libraries, saving the current values first, and dnl RRA_LIB_KRB5_RESTORE to restore those settings to before the last dnl RRA_LIB_KRB5_SWITCH. dnl +dnl If KRB5_CPPFLAGS, KRB5_LDFLAGS, or KRB5_LIBS are set before calling these +dnl macros, their values will be added to whatever the macros discover. +dnl dnl Provides the RRA_LIB_KRB5_OPTIONAL macro, which should be used if Kerberos dnl support is optional. This macro will still always set the substitution dnl variables, but they'll be empty unless --with-krb5 is given. Also, @@ -25,8 +28,12 @@ dnl change library ordering in that case. dnl dnl Depends on RRA_ENABLE_REDUCED_DEPENDS and RRA_SET_LDFLAGS. dnl +dnl Also provides RRA_FUNC_KRB5_GET_INIT_CREDS_OPT_FREE_ARGS, which checks +dnl whether krb5_get_init_creds_opt_free takes one argument or two. Defines +dnl HAVE_KRB5_GET_INIT_CREDS_OPT_FREE_2_ARGS if it takes two arguments. +dnl dnl Written by Russ Allbery <rra@stanford.edu> -dnl Copyright 2005, 2006, 2007, 2008, 2009 +dnl Copyright 2005, 2006, 2007, 2008, 2009, 2010 dnl Board of Trustees, Leland Stanford Jr. University dnl dnl See LICENSE for licensing terms. @@ -99,10 +106,11 @@ AC_DEFUN([_RRA_LIB_KRB5_MANUAL], [AC_CHECK_LIB([nsl], [socket], [LIBS="-lnsl -lsocket $LIBS"], , [-lsocket])]) AC_SEARCH_LIBS([crypt], [crypt]) + AC_SEARCH_LIBS([rk_simple_execve], [roken]) rra_krb5_extra="$LIBS" LIBS="$rra_krb5_save_LIBS" AC_CHECK_LIB([krb5], [krb5_init_context], - [KRB5_LIBS="-lkrb5 -lasn1 -lroken -lcrypto -lcom_err $rra_krb5_extra"], + [KRB5_LIBS="-lkrb5 -lasn1 -lcom_err -lcrypto $rra_krb5_extra"], [AC_CHECK_LIB([krb5support], [krb5int_getspecific], [rra_krb5_extra="-lkrb5support $rra_krb5_extra"], [AC_CHECK_LIB([pthreads], [pthread_setspecific], @@ -125,7 +133,7 @@ AC_DEFUN([_RRA_LIB_KRB5_MANUAL], [AS_IF([test x"$1" = xtrue], [AC_MSG_ERROR([cannot find usable Kerberos v5 library])])], [$rra_krb5_extra])], - [-lasn1 -lroken -lcrypto -lcom_err $rra_krb5_extra]) + [-lasn1 -lcom_err -lcrypto $rra_krb5_extra]) LIBS="$KRB5_LIBS $LIBS" AC_CHECK_FUNCS([krb5_get_error_message], [AC_CHECK_FUNCS([krb5_free_error_message])], @@ -200,9 +208,6 @@ AC_DEFUN([RRA_LIB_KRB5], [rra_krb5_root= rra_krb5_libdir= rra_krb5_includedir= - KRB5_CPPFLAGS= - KRB5_LDFLAGS= - KRB5_LIBS= AC_SUBST([KRB5_CPPFLAGS]) AC_SUBST([KRB5_LDFLAGS]) AC_SUBST([KRB5_LIBS]) @@ -230,9 +235,6 @@ AC_DEFUN([RRA_LIB_KRB5_OPTIONAL], rra_krb5_libdir= rra_krb5_includedir= rra_use_kerberos= - KRB5_CPPFLAGS= - KRB5_LDFLAGS= - KRB5_LIBS= AC_SUBST([KRB5_CPPFLAGS]) AC_SUBST([KRB5_LDFLAGS]) AC_SUBST([KRB5_LIBS]) @@ -261,3 +263,20 @@ AC_DEFUN([RRA_LIB_KRB5_OPTIONAL], [_RRA_LIB_KRB5_INTERNAL([false])])]) AS_IF([test x"$KRB5_LIBS" != x], [AC_DEFINE([HAVE_KERBEROS], 1, [Define to enable Kerberos features.])])]) + +dnl Check whether krb5_get_init_creds_opt_free takes one argument or two. +dnl Early Heimdal used to take a single argument. Defines +dnl HAVE_KRB5_GET_INIT_CREDS_OPT_FREE_2_ARGS if it takes two arguments. +dnl +dnl Should be called with RRA_LIB_KRB5_SWITCH active. +AC_DEFUN([RRA_FUNC_KRB5_GET_INIT_CREDS_OPT_FREE_ARGS], +[AC_CACHE_CHECK([if krb5_get_init_creds_opt_free takes two arguments], + [rra_cv_func_krb5_get_init_creds_opt_free_args], + [AC_TRY_COMPILE([#include <krb5.h>], + [krb5_get_init_creds_opt *opts; krb5_context c; + krb5_get_init_creds_opt_free(c, opts);], + [rra_cv_func_krb5_get_init_creds_opt_free_args=yes], + [rra_cv_func_krb5_get_init_creds_opt_free_args=no])]) + AS_IF([test $rra_cv_func_krb5_get_init_creds_opt_free_args = yes], + [AC_DEFINE([HAVE_KRB5_GET_INIT_CREDS_OPT_FREE_2_ARGS], 1, + [Define if krb5_get_init_creds_opt_free takes two arguments.])])]) diff --git a/m4/remctl.m4 b/m4/remctl.m4 index 8ee3c16..bb3a56f 100644 --- a/m4/remctl.m4 +++ b/m4/remctl.m4 @@ -21,7 +21,7 @@ dnl dnl See LICENSE for licensing terms. dnl Save the current CPPFLAGS, LDFLAGS, and LIBS settings and switch to -dnl versions that include the Kerberos v5 flags. Used as a wrapper, with +dnl versions that include the remctl flags. Used as a wrapper, with dnl RRA_LIB_REMCTL_RESTORE, around tests. AC_DEFUN([RRA_LIB_REMCTL_SWITCH], [rra_remctl_save_CPPFLAGS="$CPPFLAGS" diff --git a/portable/krb5-extra.c b/portable/krb5-extra.c index dcddbe4..89ccbde 100644 --- a/portable/krb5-extra.c +++ b/portable/krb5-extra.c @@ -97,7 +97,8 @@ krb5_free_error_message(krb5_context ctx UNUSED, const char *msg) * assumes that an all-zero bit pattern will create a NULL pointer. */ krb5_error_code -krb5_get_init_creds_opt_alloc(krb5_context ctx, krb5_get_init_creds_opt **opts) +krb5_get_init_creds_opt_alloc(krb5_context ctx UNUSED, + krb5_get_init_creds_opt **opts) { *opts = calloc(1, sizeof(krb5_get_init_creds_opt)); if (*opts == NULL) diff --git a/portable/krb5.h b/portable/krb5.h index d9ef283..3b5700b 100644 --- a/portable/krb5.h +++ b/portable/krb5.h @@ -23,10 +23,17 @@ #ifndef PORTABLE_KRB5_H #define PORTABLE_KRB5_H 1 -#include <config.h> +/* + * Allow inclusion of config.h to be skipped, since sometimes we have to use a + * stripped-down version of config.h with a different name. + */ +#ifndef CONFIG_H_INCLUDED +# include <config.h> +#endif #include <portable/macros.h> #include <krb5.h> +#include <stdlib.h> BEGIN_DECLS @@ -50,27 +57,39 @@ void krb5_free_error_message(krb5_context, const char *); #endif /* - * Both current MIT and current Heimdal prefer _opt_alloc, but older versions - * of both require allocating your own struct and calling _opt_init. + * Both current MIT and current Heimdal prefer _opt_alloc and _opt_free, but + * older versions of both require allocating your own struct and calling + * _opt_init. */ #ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC krb5_error_code krb5_get_init_creds_opt_alloc(krb5_context, krb5_get_init_creds_opt **); #endif +#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_FREE +# ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_FREE_2_ARGS +# define krb5_get_init_creds_opt_free(c, o) krb5_get_init_creds_opt_free(o) +# endif +#else +# define krb5_get_init_creds_opt_free(c, o) free(o) +#endif /* Heimdal-specific. */ #ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_DEFAULT_FLAGS #define krb5_get_init_creds_opt_set_default_flags(c, p, r, o) /* empty */ #endif -/* Heimdal: krb5_kt_free_entry, MIT: krb5_free_keytab_entry_contents. */ -#ifndef HAVE_KRB5_KT_FREE_ENTRY +/* + * Heimdal: krb5_kt_free_entry, MIT: krb5_free_keytab_entry_contents. We + * check for the declaration rather than the function since the function is + * present in older MIT Kerberos libraries but not prototyped. + */ +#if !HAVE_DECL_KRB5_KT_FREE_ENTRY # define krb5_kt_free_entry(c, e) krb5_free_keytab_entry_contents((c), (e)) #endif /* * Heimdal provides a nice function that just returns a const char *. On MIT, - * there's an accessor macro that returns the krb5_data pointer, wihch + * there's an accessor macro that returns the krb5_data pointer, which * requires more work to get at the underlying char *. */ #ifndef HAVE_KRB5_PRINCIPAL_GET_REALM diff --git a/tests/portable/snprintf-t.c b/tests/portable/snprintf-t.c index ca6ae61..fd4c228 100644 --- a/tests/portable/snprintf-t.c +++ b/tests/portable/snprintf-t.c @@ -2,7 +2,7 @@ * snprintf test suite. * * Written by Russ Allbery <rra@stanford.edu> - * Copyright 2009 Board of Trustees, Leland Stanford Jr. University + * Copyright 2009, 2010 Board of Trustees, Leland Stanford Jr. University * Copyright (c) 2004, 2005, 2006 * by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, @@ -17,6 +17,12 @@ #include <tests/tap/basic.h> /* + * Disable the requirement that format strings be literals. We need variable + * formats for easy testing. + */ +#pragma GCC diagnostic ignored "-Wformat-nonliteral" + +/* * Intentionally don't add the printf attribute here since we pass a * zero-length printf format during testing and don't want warnings. */ @@ -86,7 +92,7 @@ static unsigned long long ullong_nums[] = { static void -test_format(bool truncate, const char *expected, int count, +test_format(bool trunc, const char *expected, int count, const char *format, ...) { char buf[128]; @@ -94,7 +100,7 @@ test_format(bool truncate, const char *expected, int count, va_list args; va_start(args, format); - result = test_vsnprintf(buf, truncate ? 32 : sizeof(buf), format, args); + result = test_vsnprintf(buf, trunc ? 32 : sizeof(buf), format, args); va_end(args); is_string(expected, buf, "format %s, wanted %s", format, expected); is_int(count, result, "...and output length correct"); diff --git a/tests/runtests.c b/tests/runtests.c index 1670012..ab77629 100644 --- a/tests/runtests.c +++ b/tests/runtests.c @@ -8,22 +8,41 @@ * Expects a list of executables located in the given file, one line per * executable. For each one, runs it as part of a test suite, reporting * results. Test output should start with a line containing the number of - * tests (numbered from 1 to this number), and then each line should be in the - * following format: + * tests (numbered from 1 to this number), optionally preceded by "1..", + * although that line may be given anywhere in the output. Each additional + * line should be in the following format: * * ok <number> * not ok <number> * ok <number> # skip + * not ok <number> # todo * - * where <number> is the number of the test. ok indicates success, not ok - * indicates failure, and "# skip" indicates the test was skipped for some - * reason (maybe because it doesn't apply to this platform). This is a subset - * of TAP as documented in Test::Harness::TAP, which comes with Perl. + * where <number> is the number of the test. An optional comment is permitted + * after the number if preceded by whitespace. ok indicates success, not ok + * indicates failure. "# skip" and "# todo" are a special cases of a comment, + * and must start with exactly that formatting. They indicate the test was + * skipped for some reason (maybe because it doesn't apply to this platform) + * or is testing something known to currently fail. The text following either + * "# skip" or "# todo" and whitespace is the reason. + * + * As a special case, the first line of the output may be in the form: + * + * 1..0 # skip some reason + * + * which indicates that this entire test case should be skipped and gives a + * reason. + * + * Any other lines are ignored, although for compliance with the TAP protocol + * all lines other than the ones in the above format should be sent to + * standard error rather than standard output and start with #. + * + * This is a subset of TAP as documented in Test::Harness::TAP or + * TAP::Parser::Grammar, which comes with Perl. * * Any bug reports, bug fixes, and improvements are very much welcome and * should be sent to the e-mail address below. * - * Copyright 2000, 2001, 2004, 2006, 2007, 2008, 2009 + * Copyright 2000, 2001, 2004, 2006, 2007, 2008, 2009, 2010 * Russ Allbery <rra@stanford.edu> * * Permission is hereby granted, free of charge, to any person obtaining a @@ -88,6 +107,14 @@ enum test_status { TEST_INVALID }; +/* Indicates the state of our plan. */ +enum plan_status { + PLAN_INIT, /* Nothing seen yet. */ + PLAN_FIRST, /* Plan seen before any tests. */ + PLAN_PENDING, /* Test seen and no plan yet. */ + PLAN_FINAL /* Plan seen after some tests. */ +}; + /* Error exit statuses for test processes. */ #define CHILDERR_DUP 100 /* Couldn't redirect stderr or stdout. */ #define CHILDERR_EXEC 101 /* Couldn't exec child process. */ @@ -97,12 +124,14 @@ enum test_status { struct testset { char *file; /* The file name of the test. */ char *path; /* The path to the test program. */ - int count; /* Expected count of tests. */ - int current; /* The last seen test number. */ - int length; /* The length of the last status message. */ - int passed; /* Count of passing tests. */ - int failed; /* Count of failing lists. */ - int skipped; /* Count of skipped tests (passed). */ + enum plan_status plan; /* The status of our plan. */ + unsigned long count; /* Expected count of tests. */ + unsigned long current; /* The last seen test number. */ + unsigned int length; /* The length of the last status message. */ + unsigned long passed; /* Count of passing tests. */ + unsigned long failed; /* Count of failing lists. */ + unsigned long skipped; /* Count of skipped tests (passed). */ + unsigned long allocated; /* The size of the results table. */ enum test_status *results; /* Table of results by test number. */ int aborted; /* Whether the set as aborted. */ int reported; /* Whether the results were reported. */ @@ -131,8 +160,9 @@ Failed Set Fail/Total (%) Skip Stat Failing Tests\n\ -------------------------- -------------- ---- ---- ------------------------"; /* Include the file name and line number in malloc failures. */ -#define xmalloc(size) x_malloc((size), __FILE__, __LINE__) -#define xstrdup(p) x_strdup((p), __FILE__, __LINE__) +#define xmalloc(size) x_malloc((size), __FILE__, __LINE__) +#define xrealloc(p, size) x_realloc((p), (size), __FILE__, __LINE__) +#define xstrdup(p) x_strdup((p), __FILE__, __LINE__) /* @@ -164,7 +194,7 @@ x_malloc(size_t size, const char *file, int line) void *p; p = malloc(size); - if (!p) + if (p == NULL) sysdie("failed to malloc %lu bytes at %s line %d", (unsigned long) size, file, line); return p; @@ -172,6 +202,20 @@ x_malloc(size_t size, const char *file, int line) /* + * Reallocate memory, reporting a fatal error and exiting on failure. + */ +static void * +x_realloc(void *p, size_t size, const char *file, int line) +{ + p = realloc(p, size); + if (p == NULL) + sysdie("failed to realloc %lu bytes at %s line %d", + (unsigned long) size, file, line); + return p; +} + + +/* * Copy a string, reporting a fatal error and exiting on failure. */ static char * @@ -182,7 +226,7 @@ x_strdup(const char *s, const char *file, int line) len = strlen(s) + 1; p = malloc(len); - if (!p) + if (p == NULL) sysdie("failed to strdup %lu bytes at %s line %d", (unsigned long) len, file, line); memcpy(p, s, len); @@ -235,62 +279,6 @@ skip_whitespace(const char *p) /* - * Read the first line of test output, which should contain the range of - * test numbers, and initialize the testset structure. Assume it was zeroed - * before being passed in. Return true if initialization succeeds, false - * otherwise. - */ -static int -test_init(const char *line, struct testset *ts) -{ - int i; - - /* - * Prefer a simple number of tests, but if the count is given as a range - * such as 1..10, accept that too for compatibility with Perl's - * Test::Harness. - */ - line = skip_whitespace(line); - if (strncmp(line, "1..", 3) == 0) - line += 3; - - /* - * Get the count, check it for validity, and initialize the struct. If we - * have something of the form "1..0 # skip foo", the whole file was - * skipped; record that. - */ - i = strtol(line, (char **) &line, 10); - if (i == 0) { - line = skip_whitespace(line); - if (*line == '#') { - line = skip_whitespace(line + 1); - if (strncasecmp(line, "skip", 4) == 0) { - line = skip_whitespace(line + 4); - if (*line != '\0') { - ts->reason = xstrdup(line); - ts->reason[strlen(ts->reason) - 1] = '\0'; - } - ts->all_skipped = 1; - ts->aborted = 1; - return 0; - } - } - } - if (i <= 0) { - puts("ABORTED (invalid test count)"); - ts->aborted = 1; - ts->reported = 1; - return 0; - } - ts->count = i; - ts->results = xmalloc(ts->count * sizeof(enum test_status)); - for (i = 0; i < ts->count; i++) - ts->results[i] = TEST_INVALID; - return 1; -} - - -/* * Start a program, connecting its stdout to a pipe on our end and its stderr * to /dev/null, and storing the file descriptor to read from in the two * argument. Returns the PID of the new process. Errors are fatal. @@ -340,7 +328,7 @@ test_start(const char *path, int *fd) static void test_backspace(struct testset *ts) { - int i; + unsigned int i; if (!isatty(STDOUT_FILENO)) return; @@ -355,6 +343,87 @@ test_backspace(struct testset *ts) /* + * Read the plan line of test output, which should contain the range of test + * numbers. We may initialize the testset structure here if we haven't yet + * seen a test. Return true if initialization succeeded and the test should + * continue, false otherwise. + */ +static int +test_plan(const char *line, struct testset *ts) +{ + unsigned long i; + long n; + + /* + * Accept a plan without the leading 1.. for compatibility with older + * versions of runtests. This will only be allowed if we've not yet seen + * a test result. + */ + line = skip_whitespace(line); + if (strncmp(line, "1..", 3) == 0) + line += 3; + + /* + * Get the count, check it for validity, and initialize the struct. If we + * have something of the form "1..0 # skip foo", the whole file was + * skipped; record that. If we do skip the whole file, zero out all of + * our statistics, since they're no longer relevant. + */ + n = strtol(line, (char **) &line, 10); + if (n == 0) { + line = skip_whitespace(line); + if (*line == '#') { + line = skip_whitespace(line + 1); + if (strncasecmp(line, "skip", 4) == 0) { + line = skip_whitespace(line + 4); + if (*line != '\0') { + ts->reason = xstrdup(line); + ts->reason[strlen(ts->reason) - 1] = '\0'; + } + ts->all_skipped = 1; + ts->aborted = 1; + ts->count = 0; + ts->passed = 0; + ts->skipped = 0; + ts->failed = 0; + return 0; + } + } + } + if (n <= 0) { + puts("ABORTED (invalid test count)"); + ts->aborted = 1; + ts->reported = 1; + return 0; + } + if (ts->plan == PLAN_INIT && ts->allocated == 0) { + ts->count = n; + ts->allocated = n; + ts->plan = PLAN_FIRST; + ts->results = xmalloc(ts->count * sizeof(enum test_status)); + for (i = 0; i < ts->count; i++) + ts->results[i] = TEST_INVALID; + } else if (ts->plan == PLAN_PENDING) { + if ((unsigned long) n < ts->count) { + printf("ABORTED (invalid test number %lu)\n", ts->count); + ts->aborted = 1; + ts->reported = 1; + return 0; + } + ts->count = n; + if ((unsigned long) n > ts->allocated) { + ts->results = xrealloc(ts->results, n * sizeof(enum test_status)); + for (i = ts->allocated; i < ts->count; i++) + ts->results[i] = TEST_INVALID; + ts->allocated = n; + } + ts->plan = PLAN_FINAL; + } + return 1; +} + + +/* * Given a single line of output from a test, parse it and return the success * status of that test. Anything printed to stdout not matching the form * /^(not )?ok \d+/ is ignored. Sets ts->current to the test number that just @@ -366,20 +435,21 @@ test_checkline(const char *line, struct testset *ts) enum test_status status = TEST_PASS; const char *bail; char *end; - int current; + long number; + unsigned long i, current; /* Before anything, check for a test abort. */ bail = strstr(line, "Bail out!"); if (bail != NULL) { bail = skip_whitespace(bail + strlen("Bail out!")); if (*bail != '\0') { - int length; + size_t length; length = strlen(bail); if (bail[length - 1] == '\n') length--; test_backspace(ts); - printf("ABORTED (%.*s)\n", length, bail); + printf("ABORTED (%.*s)\n", (int) length, bail); ts->reported = 1; } ts->aborted = 1; @@ -393,6 +463,26 @@ test_checkline(const char *line, struct testset *ts) if (line[strlen(line) - 1] != '\n') return; + /* If the line begins with a hash mark, ignore it. */ + if (line[0] == '#') + return; + + /* If we haven't yet seen a plan, look for one. */ + if (ts->plan == PLAN_INIT && isdigit((unsigned char)(*line))) { + if (!test_plan(line, ts)) + return; + } else if (strncmp(line, "1..", 3) == 0) { + if (ts->plan == PLAN_PENDING) { + if (!test_plan(line, ts)) + return; + } else { + puts("ABORTED (multiple plans)"); + ts->aborted = 1; + ts->reported = 1; + return; + } + } + /* Parse the line, ignoring something we can't parse. */ if (strncmp(line, "not ", 4) == 0) { status = TEST_FAIL; @@ -402,17 +492,36 @@ test_checkline(const char *line, struct testset *ts) return; line = skip_whitespace(line + 2); errno = 0; - current = strtol(line, &end, 10); + number = strtol(line, &end, 10); if (errno != 0 || end == line) - current = ts->current + 1; - if (current <= 0 || current > ts->count) { + number = ts->current + 1; + current = number; + if (number <= 0 || (current > ts->count && ts->plan == PLAN_FIRST)) { test_backspace(ts); - printf("ABORTED (invalid test number %d)\n", current); + printf("ABORTED (invalid test number %lu)\n", current); ts->aborted = 1; ts->reported = 1; return; } + /* We have a valid test result. Tweak the results array if needed. */ + if (ts->plan == PLAN_INIT || ts->plan == PLAN_PENDING) { + ts->plan = PLAN_PENDING; + if (current > ts->count) + ts->count = current; + if (current > ts->allocated) { + unsigned long n; + + n = (ts->allocated == 0) ? 32 : ts->allocated * 2; + if (n < current) + n = current; + ts->results = xrealloc(ts->results, n * sizeof(enum test_status)); + for (i = ts->allocated; i < n; i++) + ts->results[i] = TEST_INVALID; + ts->allocated = n; + } + } + /* * Handle directives. We should probably do something more interesting * with unexpected passes of todo tests. @@ -431,7 +540,7 @@ test_checkline(const char *line, struct testset *ts) /* Make sure that the test number is in range and not a duplicate. */ if (ts->results[current - 1] != TEST_INVALID) { test_backspace(ts); - printf("ABORTED (duplicate test number %d)\n", current); + printf("ABORTED (duplicate test number %lu)\n", current); ts->aborted = 1; ts->reported = 1; return; @@ -442,13 +551,13 @@ test_checkline(const char *line, struct testset *ts) case TEST_PASS: ts->passed++; break; case TEST_FAIL: ts->failed++; break; case TEST_SKIP: ts->skipped++; break; - default: break; + case TEST_INVALID: break; } ts->current = current; ts->results[current - 1] = status; test_backspace(ts); if (isatty(STDOUT_FILENO)) { - ts->length = printf("%d/%d", current, ts->count); + ts->length = printf("%lu/%lu", current, ts->count); fflush(stdout); } } @@ -461,12 +570,13 @@ test_checkline(const char *line, struct testset *ts) * chars plus the space needed would go over the limit (use a limit of 0 to * disable this. */ -static int -test_print_range(int first, int last, int chars, int limit) +static unsigned int +test_print_range(unsigned long first, unsigned long last, unsigned int chars, + unsigned int limit) { - int needed = 0; - int out = 0; - int n; + unsigned int needed = 0; + unsigned int out = 0; + unsigned long n; if (chars > 0) { needed += 2; @@ -484,8 +594,8 @@ test_print_range(int first, int last, int chars, int limit) out += printf("..."); } else { if (last > first) - out += printf("%d-", first); - out += printf("%d", last); + out += printf("%lu-", first); + out += printf("%lu", last); } return out; } @@ -500,16 +610,16 @@ test_print_range(int first, int last, int chars, int limit) static void test_summarize(struct testset *ts, int status) { - int i; - int missing = 0; - int failed = 0; - int first = 0; - int last = 0; + unsigned long i; + unsigned long missing = 0; + unsigned long failed = 0; + unsigned long first = 0; + unsigned long last = 0; if (ts->aborted) { fputs("ABORTED", stdout); if (ts->count > 0) - printf(" (passed %d/%d)", ts->passed, ts->count - ts->skipped); + printf(" (passed %lu/%lu)", ts->passed, ts->count - ts->skipped); } else { for (i = 0; i < ts->count; i++) { if (ts->results[i] == TEST_INVALID) { @@ -553,9 +663,9 @@ test_summarize(struct testset *ts, int status) fputs(!status ? "ok" : "dubious", stdout); if (ts->skipped > 0) { if (ts->skipped == 1) - printf(" (skipped %d test)", ts->skipped); + printf(" (skipped %lu test)", ts->skipped); else - printf(" (skipped %d tests)", ts->skipped); + printf(" (skipped %lu tests)", ts->skipped); } } } @@ -570,8 +680,9 @@ test_summarize(struct testset *ts, int status) /* * Given a test set, analyze the results, classify the exit status, handle a - * few special error messages, and then pass it along to test_summarize() - * for the regular output. + * few special error messages, and then pass it along to test_summarize() for + * the regular output. Returns true if the test set ran successfully and all + * tests passed or were skipped, false otherwise. */ static int test_analyze(struct testset *ts) @@ -606,6 +717,10 @@ test_analyze(struct testset *ts) } else if (WIFSIGNALED(ts->status)) { test_summarize(ts, -WTERMSIG(ts->status)); return 0; + } else if (ts->plan != PLAN_FIRST && ts->plan != PLAN_FINAL) { + puts("ABORTED (no valid test plan)"); + ts->aborted = 1; + return 0; } else { test_summarize(ts, 0); return (ts->failed == 0); @@ -622,14 +737,12 @@ static int test_run(struct testset *ts) { pid_t testpid, child; - int outfd, i, status; + int outfd, status; + unsigned long i; FILE *output; char buffer[BUFSIZ]; - /* - * Initialize the test and our data structures, flagging this set in error - * if the initialization fails. - */ + /* Run the test program. */ testpid = test_start(ts->path, &outfd); output = fdopen(outfd, "r"); if (!output) { @@ -637,15 +750,11 @@ test_run(struct testset *ts) fflush(stdout); sysdie("fdopen failed"); } - if (!fgets(buffer, sizeof(buffer), output)) - ts->aborted = 1; - if (!ts->aborted && !test_init(buffer, ts)) - ts->aborted = 1; /* Pass each line of output to test_checkline(). */ while (!ts->aborted && fgets(buffer, sizeof(buffer), output)) test_checkline(buffer, ts); - if (ferror(output)) + if (ferror(output) || ts->plan == PLAN_INIT) ts->aborted = 1; test_backspace(ts); @@ -686,7 +795,8 @@ static void test_fail_summary(const struct testlist *fails) { struct testset *ts; - int i, chars, total, first, last; + unsigned int chars; + unsigned long i, first, last, total; puts(header); @@ -695,7 +805,7 @@ test_fail_summary(const struct testlist *fails) for (; fails; fails = fails->next) { ts = fails->ts; total = ts->count - ts->skipped; - printf("%-26.26s %4d/%-4d %3.0f%% %4d ", ts->file, ts->failed, + printf("%-26.26s %4lu/%-4lu %3.0f%% %4lu ", ts->file, ts->failed, total, total ? (ts->failed * 100.0) / total : 0, ts->skipped); if (WIFEXITED(ts->status)) @@ -711,19 +821,25 @@ test_fail_summary(const struct testlist *fails) last = 0; for (i = 0; i < ts->count; i++) { if (ts->results[i] == TEST_FAIL) { - if (first && i == last) + if (first != 0 && i == last) last = i + 1; else { - if (first) + if (first != 0) chars += test_print_range(first, last, chars, 20); first = i + 1; last = i + 1; } } } - if (first) + if (first != 0) test_print_range(first, last, chars, 20); putchar('\n'); + free(ts->file); + free(ts->path); + free(ts->results); + if (ts->reason != NULL) + free(ts->reason); + free(ts); } } @@ -746,7 +862,7 @@ find_test(const char *name, struct testset *ts, const char *source, { char *path; const char *bases[] = { ".", build, source, NULL }; - int i; + unsigned int i; for (i = 0; bases[i] != NULL; i++) { path = xmalloc(strlen(bases[i]) + strlen(name) + 4); @@ -778,20 +894,21 @@ static int test_batch(const char *testlist, const char *source, const char *build) { FILE *tests; - size_t length, i; - size_t longest = 0; + unsigned int length, i; + unsigned int longest = 0; char buffer[BUFSIZ]; - int line; + unsigned int line; struct testset ts, *tmp; struct timeval start, end; struct rusage stats; - struct testlist *failhead = 0; - struct testlist *failtail = 0; - int total = 0; - int passed = 0; - int skipped = 0; - int failed = 0; - int aborted = 0; + struct testlist *failhead = NULL; + struct testlist *failtail = NULL; + struct testlist *next; + unsigned long total = 0; + unsigned long passed = 0; + unsigned long skipped = 0; + unsigned long failed = 0; + unsigned long aborted = 0; /* * Open our file of tests to run and scan it, checking for lines that @@ -805,7 +922,7 @@ test_batch(const char *testlist, const char *source, const char *build) line++; length = strlen(buffer) - 1; if (buffer[length] != '\n') { - fprintf(stderr, "%s:%d: line too long\n", testlist, line); + fprintf(stderr, "%s:%u: line too long\n", testlist, line); exit(1); } if (length > longest) @@ -834,7 +951,7 @@ test_batch(const char *testlist, const char *source, const char *build) line++; length = strlen(buffer) - 1; if (buffer[length] != '\n') { - fprintf(stderr, "%s:%d: line too long\n", testlist, line); + fprintf(stderr, "%s:%u: line too long\n", testlist, line); exit(1); } buffer[length] = '\0'; @@ -844,12 +961,14 @@ test_batch(const char *testlist, const char *source, const char *build) if (isatty(STDOUT_FILENO)) fflush(stdout); memset(&ts, 0, sizeof(ts)); + ts.plan = PLAN_INIT; ts.file = xstrdup(buffer); find_test(buffer, &ts, source, build); ts.reason = NULL; if (test_run(&ts)) { free(ts.file); free(ts.path); + free(ts.results); if (ts.reason != NULL) free(ts.reason); } else { @@ -858,13 +977,13 @@ test_batch(const char *testlist, const char *source, const char *build) if (!failhead) { failhead = xmalloc(sizeof(struct testset)); failhead->ts = tmp; - failhead->next = 0; + failhead->next = NULL; failtail = failhead; } else { failtail->next = xmalloc(sizeof(struct testset)); failtail = failtail->next; failtail->ts = tmp; - failtail->next = 0; + failtail->next = NULL; } } aborted += ts.aborted; @@ -880,29 +999,35 @@ test_batch(const char *testlist, const char *source, const char *build) getrusage(RUSAGE_CHILDREN, &stats); /* Print out our final results. */ - if (failhead) + if (failhead != NULL) { test_fail_summary(failhead); + while (failhead != NULL) { + next = failhead->next; + free(failhead); + failhead = next; + } + } putchar('\n'); if (aborted != 0) { if (aborted == 1) - printf("Aborted %d test set", aborted); + printf("Aborted %lu test set", aborted); else - printf("Aborted %d test sets", aborted); - printf(", passed %d/%d tests", passed, total); + printf("Aborted %lu test sets", aborted); + printf(", passed %lu/%lu tests", passed, total); } else if (failed == 0) fputs("All tests successful", stdout); else - printf("Failed %d/%d tests, %.2f%% okay", failed, total, + printf("Failed %lu/%lu tests, %.2f%% okay", failed, total, (total - failed) * 100.0 / total); if (skipped != 0) { if (skipped == 1) - printf(", %d test skipped", skipped); + printf(", %lu test skipped", skipped); else - printf(", %d tests skipped", skipped); + printf(", %lu tests skipped", skipped); } puts("."); - printf("Files=%d, Tests=%d", line, total); + printf("Files=%u, Tests=%lu", line, total); printf(", %.2f seconds", tv_diff(&end, &start)); printf(" (%.2f usr + %.2f sys = %.2f CPU)\n", tv_seconds(&stats.ru_utime), tv_seconds(&stats.ru_stime), diff --git a/tests/tap/basic.c b/tests/tap/basic.c index 5ca9ff4..829f91a 100644 --- a/tests/tap/basic.c +++ b/tests/tap/basic.c @@ -2,12 +2,13 @@ * Some utility routines for writing tests. * * Herein are a variety of utility routines for writing tests. All routines - * of the form ok*() take a test number and some number of appropriate + * of the form ok() or is*() take a test number and some number of appropriate * arguments, check to be sure the results match the expected output using the * arguments, and print out something appropriate for that test number. Other - * utility routines help in constructing more complex tests. + * utility routines help in constructing more complex tests, skipping tests, + * or setting up the TAP output format. * - * Copyright 2009 Russ Allbery <rra@stanford.edu> + * Copyright 2009, 2010 Russ Allbery <rra@stanford.edu> * Copyright 2006, 2007, 2008 * Board of Trustees, Leland Stanford Jr. University * Copyright (c) 2004, 2005, 2006 @@ -34,7 +35,7 @@ * The test count. Always contains the number that will be used for the next * test status. */ -int testnum = 1; +unsigned long testnum = 1; /* * Status information stored so that we can give a test summary at the end of @@ -44,10 +45,14 @@ int testnum = 1; * We also store the PID of the process that called plan() and only summarize * results when that process exits, so as to not misreport results in forked * processes. + * + * If _lazy is true, we're doing lazy planning and will print out the plan + * based on the last test number at the end of testing. */ -static int _planned = 0; -static int _failed = 0; +static unsigned long _planned = 0; +static unsigned long _failed = 0; static pid_t _process = 0; +static int _lazy = 0; /* @@ -57,22 +62,28 @@ static pid_t _process = 0; static void finish(void) { - int highest = testnum - 1; - - if (_process != 0 && getpid() == _process && _planned > 0) { + unsigned long highest = testnum - 1; + + if (_planned == 0 && !_lazy) + return; + if (_process != 0 && getpid() == _process) { + if (_lazy) { + printf("1..%lu\n", highest); + _planned = highest; + } if (_planned > highest) - printf("# Looks like you planned %d test%s but only ran %d\n", + printf("# Looks like you planned %lu test%s but only ran %lu\n", _planned, (_planned > 1 ? "s" : ""), highest); else if (_planned < highest) - printf("# Looks like you planned %d test%s but ran %d extra\n", + printf("# Looks like you planned %lu test%s but ran %lu extra\n", _planned, (_planned > 1 ? "s" : ""), highest - _planned); else if (_failed > 0) - printf("# Looks like you failed %d test%s of %d\n", _failed, + printf("# Looks like you failed %lu test%s of %lu\n", _failed, (_failed > 1 ? "s" : ""), _planned); else if (_planned > 1) - printf("# All %d tests successful or skipped\n", _planned); + printf("# All %lu tests successful or skipped\n", _planned); else - printf("# %d test successful or skipped\n", _planned); + printf("# %lu test successful or skipped\n", _planned); } } @@ -82,12 +93,12 @@ finish(void) * the number of tests in the test suite. */ void -plan(int count) +plan(unsigned long count) { if (setvbuf(stdout, NULL, _IOLBF, BUFSIZ) != 0) fprintf(stderr, "# cannot set stdout to line buffered: %s\n", strerror(errno)); - printf("1..%d\n", count); + printf("1..%lu\n", count); testnum = 1; _planned = count; _process = getpid(); @@ -96,6 +107,23 @@ plan(int count) /* + * Initialize things for lazy planning, where we'll automatically print out a + * plan at the end of the program. Turns on line buffering on stdout as well. + */ +void +plan_lazy(void) +{ + if (setvbuf(stdout, NULL, _IOLBF, BUFSIZ) != 0) + fprintf(stderr, "# cannot set stdout to line buffered: %s\n", + strerror(errno)); + testnum = 1; + _process = getpid(); + _lazy = 1; + atexit(finish); +} + + +/* * Skip the entire test suite and exits. Should be called instead of plan(), * not after it, since it prints out a special plan line. */ @@ -134,7 +162,7 @@ print_desc(const char *format, va_list args) void ok(int success, const char *format, ...) { - printf("%sok %d", success ? "" : "not ", testnum++); + printf("%sok %lu", success ? "" : "not ", testnum++); if (!success) _failed++; if (format != NULL) { @@ -149,12 +177,27 @@ ok(int success, const char *format, ...) /* + * Same as ok(), but takes the format arguments as a va_list. + */ +void +okv(int success, const char *format, va_list args) +{ + printf("%sok %lu", success ? "" : "not ", testnum++); + if (!success) + _failed++; + if (format != NULL) + print_desc(format, args); + putchar('\n'); +} + + +/* * Skip a test. */ void skip(const char *reason, ...) { - printf("ok %d # skip", testnum++); + printf("ok %lu # skip", testnum++); if (reason != NULL) { va_list args; @@ -171,12 +214,12 @@ skip(const char *reason, ...) * Report the same status on the next count tests. */ void -ok_block(int count, int status, const char *format, ...) +ok_block(unsigned long count, int status, const char *format, ...) { - int i; + unsigned long i; for (i = 0; i < count; i++) { - printf("%sok %d", status ? "" : "not ", testnum++); + printf("%sok %lu", status ? "" : "not ", testnum++); if (!status) _failed++; if (format != NULL) { @@ -195,12 +238,12 @@ ok_block(int count, int status, const char *format, ...) * Skip the next count tests. */ void -skip_block(int count, const char *reason, ...) +skip_block(unsigned long count, const char *reason, ...) { - int i; + unsigned long i; for (i = 0; i < count; i++) { - printf("ok %d # skip", testnum++); + printf("ok %lu # skip", testnum++); if (reason != NULL) { va_list args; @@ -219,13 +262,13 @@ skip_block(int count, const char *reason, ...) * if those two numbers match. */ void -is_int(int wanted, int seen, const char *format, ...) +is_int(long wanted, long seen, const char *format, ...) { if (wanted == seen) - printf("ok %d", testnum++); + printf("ok %lu", testnum++); else { - printf("# wanted: %d\n# seen: %d\n", wanted, seen); - printf("not ok %d", testnum++); + printf("# wanted: %ld\n# seen: %ld\n", wanted, seen); + printf("not ok %lu", testnum++); _failed++; } if (format != NULL) { @@ -251,10 +294,10 @@ is_string(const char *wanted, const char *seen, const char *format, ...) if (seen == NULL) seen = "(null)"; if (strcmp(wanted, seen) == 0) - printf("ok %d", testnum++); + printf("ok %lu", testnum++); else { printf("# wanted: %s\n# seen: %s\n", wanted, seen); - printf("not ok %d", testnum++); + printf("not ok %lu", testnum++); _failed++; } if (format != NULL) { @@ -276,10 +319,10 @@ void is_double(double wanted, double seen, const char *format, ...) { if (wanted == seen) - printf("ok %d", testnum++); + printf("ok %lu", testnum++); else { printf("# wanted: %g\n# seen: %g\n", wanted, seen); - printf("not ok %d", testnum++); + printf("not ok %lu", testnum++); _failed++; } if (format != NULL) { @@ -301,11 +344,11 @@ void is_hex(unsigned long wanted, unsigned long seen, const char *format, ...) { if (wanted == seen) - printf("ok %d", testnum++); + printf("ok %lu", testnum++); else { printf("# wanted: %lx\n# seen: %lx\n", (unsigned long) wanted, (unsigned long) seen); - printf("not ok %d", testnum++); + printf("not ok %lu", testnum++); _failed++; } if (format != NULL) { @@ -354,3 +397,88 @@ sysbail(const char *format, ...) printf(": %s\n", strerror(oerrno)); exit(1); } + + +/* + * Report a diagnostic to stderr. + */ +void +diag(const char *format, ...) +{ + va_list args; + + fflush(stdout); + printf("# "); + va_start(args, format); + vprintf(format, args); + va_end(args); + printf("\n"); +} + + +/* + * Report a diagnostic to stderr, appending strerror(errno). + */ +void +sysdiag(const char *format, ...) +{ + va_list args; + int oerrno = errno; + + fflush(stdout); + printf("# "); + va_start(args, format); + vprintf(format, args); + va_end(args); + printf(": %s\n", strerror(oerrno)); +} + + +/* + * Locate a test file. Given the partial path to a file, look under BUILD and + * then SOURCE for the file and return the full path to the file. Returns + * NULL if the file doesn't exist. A non-NULL return should be freed with + * test_file_path_free(). + * + * This function uses sprintf because it attempts to be independent of all + * other portability layers. The use immediately after a memory allocation + * should be safe without using snprintf or strlcpy/strlcat. + */ +char * +test_file_path(const char *file) +{ + char *base; + char *path = NULL; + size_t length; + const char *envs[] = { "BUILD", "SOURCE", NULL }; + int i; + + for (i = 0; envs[i] != NULL; i++) { + base = getenv(envs[i]); + if (base == NULL) + continue; + length = strlen(base) + 1 + strlen(file) + 1; + path = malloc(length); + if (path == NULL) + sysbail("cannot allocate memory"); + sprintf(path, "%s/%s", base, file); + if (access(path, R_OK) == 0) + break; + free(path); + path = NULL; + } + return path; +} + + +/* + * Free a path returned from test_file_path(). This function exists primarily + * for Windows, where memory must be freed from the same library domain that + * it was allocated from. + */ +void +test_file_path_free(char *path) +{ + if (path != NULL) + free(path); +} diff --git a/tests/tap/basic.h b/tests/tap/basic.h index efe94ba..9602db4 100644 --- a/tests/tap/basic.h +++ b/tests/tap/basic.h @@ -1,6 +1,7 @@ /* * Basic utility routines for the TAP protocol. * + * Copyright 2009, 2010 Russ Allbery <rra@stanford.edu> * Copyright 2006, 2007, 2008 * Board of Trustees, Leland Stanford Jr. University * Copyright (c) 2004, 2005, 2006 @@ -14,6 +15,7 @@ #ifndef TAP_BASIC_H #define TAP_BASIC_H 1 +#include <stdarg.h> /* va_list */ #include <sys/types.h> /* pid_t */ /* @@ -56,29 +58,40 @@ BEGIN_DECLS * The test count. Always contains the number that will be used for the next * test status. */ -extern int testnum; +extern unsigned long testnum; /* Print out the number of tests and set standard output to line buffered. */ -void plan(int count); +void plan(unsigned long count); + +/* + * Prepare for lazy planning, in which the plan will be printed automatically + * at the end of the test program. + */ +void plan_lazy(void); /* Skip the entire test suite. Call instead of plan. */ void skip_all(const char *format, ...) __attribute__((__noreturn__, __format__(printf, 1, 2))); -/* Basic reporting functions. */ +/* + * Basic reporting functions. The okv() function is the same as ok() but + * takes the test description as a va_list to make it easier to reuse the + * reporting infrastructure when writing new tests. + */ void ok(int success, const char *format, ...) __attribute__((__format__(printf, 2, 3))); +void okv(int success, const char *format, va_list args); void skip(const char *reason, ...) __attribute__((__format__(printf, 1, 2))); /* Report the same status on, or skip, the next count tests. */ -void ok_block(int count, int success, const char *format, ...) +void ok_block(unsigned long count, int success, const char *format, ...) __attribute__((__format__(printf, 3, 4))); -void skip_block(int count, const char *reason, ...) +void skip_block(unsigned long count, const char *reason, ...) __attribute__((__format__(printf, 2, 3))); /* Check an expected value against a seen value. */ -void is_int(int wanted, int seen, const char *format, ...) +void is_int(long wanted, long seen, const char *format, ...) __attribute__((__format__(printf, 3, 4))); void is_double(double wanted, double seen, const char *format, ...) __attribute__((__format__(printf, 3, 4))); @@ -93,6 +106,20 @@ void bail(const char *format, ...) void sysbail(const char *format, ...) __attribute__((__noreturn__, __nonnull__, __format__(printf, 1, 2))); +/* Report a diagnostic to stderr prefixed with #. */ +void diag(const char *format, ...) + __attribute__((__nonnull__, __format__(printf, 1, 2))); +void sysdiag(const char *format, ...) + __attribute__((__nonnull__, __format__(printf, 1, 2))); + +/* + * Find a test file under BUILD or SOURCE, returning the full path. The + * returned path should be freed with test_file_path_free(). + */ +char *test_file_path(const char *file) + __attribute__((__malloc__, __nonnull__)); +void test_file_path_free(char *path); + END_DECLS -#endif /* LIBTEST_H */ +#endif /* TAP_BASIC_H */ diff --git a/tests/tap/kerberos.c b/tests/tap/kerberos.c index 700212e..a17d980 100644 --- a/tests/tap/kerberos.c +++ b/tests/tap/kerberos.c @@ -23,33 +23,6 @@ /* - * Given the partial path to a file, look under BUILD and then SOURCE for the - * file and return the full path to the file in newly-allocated memory. - * Returns NULL if the file doesn't exist. - */ -static char * -find_file(const char *file) -{ - char *base; - char *path = NULL; - const char *envs[] = { "BUILD", "SOURCE", NULL }; - int i; - - for (i = 0; envs[i] != NULL; i++) { - base = getenv(envs[i]); - if (base == NULL) - continue; - path = concatpath(base, file); - if (access(path, R_OK) == 0) - break; - free(path); - path = NULL; - } - return path; -} - - -/* * Obtain Kerberos tickets for the principal specified in test.principal using * the keytab specified in test.keytab, both of which are presumed to be in * tests/data in either the build or the source tree. @@ -78,7 +51,7 @@ kerberos_setup(void) krb5_creds creds; /* Read the principal name and find the keytab file. */ - path = find_file("data/test.principal"); + path = test_file_path("data/test.principal"); if (path == NULL) return NULL; file = fopen(path, "r"); @@ -95,7 +68,7 @@ kerberos_setup(void) bail("no newline in %s", path); free(path); principal[strlen(principal) - 1] = '\0'; - path = find_file("data/test.keytab"); + path = test_file_path("data/test.keytab"); if (path == NULL) return NULL; diff --git a/tests/tap/kerberos.sh b/tests/tap/kerberos.sh index fbeaaba..904cae5 100644 --- a/tests/tap/kerberos.sh +++ b/tests/tap/kerberos.sh @@ -1,4 +1,4 @@ -# Shell function library for Kerberos test support. +# Shell function library to initialize Kerberos credentials # # Written by Russ Allbery <rra@stanford.edu> # Copyright 2009, 2010 Board of Trustees, Leland Stanford Jr. University @@ -10,18 +10,9 @@ # configured. Sets the global principal variable to the principal to use. kerberos_setup () { local keytab - keytab='' - for f in "$BUILD/data/test.keytab" "$SOURCE/data/test.keytab" ; do - if [ -r "$f" ] ; then - keytab="$f" - fi - done - principal='' - for f in "$BUILD/data/test.principal" "$SOURCE/data/test.principal" ; do - if [ -r "$f" ] ; then - principal=`cat "$BUILD/data/test.principal"` - fi - done + keytab=`test_file_path data/test.keytab` + principal=`test_file_path data/test.principal` + principal=`cat "$principal" 2>/dev/null` if [ -z "$keytab" ] || [ -z "$principal" ] ; then return 1 fi diff --git a/tests/tap/libtap.sh b/tests/tap/libtap.sh index 1846840..a9b46d4 100644 --- a/tests/tap/libtap.sh +++ b/tests/tap/libtap.sh @@ -1,7 +1,7 @@ # Shell function library for test cases. # # Written by Russ Allbery <rra@stanford.edu> -# Copyright 2009 Russ Allbery <rra@stanford.edu> +# Copyright 2009, 2010 Russ Allbery <rra@stanford.edu> # Copyright 2006, 2007, 2008 Board of Trustees, Leland Stanford Jr. University # # See LICENSE for licensing terms. @@ -15,10 +15,22 @@ plan () { trap finish 0 } +# Prepare for lazy planning. +plan_lazy () { + count=1 + planned=0 + failed=0 + trap finish 0 +} + # Report the test status on exit. finish () { local highest looks highest=`expr "$count" - 1` + if [ "$planned" = 0 ] ; then + echo "1..$highest" + planned="$highest" + fi looks='# Looks like you' if [ "$planned" -gt 0 ] ; then if [ "$planned" -gt "$highest" ] ; then @@ -146,3 +158,21 @@ bail () { echo 'Bail out!' "$@" exit 1 } + +# Output a diagnostic on standard error, preceded by the required # mark. +diag () { + echo '#' "$@" +} + +# Search for the given file first in $BUILD and then in $SOURCE and echo the +# path where the file was found, or the empty string if the file wasn't +# found. +test_file_path () { + if [ -f "$BUILD/$1" ] ; then + echo "$BUILD/$1" + elif [ -f "$SOURCE/$1" ] ; then + echo "$SOURCE/$1" + else + echo '' + fi +} diff --git a/tests/tap/remctl.sh b/tests/tap/remctl.sh index b9667ef..9e01bcf 100644 --- a/tests/tap/remctl.sh +++ b/tests/tap/remctl.sh @@ -10,18 +10,12 @@ remctld_start () { local keytab principal rm -f "$BUILD/data/remctld.pid" - keytab='' - for f in "$BUILD/data/test.keytab" "$SOURCE/data/test.keytab" ; do - if [ -r "$f" ] ; then - keytab="$f" - fi - done - principal='' - for f in "$BUILD/data/test.principal" "$SOURCE/data/test.principal" ; do - if [ -r "$f" ] ; then - principal=`cat "$BUILD/data/test.principal"` - fi - done + keytab=`test_file_path data/test.keytab` + principal=`test_file_path data/test.principal` + principal=`cat "$principal" 2>/dev/null` + if [ -z "$keytab" ] || [ -z "$principal" ] ; then + return 1 + fi if [ -n "$VALGRIND" ] ; then ( "$VALGRIND" --log-file=valgrind.%p --leak-check=full "$1" -m \ -p 14373 -s "$principal" -P "$BUILD/data/remctld.pid" -f "$2" -d \ diff --git a/tests/util/messages-t.c b/tests/util/messages-t.c index fb82a42..a58f82c 100644 --- a/tests/util/messages-t.c +++ b/tests/util/messages-t.c @@ -146,8 +146,8 @@ test_strerror(int status, const char *output, int error, char *full_output, *name; full_output = concat(output, ": ", strerror(error), "\n", (char *) NULL); - xasprintf(&name, "strerror %d", testnum / 3 + 1); - is_function_output(function, status, full_output, name); + xasprintf(&name, "strerror %lu", testnum / 3 + 1); + is_function_output(function, status, full_output, "%s", name); free(full_output); free(name); } diff --git a/tests/util/xmalloc.c b/tests/util/xmalloc.c index 3bd5588..b6f4564 100644 --- a/tests/util/xmalloc.c +++ b/tests/util/xmalloc.c @@ -246,8 +246,6 @@ main(int argc, char *argv[]) size_t limit = 0; int willfail = 0; unsigned char code; - struct rlimit rl; - void *tmp; if (argc < 3) die("Usage error. Type, size, and limit must be given."); @@ -290,6 +288,9 @@ main(int argc, char *argv[]) */ if (limit > 0) { #if HAVE_SETRLIMIT && defined(RLIMIT_AS) + struct rlimit rl; + void *tmp; + rl.rlim_cur = limit; rl.rlim_max = limit; if (setrlimit(RLIMIT_AS, &rl) < 0) { diff --git a/util/macros.h b/util/macros.h index 97b2c2b..0104d9f 100644 --- a/util/macros.h +++ b/util/macros.h @@ -8,7 +8,6 @@ #ifndef UTIL_MACROS_H #define UTIL_MACROS_H 1 -#include <config.h> #include <portable/macros.h> /* Used for unused parameters to silence gcc warnings. */ diff --git a/util/messages.c b/util/messages.c index ef920b2..3592692 100644 --- a/util/messages.c +++ b/util/messages.c @@ -107,9 +107,9 @@ const char *message_program_name = NULL; * handler list, the count of handlers, and the argument list. */ static void -message_handlers(message_handler_func **list, int count, va_list args) +message_handlers(message_handler_func **list, unsigned int count, va_list args) { - int i; + unsigned int i; if (*list != stdout_handlers && *list != stderr_handlers) free(*list); @@ -127,7 +127,7 @@ message_handlers(message_handler_func **list, int count, va_list args) */ #define HANDLER_FUNCTION(type) \ void \ - message_handlers_ ## type(int count, ...) \ + message_handlers_ ## type(unsigned int count, ...) \ { \ va_list args; \ \ @@ -145,7 +145,7 @@ HANDLER_FUNCTION(die) * Print a message to stdout, supporting message_program_name. */ void -message_log_stdout(int len UNUSED, const char *fmt, va_list args, int err) +message_log_stdout(size_t len UNUSED, const char *fmt, va_list args, int err) { if (message_program_name != NULL) fprintf(stdout, "%s: ", message_program_name); @@ -162,7 +162,7 @@ message_log_stdout(int len UNUSED, const char *fmt, va_list args, int err) * stdout so that errors and regular output occur in the right order. */ void -message_log_stderr(int len UNUSED, const char *fmt, va_list args, int err) +message_log_stderr(size_t len UNUSED, const char *fmt, va_list args, int err) { fflush(stdout); if (message_program_name != NULL) @@ -183,7 +183,7 @@ message_log_stderr(int len UNUSED, const char *fmt, va_list args, int err) * log the errno information. */ static void -message_log_syslog(int pri, int len, const char *fmt, va_list args, int err) +message_log_syslog(int pri, size_t len, const char *fmt, va_list args, int err) { char *buffer; @@ -218,11 +218,11 @@ message_log_syslog(int pri, int len, const char *fmt, va_list args, int err) * Do the same sort of wrapper to generate all of the separate syslog logging * functions. */ -#define SYSLOG_FUNCTION(name, type) \ - void \ - message_log_syslog_ ## name(int l, const char *f, va_list a, int e) \ - { \ - message_log_syslog(LOG_ ## type, l, f, a, e); \ +#define SYSLOG_FUNCTION(name, type) \ + void \ + message_log_syslog_ ## name(size_t l, const char *f, va_list a, int e) \ + { \ + message_log_syslog(LOG_ ## type, l, f, a, e); \ } SYSLOG_FUNCTION(debug, DEBUG) SYSLOG_FUNCTION(info, INFO) @@ -243,7 +243,7 @@ debug(const char *format, ...) { va_list args; message_handler_func *log; - int length; + ssize_t length; if (debug_handlers == NULL) return; @@ -254,7 +254,7 @@ debug(const char *format, ...) return; for (log = debug_handlers; *log != NULL; log++) { va_start(args, format); - (**log)(length, format, args, 0); + (**log)((size_t) length, format, args, 0); va_end(args); } } @@ -264,7 +264,7 @@ notice(const char *format, ...) { va_list args; message_handler_func *log; - int length; + ssize_t length; va_start(args, format); length = vsnprintf(NULL, 0, format, args); @@ -273,7 +273,7 @@ notice(const char *format, ...) return; for (log = notice_handlers; *log != NULL; log++) { va_start(args, format); - (**log)(length, format, args, 0); + (**log)((size_t) length, format, args, 0); va_end(args); } } @@ -283,7 +283,7 @@ sysnotice(const char *format, ...) { va_list args; message_handler_func *log; - int length; + ssize_t length; int error = errno; va_start(args, format); @@ -293,7 +293,7 @@ sysnotice(const char *format, ...) return; for (log = notice_handlers; *log != NULL; log++) { va_start(args, format); - (**log)(length, format, args, error); + (**log)((size_t) length, format, args, error); va_end(args); } } @@ -303,7 +303,7 @@ warn(const char *format, ...) { va_list args; message_handler_func *log; - int length; + ssize_t length; va_start(args, format); length = vsnprintf(NULL, 0, format, args); @@ -312,7 +312,7 @@ warn(const char *format, ...) return; for (log = warn_handlers; *log != NULL; log++) { va_start(args, format); - (**log)(length, format, args, 0); + (**log)((size_t) length, format, args, 0); va_end(args); } } @@ -322,7 +322,7 @@ syswarn(const char *format, ...) { va_list args; message_handler_func *log; - int length; + ssize_t length; int error = errno; va_start(args, format); @@ -332,7 +332,7 @@ syswarn(const char *format, ...) return; for (log = warn_handlers; *log != NULL; log++) { va_start(args, format); - (**log)(length, format, args, error); + (**log)((size_t) length, format, args, error); va_end(args); } } @@ -342,7 +342,7 @@ die(const char *format, ...) { va_list args; message_handler_func *log; - int length; + ssize_t length; va_start(args, format); length = vsnprintf(NULL, 0, format, args); @@ -350,7 +350,7 @@ die(const char *format, ...) if (length >= 0) for (log = die_handlers; *log != NULL; log++) { va_start(args, format); - (**log)(length, format, args, 0); + (**log)((size_t) length, format, args, 0); va_end(args); } exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1); @@ -361,7 +361,7 @@ sysdie(const char *format, ...) { va_list args; message_handler_func *log; - int length; + ssize_t length; int error = errno; va_start(args, format); @@ -370,7 +370,7 @@ sysdie(const char *format, ...) if (length >= 0) for (log = die_handlers; *log != NULL; log++) { va_start(args, format); - (**log)(length, format, args, error); + (**log)((size_t) length, format, args, error); va_end(args); } exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1); diff --git a/util/messages.h b/util/messages.h index ff86f39..dbdb256 100644 --- a/util/messages.h +++ b/util/messages.h @@ -49,35 +49,35 @@ void sysdie(const char *, ...) * of those handlers. These functions are not thread-safe; they set global * variables. */ -void message_handlers_debug(int count, ...); -void message_handlers_notice(int count, ...); -void message_handlers_warn(int count, ...); -void message_handlers_die(int count, ...); +void message_handlers_debug(unsigned int count, ...); +void message_handlers_notice(unsigned int count, ...); +void message_handlers_warn(unsigned int count, ...); +void message_handlers_die(unsigned int count, ...); /* * Some useful handlers, intended to be passed to message_handlers_*. All * handlers take the length of the formatted message, the format, a variadic * argument list, and the errno setting if any. */ -void message_log_stdout(int, const char *, va_list, int) +void message_log_stdout(size_t, const char *, va_list, int) __attribute((__nonnull__)); -void message_log_stderr(int, const char *, va_list, int) +void message_log_stderr(size_t, const char *, va_list, int) __attribute((__nonnull__)); -void message_log_syslog_debug(int, const char *, va_list, int) +void message_log_syslog_debug(size_t, const char *, va_list, int) __attribute((__nonnull__)); -void message_log_syslog_info(int, const char *, va_list, int) +void message_log_syslog_info(size_t, const char *, va_list, int) __attribute((__nonnull__)); -void message_log_syslog_notice(int, const char *, va_list, int) +void message_log_syslog_notice(size_t, const char *, va_list, int) __attribute((__nonnull__)); -void message_log_syslog_warning(int, const char *, va_list, int) +void message_log_syslog_warning(size_t, const char *, va_list, int) __attribute((__nonnull__)); -void message_log_syslog_err(int, const char *, va_list, int) +void message_log_syslog_err(size_t, const char *, va_list, int) __attribute((__nonnull__)); -void message_log_syslog_crit(int, const char *, va_list, int) +void message_log_syslog_crit(size_t, const char *, va_list, int) __attribute((__nonnull__)); /* The type of a message handler. */ -typedef void (*message_handler_func)(int, const char *, va_list, int); +typedef void (*message_handler_func)(size_t, const char *, va_list, int); /* If non-NULL, called before exit and its return value passed to exit. */ extern int (*message_fatal_cleanup)(void); |