From c02942ddc12408f0e5b9d828cddf240519d1fe93 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Tue, 9 Feb 2010 18:40:22 -0800 Subject: Update to C TAP Harness 1.1 and rra-c-util 3.0 tests * Update portable and util tests for C TAP Harness 1.1. * Remove the need for Autoconf substitution in test programs. * Support running a single test program with runtests -o. * Properly handle test cases that are skipped in their entirety. * Much improved C TAP library more closely matching Test::More. Rewrite client/basic-t to use the new test library functions and my current test case coding style. --- tests/tap/libtap.sh | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 tests/tap/libtap.sh (limited to 'tests/tap/libtap.sh') diff --git a/tests/tap/libtap.sh b/tests/tap/libtap.sh new file mode 100644 index 0000000..1846840 --- /dev/null +++ b/tests/tap/libtap.sh @@ -0,0 +1,148 @@ +# Shell function library for test cases. +# +# Written by Russ Allbery +# Copyright 2009 Russ Allbery +# Copyright 2006, 2007, 2008 Board of Trustees, Leland Stanford Jr. University +# +# See LICENSE for licensing terms. + +# Print out the number of test cases we expect to run. +plan () { + count=1 + planned="$1" + failed=0 + echo "1..$1" + trap finish 0 +} + +# Report the test status on exit. +finish () { + local highest looks + highest=`expr "$count" - 1` + looks='# Looks like you' + if [ "$planned" -gt 0 ] ; then + if [ "$planned" -gt "$highest" ] ; then + if [ "$planned" -gt 1 ] ; then + echo "$looks planned $planned tests but only ran $highest" + else + echo "$looks planned $planned test but only ran $highest" + fi + elif [ "$planned" -lt "$highest" ] ; then + local extra + extra=`expr "$highest" - "$planned"` + if [ "$planned" -gt 1 ] ; then + echo "$looks planned $planned tests but ran $extra extra" + else + echo "$looks planned $planned test but ran $extra extra" + fi + elif [ "$failed" -gt 0 ] ; then + if [ "$failed" -gt 1 ] ; then + echo "$looks failed $failed tests of $planned" + else + echo "$looks failed $failed test of $planned" + fi + elif [ "$planned" -gt 1 ] ; then + echo "# All $planned tests successful or skipped" + else + echo "# $planned test successful or skipped" + fi + fi +} + +# Skip the entire test suite. Should be run instead of plan. +skip_all () { + local desc + desc="$1" + if [ -n "$desc" ] ; then + echo "1..0 # skip $desc" + else + echo "1..0 # skip" + fi + exit 0 +} + +# ok takes a test description and a command to run and prints success if that +# command is successful, false otherwise. The count starts at 1 and is +# updated each time ok is printed. +ok () { + local desc + desc="$1" + if [ -n "$desc" ] ; then + desc=" - $desc" + fi + shift + if "$@" ; then + echo ok $count$desc + else + echo not ok $count$desc + failed=`expr $failed + 1` + fi + count=`expr $count + 1` +} + +# Skip the next test. Takes the reason why the test is skipped. +skip () { + echo "ok $count # skip $*" + count=`expr $count + 1` +} + +# Report the same status on a whole set of tests. Takes the count of tests, +# the description, and then the command to run to determine the status. +ok_block () { + local end i desc + i=$count + end=`expr $count + $1` + shift + desc="$1" + shift + while [ "$i" -lt "$end" ] ; do + ok "$desc" "$@" + i=`expr $i + 1` + done +} + +# Skip a whole set of tests. Takes the count and then the reason for skipping +# the test. +skip_block () { + local i end + i=$count + end=`expr $count + $1` + shift + while [ "$i" -lt "$end" ] ; do + skip "$@" + i=`expr $i + 1` + done +} + +# Run a program expected to succeed, and print ok if it does and produces the +# correct output. Takes the description, expected exit status, the expected +# output, the command to run, and then any arguments for that command. Strip +# a colon and everything after it off the output if the expected status is +# non-zero, since this is probably a system-specific error message. +ok_program () { + local desc w_status w_output output status + desc="$1" + shift + w_status="$1" + shift + w_output="$1" + shift + output=`"$@" 2>&1` + status=$? + if [ "$w_status" -ne 0 ] ; then + output=`echo "$output" | sed 's/^\([^:]* [^:]*\):.*/\1/'` + fi + if [ $status = $w_status ] && [ x"$output" = x"$w_output" ] ; then + ok "$desc" true + else + echo "# saw: ($status) $output" + echo "# not: ($w_status) $w_output" + ok "$desc" false + fi +} + +# Bail out with an error message. +bail () { + echo 'Bail out!' "$@" + exit 1 +} -- cgit v1.2.3 From 468ded4c2fae05a815bef91bdcb17d52f9cdcb2b Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Wed, 25 Aug 2010 15:08:05 -0700 Subject: 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. --- NEWS | 14 ++ configure.ac | 4 +- m4/gssapi.m4 | 3 +- m4/krb5.m4 | 39 +++-- m4/remctl.m4 | 2 +- portable/krb5-extra.c | 3 +- portable/krb5.h | 31 +++- tests/portable/snprintf-t.c | 12 +- tests/runtests.c | 403 +++++++++++++++++++++++++++++--------------- tests/tap/basic.c | 196 +++++++++++++++++---- tests/tap/basic.h | 41 ++++- tests/tap/kerberos.c | 31 +--- tests/tap/kerberos.sh | 17 +- tests/tap/libtap.sh | 32 +++- tests/tap/remctl.sh | 18 +- tests/util/messages-t.c | 4 +- tests/util/xmalloc.c | 5 +- util/macros.h | 1 - util/messages.c | 50 +++--- util/messages.h | 26 +-- 20 files changed, 631 insertions(+), 301 deletions(-) (limited to 'tests/tap/libtap.sh') diff --git a/NEWS b/NEWS index 6202878..f4e7abb 100644 --- a/NEWS +++ b/NEWS @@ -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 ]) 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 diff --git a/m4/krb5.m4 b/m4/krb5.m4 index bba9694..38a050e 100644 --- a/m4/krb5.m4 +++ b/m4/krb5.m4 @@ -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 -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_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 +/* + * 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 +#endif #include #include +#include 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 - * 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, @@ -16,6 +16,12 @@ #include +/* + * 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 * not ok * ok # skip + * not ok # todo * - * where 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 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 * * 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,13 +194,27 @@ 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; } +/* + * 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. */ @@ -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); @@ -234,62 +278,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 @@ -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; @@ -354,6 +342,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 @@ -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 + * Copyright 2009, 2010 Russ Allbery * 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(); @@ -95,6 +106,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) { @@ -148,13 +176,28 @@ 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 * 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 /* va_list */ #include /* 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 @@ -22,33 +22,6 @@ #include -/* - * 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 @@ -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 # 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 -# Copyright 2009 Russ Allbery +# Copyright 2009, 2010 Russ Allbery # 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 #include /* 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); -- cgit v1.2.3 From 234e3805c524a7432caed8be328df6e2fbfe9afb Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Wed, 27 Feb 2013 14:25:37 -0800 Subject: Update to rra-c-util 4.8 and C TAP Harness 1.12 Update to rra-c-util 4.8: * Look for krb5-config in /usr/kerberos/bin after the user's PATH. * Kerberos library probing fixes without transitive shared libraries. * Fix Autoconf warnings when probing for AIX's bundled Kerberos. * Avoid using krb5-config if --with-{krb5,gssapi}-{include,lib} given. * Correctly remove -I/usr/include from Kerberos and GSS-API flags. * Build on systems where krb5/krb5.h exists but krb5.h does not. * Pass --deps to krb5-config unless --enable-reduced-depends was used. * Do not use krb5-config results unless gssapi is supported. * Fix probing for Heimdal's libroken to work with older versions. * Update warning flags for GCC 4.6.1. * Update utility library and test suite for newer GCC warnings. * Fix broken GCC attribute markers causing compilation problems. * Suppress warnings on compilers that support gcc's __attribute__. * Add notices to all files copied over from rra-c-util. * Fix warnings when reporting memory allocation failure in messages.c. * Fix message utility library compiler warnings on 64-bit systems. * Include strings.h for additional POSIX functions where found. * Use an atexit handler to clean up after Kerberos tests. * Kerberos test configuration now goes in tests/config. * The principal of the test keytab is determined automatically. * Simplify the test suite calls for Kerberos and remctl tests. * Check for a missing ssize_t. * Improve the xstrndup utility function. * Checked asprintf variants are now void functions and cannot fail. * Fix use of long long in portable/mkstemp.c. * Fix test suite portability to Solaris. * Substantial improvements to the POD syntax and spelling checks. Update to C TAP Harness 1.12: * Fix compliation of runtests with more aggressive warnings. * Add a more complete usage message and a -h command-line flag. * Flush stderr before printing output from tests. * Better handle running shell tests without BUILD and SOURCE set. * Fix runtests to honor -s even if BUILD and -b aren't given. * runtests now frees all allocated resources on exit. * Only use feature-test macros when requested or built with gcc -ansi. * Drop is_double from the C TAP library to avoid requiring -lm. * Avoid using local in the shell libtap.sh library. * Suppress warnings on compilers that support gcc's __attribute__. Change-Id: I394294d5486ac1ce265c7713bec71a148aaaf1ce Reviewed-on: https://gerrit.stanford.edu/841 Reviewed-by: Russ Allbery Tested-by: Russ Allbery --- .gitignore | 8 +- Makefile.am | 20 +- NEWS | 43 ++++ README | 2 +- configure.ac | 35 +-- m4/gssapi.m4 | 114 +++++--- m4/krb5-config.m4 | 101 ++++++++ m4/krb5.m4 | 206 +++++++++------ m4/lib-depends.m4 | 9 +- m4/lib-pathname.m4 | 10 +- m4/remctl.m4 | 94 +++++-- m4/snprintf.m4 | 9 +- m4/vamacros.m4 | 9 +- portable/asprintf.c | 12 +- portable/dummy.c | 12 +- portable/krb5-extra.c | 12 +- portable/krb5.h | 20 +- portable/macros.h | 35 ++- portable/mkstemp.c | 14 +- portable/setenv.c | 19 +- portable/snprintf.c | 3 + portable/stdbool.h | 20 +- portable/strlcat.c | 12 +- portable/strlcpy.c | 12 +- portable/system.h | 75 ++++-- portable/uio.h | 12 +- tests/client/full-t.in | 8 +- tests/client/prompt-t.in | 12 +- tests/config/README | 24 ++ tests/data/perl.conf | 6 + tests/docs/pod-spelling-t | 108 +++----- tests/docs/pod-t | 52 +++- tests/portable/asprintf-t.c | 13 +- tests/portable/mkstemp-t.c | 13 +- tests/portable/setenv-t.c | 16 +- tests/portable/snprintf-t.c | 18 +- tests/portable/strlcat-t.c | 16 +- tests/portable/strlcpy-t.c | 16 +- tests/runtests.c | 165 ++++++++---- tests/tap/basic.c | 239 +++++++++++++---- tests/tap/basic.h | 81 +++--- tests/tap/kerberos.c | 499 ++++++++++++++++++++++++++++++------ tests/tap/kerberos.h | 115 ++++++++- tests/tap/kerberos.sh | 64 +++-- tests/tap/libtap.sh | 192 +++++++++----- tests/tap/macros.h | 88 +++++++ tests/tap/messages.c | 49 ++-- tests/tap/messages.h | 30 ++- tests/tap/perl/Test/RRA.pm | 222 ++++++++++++++++ tests/tap/perl/Test/RRA/Automake.pm | 362 ++++++++++++++++++++++++++ tests/tap/perl/Test/RRA/Config.pm | 200 +++++++++++++++ tests/tap/process.c | 125 +++++++-- tests/tap/process.h | 52 +++- tests/tap/remctl.sh | 61 +++-- tests/tap/string.c | 65 +++++ tests/tap/string.h | 49 ++++ tests/util/messages-krb5-t.c | 41 ++- tests/util/messages-t.c | 126 +++++---- tests/util/xmalloc-t | 130 ++++++---- tests/util/xmalloc.c | 72 ++++-- util/macros.h | 21 +- util/messages-krb5.c | 50 +++- util/messages-krb5.h | 23 +- util/messages.c | 26 +- util/messages.h | 37 ++- util/xmalloc.c | 62 +++-- util/xmalloc.h | 27 +- 67 files changed, 3603 insertions(+), 890 deletions(-) create mode 100644 m4/krb5-config.m4 create mode 100644 tests/config/README create mode 100644 tests/data/perl.conf create mode 100644 tests/tap/macros.h create mode 100644 tests/tap/perl/Test/RRA.pm create mode 100644 tests/tap/perl/Test/RRA/Automake.pm create mode 100644 tests/tap/perl/Test/RRA/Config.pm create mode 100644 tests/tap/string.c create mode 100644 tests/tap/string.h (limited to 'tests/tap/libtap.sh') diff --git a/.gitignore b/.gitignore index d5ae8a0..23ffe53 100644 --- a/.gitignore +++ b/.gitignore @@ -22,11 +22,9 @@ /tests/client/full-t /tests/client/prompt-t /tests/client/rekey-t -/tests/data/.placeholder -/tests/data/test.keytab -/tests/data/test.password -/tests/data/test.principal -/tests/data/test.krbtype +/tests/config/keytab +/tests/config/password +/tests/config/principal /tests/portable/asprintf-t /tests/portable/mkstemp-t /tests/portable/setenv-t diff --git a/Makefile.am b/Makefile.am index 0e1d99c..772a71e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -26,7 +26,6 @@ PERL_FILES = perl/Wallet/ACL.pm perl/Wallet/ACL/Base.pm \ perl/t/schema.t perl/t/server.t perl/t/verifier-netdb.t \ perl/t/verifier.t -AUTOMAKE_OPTIONS = foreign subdir-objects ACLOCAL_AMFLAGS = -I m4 EXTRA_DIST = .gitignore LICENSE autogen client/wallet.pod \ client/wallet-rekey.pod config/allow-extract config/keytab \ @@ -97,18 +96,22 @@ dist_pkgdata_DATA = perl/sql/Wallet-Schema-0.07-0.08-MySQL.sql \ # # -Wconversion http://bugs.debian.org/488884 (htons warnings) # -# Last checked against gcc 4.4 (2010-08-15). -WARNINGS = -g -O -Wall -Wextra -Wendif-labels -Wformat=2 -Winit-self \ - -Wswitch-enum -Wdeclaration-after-statement -Wshadow -Wpointer-arith \ - -Wbad-function-cast -Wwrite-strings -Wstrict-prototypes \ - -Wmissing-prototypes -Wnested-externs -Werror +# Last checked against gcc 4.6.1 (2011-05-04). -D_FORTIFY_SOURCE=2 enables +# warn_unused_result attribute markings on glibc functions on Linux, which +# catches a few more issues. +WARNINGS = -g -O -D_FORTIFY_SOURCE=2 -Wall -Wextra -Wendif-labels \ + -Wformat=2 -Winit-self -Wswitch-enum -Wdeclaration-after-statement \ + -Wshadow -Wpointer-arith -Wbad-function-cast -Wcast-align \ + -Wwrite-strings -Wjump-misses-init -Wlogical-op \ + -Wstrict-prototypes -Wmissing-prototypes -Wredundant-decls \ + -Wnested-externs -Werror warnings: $(MAKE) V=0 CFLAGS='$(WARNINGS)' $(MAKE) V=0 CFLAGS='$(WARNINGS)' $(check_PROGRAMS) # Remove some additional files. -DISTCLEANFILES = perl/Makefile tests/data/.placeholder +DISTCLEANFILES = perl/Makefile MAINTAINERCLEANFILES = Makefile.in aclocal.m4 build-aux/compile \ build-aux/depcomp build-aux/install-sh build-aux/missing \ client/wallet.1 config.h.in config.h.in~ configure \ @@ -163,7 +166,8 @@ check_LIBRARIES = tests/tap/libtap.a tests_tap_libtap_a_CPPFLAGS = -I$(abs_top_srcdir)/tests $(KRB5_CPPFLAGS) tests_tap_libtap_a_SOURCES = tests/tap/basic.c tests/tap/basic.h \ tests/tap/kerberos.c tests/tap/kerberos.h tests/tap/messages.c \ - tests/tap/messages.h tests/tap/process.c tests/tap/process.h + tests/tap/messages.h tests/tap/process.c tests/tap/process.h \ + tests/tap/string.c tests/tap/string.h # All of the test programs. tests_portable_asprintf_t_SOURCES = tests/portable/asprintf-t.c \ diff --git a/NEWS b/NEWS index b948d91..0d98220 100644 --- a/NEWS +++ b/NEWS @@ -27,6 +27,49 @@ wallet 1.0 (unreleased) Add docs/objects-and-schemes, which provides a brief summary of the current supported object types and ACL schemes. + Update to rra-c-util 4.8: + + * Look for krb5-config in /usr/kerberos/bin after the user's PATH. + * Kerberos library probing fixes without transitive shared libraries. + * Fix Autoconf warnings when probing for AIX's bundled Kerberos. + * Avoid using krb5-config if --with-{krb5,gssapi}-{include,lib} given. + * Correctly remove -I/usr/include from Kerberos and GSS-API flags. + * Build on systems where krb5/krb5.h exists but krb5.h does not. + * Pass --deps to krb5-config unless --enable-reduced-depends was used. + * Do not use krb5-config results unless gssapi is supported. + * Fix probing for Heimdal's libroken to work with older versions. + * Update warning flags for GCC 4.6.1. + * Update utility library and test suite for newer GCC warnings. + * Fix broken GCC attribute markers causing compilation problems. + * Suppress warnings on compilers that support gcc's __attribute__. + * Add notices to all files copied over from rra-c-util. + * Fix warnings when reporting memory allocation failure in messages.c. + * Fix message utility library compiler warnings on 64-bit systems. + * Include strings.h for additional POSIX functions where found. + * Use an atexit handler to clean up after Kerberos tests. + * Kerberos test configuration now goes in tests/config. + * The principal of the test keytab is determined automatically. + * Simplify the test suite calls for Kerberos and remctl tests. + * Check for a missing ssize_t. + * Improve the xstrndup utility function. + * Checked asprintf variants are now void functions and cannot fail. + * Fix use of long long in portable/mkstemp.c. + * Fix test suite portability to Solaris. + * Substantial improvements to the POD syntax and spelling checks. + + Update to C TAP Harness 1.12: + + * Fix compliation of runtests with more aggressive warnings. + * Add a more complete usage message and a -h command-line flag. + * Flush stderr before printing output from tests. + * Better handle running shell tests without BUILD and SOURCE set. + * Fix runtests to honor -s even if BUILD and -b aren't given. + * runtests now frees all allocated resources on exit. + * Only use feature-test macros when requested or built with gcc -ansi. + * Drop is_double from the C TAP library to avoid requiring -lm. + * Avoid using local in the shell libtap.sh library. + * Suppress warnings on compilers that support gcc's __attribute__. + wallet 0.12 (2010-08-25) New client program wallet-rekey that, given a list of keytabs on the diff --git a/README b/README index c440b8c..b714098 100644 --- a/README +++ b/README @@ -223,7 +223,7 @@ TESTING support in the server, however, you will need to do some preparatory work before running the test suite. Review the files: - tests/data/README + tests/config/README perl/t/data/README and follow the instructions in those files to enable the full test diff --git a/configure.ac b/configure.ac index ffd7eeb..a79e42d 100644 --- a/configure.ac +++ b/configure.ac @@ -13,7 +13,8 @@ AC_INIT([wallet], [0.12], [rra@stanford.edu]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_LIBOBJ_DIR([portable]) AC_CONFIG_MACRO_DIR([m4]) -AM_INIT_AUTOMAKE([1.11 check-news silent-rules]) +AM_INIT_AUTOMAKE([1.11 check-news dist-xz foreign silent-rules subdir-objects + -Wall -Wno-override -Werror]) AM_MAINTAINER_MODE AC_PROG_CC @@ -22,6 +23,18 @@ AM_PROG_CC_C_O AC_PROG_INSTALL AC_PROG_RANLIB +AC_ARG_WITH([wallet-server], + [AC_HELP_STRING([--with-wallet-server=HOST], [Default wallet server])], + [AS_IF([test x"$withval" != xno && test x"$withval" != xyes], + [AC_DEFINE_UNQUOTED([WALLET_SERVER], ["$withval"], + [Define to the default server host name.])])]) +AC_ARG_WITH([wallet-port], + [AC_HELP_STRING([--with-wallet-port=PORT], + [Default wallet server port])], + [AS_IF([test x"$withval" != xno && test x"$withval" != xyes], + [AC_DEFINE_UNQUOTED([WALLET_PORT], [$withval], + [Define to the default server port.])])]) + RRA_LIB_REMCTL RRA_LIB_KRB5 RRA_LIB_KRB5_SWITCH @@ -30,8 +43,9 @@ AC_CHECK_FUNCS([krb5_get_init_creds_opt_alloc \ 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], [], [], [RRA_INCLUDES_KRB5]) AC_CHECK_DECLS([krb5_kt_free_entry]) -AC_CHECK_MEMBERS([krb5_keytab_entry.keyblock], , , [#include ]) +AC_CHECK_MEMBERS([krb5_keytab_entry.keyblock], [], [], [RRA_INCLUDES_KRB5]) RRA_LIB_KRB5_RESTORE AC_HEADER_STDBOOL @@ -40,31 +54,18 @@ AC_CHECK_DECLS([snprintf, vsnprintf]) RRA_C_C99_VAMACROS RRA_C_GNU_VAMACROS AC_TYPE_LONG_LONG_INT +AC_CHECK_TYPES([ssize_t], [], [], + [#include ]) RRA_FUNC_SNPRINTF AC_CHECK_FUNCS([setrlimit]) AC_REPLACE_FUNCS([asprintf mkstemp setenv strlcat strlcpy]) -AC_ARG_WITH([wallet-server], - [AC_HELP_STRING([--with-wallet-server=HOST], [Default wallet server])], - [AS_IF([test x"$withval" != xno && test x"$withval" != xyes], - [AC_DEFINE_UNQUOTED([WALLET_SERVER], ["$withval"], - [Define to the default server host name.])])]) -AC_ARG_WITH([wallet-port], - [AC_HELP_STRING([--with-wallet-port=PORT], - [Default wallet server port])], - [AS_IF([test x"$withval" != xno && test x"$withval" != xyes], - [AC_DEFINE_UNQUOTED([WALLET_PORT], [$withval], - [Define to the default server port.])])]) - AC_ARG_VAR([REMCTLD], [Path to the remctld binary]) AC_PATH_PROG([REMCTLD], [remctld], , [$PATH:/usr/sbin:/usr/local/sbin]) AS_IF([test x"$REMCTLD" != x], [AC_DEFINE_UNQUOTED([PATH_REMCTLD], ["$REMCTLD"], [Define to the full path to remctld to run remctl tests.])]) -dnl Create the tests/data directory for builds outside the source directory. -AC_CONFIG_COMMANDS([tests/data/.placeholder], [touch tests/data/.placeholder]) - AC_CONFIG_HEADER([config.h]) AC_CONFIG_FILES([Makefile perl/Makefile.PL]) AC_CONFIG_FILES([tests/client/basic-t], [chmod +x tests/client/basic-t]) diff --git a/m4/gssapi.m4 b/m4/gssapi.m4 index 0a657ff..c596609 100644 --- a/m4/gssapi.m4 +++ b/m4/gssapi.m4 @@ -3,7 +3,8 @@ dnl dnl Finds the compiler and linker flags for linking with GSS-API libraries. dnl Provides the --with-gssapi, --with-gssapi-include, and --with-gssapi-lib dnl configure option to specify a non-standard path to the GSS-API libraries. -dnl Uses krb5-config where available unless reduced dependencies is requested. +dnl Uses krb5-config where available unless reduced dependencies is requested +dnl or --with-gssapi-include or --with-gssapi-lib are given. dnl dnl Provides the macro RRA_LIB_GSSAPI and sets the substitution variables dnl GSSAPI_CPPFLAGS, GSSAPI_LDFLAGS, and GSSAPI_LIBS. Also provides @@ -11,13 +12,34 @@ dnl RRA_LIB_GSSAPI_SWITCH to set CPPFLAGS, LDFLAGS, and LIBS to include the dnl GSS-API libraries, saving the ecurrent values, and RRA_LIB_GSSAPI_RESTORE dnl to restore those settings to before the last RRA_LIB_GSSAPI_SWITCH. dnl -dnl Depends on RRA_ENABLE_REDUCED_DEPENDS and RRA_SET_LDFLAGS. +dnl Also provides RRA_INCLUDES_KRB5, which are the headers to include when +dnl probing the Kerberos library properties. +dnl +dnl Depends on RRA_KRB5_CONFIG, RRA_ENABLE_REDUCED_DEPENDS, and +dnl RRA_SET_LDFLAGS. +dnl +dnl The canonical version of this file is maintained in the rra-c-util +dnl package, available at . dnl dnl Written by Russ Allbery -dnl Copyright 2005, 2006, 2007, 2008, 2009 -dnl Board of Trustees, Leland Stanford Jr. University +dnl Copyright 2005, 2006, 2007, 2008, 2009, 2011, 2012 +dnl The Board of Trustees of the Leland Stanford Junior University dnl -dnl See LICENSE for licensing terms. +dnl This file is free software; the authors give unlimited permission to copy +dnl and/or distribute it, with or without modifications, as long as this +dnl notice is preserved. + +dnl Headers to include when probing for Kerberos library properties. +AC_DEFUN([RRA_INCLUDES_GSSAPI], [[ +#ifdef HAVE_GSSAPI_GSSAPI_H +# include +#else +# include +#endif +#ifdef HAVE_GSSAPI_GSSAPI_KRB5_H +# include +#endif +]]) dnl Save the current CPPFLAGS, LDFLAGS, and LIBS settings and switch to dnl versions that include the GSS-API flags. Used as a wrapper, with @@ -68,18 +90,18 @@ AC_DEFUN([_RRA_LIB_GSSAPI_MANUAL], [RRA_LIB_GSSAPI_SWITCH rra_gssapi_extra= LIBS= - AC_SEARCH_LIBS([res_search], [resolv], , + AC_SEARCH_LIBS([res_search], [resolv], [], [AC_SEARCH_LIBS([__res_search], [resolv])]) AC_SEARCH_LIBS([gethostbyname], [nsl]) - AC_SEARCH_LIBS([socket], [socket], , - [AC_CHECK_LIB([nsl], [socket], [LIBS="-lnsl -lsocket $LIBS"], , + AC_SEARCH_LIBS([socket], [socket], [], + [AC_CHECK_LIB([nsl], [socket], [LIBS="-lnsl -lsocket $LIBS"], [], [-lsocket])]) AC_SEARCH_LIBS([crypt], [crypt]) + AC_SEARCH_LIBS([roken_concat], [roken]) rra_gssapi_extra="$LIBS" LIBS="$rra_gssapi_save_LIBS" AC_CHECK_LIB([gssapi], [gss_import_name], - [GSSAPI_LIBS="-lgssapi -lkrb5 -lasn1 -lroken -lcrypto -lcom_err" - GSSAPI_LIBS="$GSSAPI_LIBS $rra_gssapi_extra"], + [GSSAPI_LIBS="-lgssapi -lkrb5 -lasn1 -lcrypto -lcom_err $rra_gssapi_extra"], [AC_CHECK_LIB([krb5support], [krb5int_getspecific], [rra_gssapi_extra="-lkrb5support $rra_gssapi_extra"], [AC_CHECK_LIB([pthreads], [pthread_setspecific], @@ -88,7 +110,7 @@ AC_DEFUN([_RRA_LIB_GSSAPI_MANUAL], [rra_gssapi_pthread="-lpthread"])]) AC_CHECK_LIB([krb5support], [krb5int_setspecific], [rra_gssapi_extra="-lkrb5support $rra_gssapi_extra" - rra_gssapi_extra="$rra_gssapi_extra $rra_gssapi_pthread"], , + rra_gssapi_extra="$rra_gssapi_extra $rra_gssapi_pthread"], [], [$rra_gssapi_pthread])]) AC_CHECK_LIB([com_err], [error_message], [rra_gssapi_extra="-lcom_err $rra_gssapi_extra"]) @@ -101,7 +123,7 @@ AC_DEFUN([_RRA_LIB_GSSAPI_MANUAL], [GSSAPI_LIBS="-lgss"], [AC_MSG_ERROR([cannot find usable GSS-API library])])], [$rra_gssapi_extra])], - [-lkrb5 -lasn1 -lroken -lcrypto -lcom_err $rra_gssapi_extra]) + [-lkrb5 -lasn1 -lcrypto -lcom_err $rra_gssapi_extra]) RRA_LIB_GSSAPI_RESTORE]) dnl Sanity-check the results of krb5-config and be sure we can really link a @@ -116,6 +138,44 @@ AC_DEFUN([_RRA_LIB_GSSAPI_CHECK], _RRA_LIB_GSSAPI_PATHS _RRA_LIB_GSSAPI_MANUAL])]) +dnl Determine GSS-API compiler and linker flags from krb5-config. +AC_DEFUN([_RRA_LIB_GSSAPI_CONFIG], +[RRA_KRB5_CONFIG([${rra_gssapi_root}], [gssapi], [GSSAPI], + [_RRA_LIB_GSSAPI_CHECK], + [_RRA_LIB_GSSAPI_PATHS + _RRA_LIB_GSSAPI_MANUAL])]) + +dnl Check for a header using a file existence check rather than using +dnl AC_CHECK_HEADERS. This is used if there were arguments to configure +dnl specifying the GSS-API library path, since we may have one header in the +dnl default include path and another under our explicitly-configured GSS-API +dnl location. +AC_DEFUN([_RRA_LIB_GSSAPI_CHECK_HEADER], +[AC_MSG_CHECKING([for $1]) + AS_IF([test -f "${rra_gssapi_incroot}/$1"], + [AC_DEFINE_UNQUOTED(AS_TR_CPP([HAVE_$1]), [1], + [Define to 1 if you have the <$1> header file.]) + AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no])])]) + +dnl Determine the GSS-API header location and probe for some other +dnl characteristics of the libraries. We use a file existence check rather +dnl than letting the compiler probe for the right header location +AC_DEFUN([_RRA_LIB_GSSAPI_EXTRA], +[rra_gssapi_incroot= + AS_IF([test x"$rra_gssapi_includedir" != x], + [rra_gssapi_incroot="$rra_gssapi_includedir"], + [AS_IF([test x"$rra_gssapi_root" != x], + [rra_gssapi_incroot="${rra_gssapi_root}/include"])]) + AS_IF([test x"$rra_gssapi_incroot" = x], + [AC_CHECK_HEADERS([gssapi/gssapi.h gssapi/gssapi_krb5.h])], + [_RRA_LIB_GSSAPI_CHECK_HEADER([gssapi/gssapi.h]) + _RRA_LIB_GSSAPI_CHECK_HEADER([gssapi/gssapi_krb5.h])]) + AC_CHECK_DECL([GSS_C_NT_USER_NAME], + [AC_DEFINE([HAVE_GSS_RFC_OIDS], 1, + [Define to 1 if the GSS-API library uses RFC-compliant OIDs.])], [], + [RRA_INCLUDES_GSSAPI])]) + dnl The main macro. AC_DEFUN([RRA_LIB_GSSAPI], [AC_REQUIRE([RRA_ENABLE_REDUCED_DEPENDS]) @@ -148,24 +208,12 @@ AC_DEFUN([RRA_LIB_GSSAPI], AS_IF([test x"$rra_reduced_depends" = xtrue], [_RRA_LIB_GSSAPI_PATHS _RRA_LIB_GSSAPI_REDUCED], - [AC_ARG_VAR([KRB5_CONFIG], [Path to krb5-config]) - AS_IF([test x"$rra_gssapi_root" != x && test -z "$KRB5_CONFIG"], - [AS_IF([test -x "${rra_gssapi_root}/bin/krb5-config"], - [KRB5_CONFIG="${rra_gssapi_root}/bin/krb5-config"])], - [AC_PATH_PROG([KRB5_CONFIG], [krb5-config])]) - AS_IF([test x"$KRB5_CONFIG" != x && test -x "$KRB5_CONFIG"], - [AC_CACHE_CHECK([for gssapi support in krb5-config], - [rra_cv_lib_gssapi_config], - [AS_IF(["$KRB5_CONFIG" 2>&1 | grep gssapi >/dev/null 2>&1], - [rra_cv_lib_gssapi_config=yes], - [rra_cv_lib_gssapi_config=no])]) - AS_IF([test "$rra_cv_lib_gssapi_config" = yes], - [GSSAPI_CPPFLAGS=`"$KRB5_CONFIG" --cflags gssapi 2>/dev/null` - GSSAPI_LIBS=`"$KRB5_CONFIG" --libs gssapi 2>/dev/null`], - [GSSAPI_CPPFLAGS=`"$KRB5_CONFIG" --cflags 2>/dev/null` - GSSAPI_LIBS=`"$KRB5_CONFIG" --libs 2>/dev/null`]) - GSSAPI_CPPFLAGS=`echo "$GSSAPI_CPPFLAGS" \ - | sed 's%-I/usr/include ?%%'` - _RRA_LIB_GSSAPI_CHECK], - [_RRA_LIB_GSSAPI_PATHS - _RRA_LIB_GSSAPI_MANUAL])])]) + [AS_IF([test x"$rra_gssapi_includedir" = x \ + && test x"$rra_gssapi_libdir" = x], + [_RRA_LIB_GSSAPI_CONFIG], + [_RRA_LIB_GSSAPI_PATHS + _RRA_LIB_GSSAPI_MANUAL])]) + + RRA_LIB_GSSAPI_SWITCH + _RRA_LIB_GSSAPI_EXTRA + RRA_LIB_GSSAPI_RESTORE]) diff --git a/m4/krb5-config.m4 b/m4/krb5-config.m4 new file mode 100644 index 0000000..d73085f --- /dev/null +++ b/m4/krb5-config.m4 @@ -0,0 +1,101 @@ +dnl Use krb5-config to get link paths for Kerberos libraries. +dnl +dnl Provides one macro, RRA_KRB5_CONFIG, which attempts to get compiler and +dnl linker flags for a library via krb5-config and sets the appropriate shell +dnl variables. Defines the Autoconf variable PATH_KRB5_CONFIG, which can be +dnl used to find the default path to krb5-config. +dnl +dnl Depends on RRA_ENABLE_REDUCED_DEPENDS. +dnl +dnl The canonical version of this file is maintained in the rra-c-util +dnl package, available at . +dnl +dnl Written by Russ Allbery +dnl Copyright 2011, 2012 +dnl The Board of Trustees of the Leland Stanford Junior University +dnl +dnl This file is free software; the authors give unlimited permission to copy +dnl and/or distribute it, with or without modifications, as long as this +dnl notice is preserved. + +dnl Check for krb5-config in the user's path and set PATH_KRB5_CONFIG. This +dnl is moved into a separate macro so that it can be loaded via AC_REQUIRE, +dnl meaning it will only be run once even if we link with multiple krb5-config +dnl libraries. +AC_DEFUN([_RRA_KRB5_CONFIG_PATH], +[AC_ARG_VAR([PATH_KRB5_CONFIG], [Path to krb5-config]) + AC_PATH_PROG([PATH_KRB5_CONFIG], [krb5-config], [], + [${PATH}:/usr/kerberos/bin])]) + +dnl Check whether the --deps flag is supported by krb5-config. Takes the path +dnl to krb5-config to use. Note that this path is not embedded in the cache +dnl variable, so this macro implicitly assumes that we will always use the +dnl same krb5-config program. +AC_DEFUN([_RRA_KRB5_CONFIG_DEPS], +[AC_REQUIRE([_RRA_KRB5_CONFIG_PATH]) + AC_CACHE_CHECK([for --deps support in krb5-config], + [rra_cv_krb5_config_deps], + [AS_IF(["$1" 2>&1 | grep deps >/dev/null 2>&1], + [rra_cv_krb5_config_deps=yes], + [rra_cv_krb5_config_deps=no])])]) + +dnl Obtain the library flags for a particular library using krb5-config. +dnl Takes the path to the krb5-config program to use, the argument to +dnl krb5-config to use, and the variable prefix under which to store the +dnl library flags. +AC_DEFUN([_RRA_KRB5_CONFIG_LIBS], +[AC_REQUIRE([_RRA_KRB5_CONFIG_PATH]) + AC_REQUIRE([RRA_ENABLE_REDUCED_DEPENDS]) + _RRA_KRB5_CONFIG_DEPS([$1]) + AS_IF([test x"$rra_reduced_depends" = xfalse \ + && test x"$rra_cv_krb5_config_deps" = xyes], + [$3[]_LIBS=`"$1" --deps --libs $2 2>/dev/null`], + [$3[]_LIBS=`"$1" --libs $2 2>/dev/null`])]) + +dnl Attempt to find the flags for a library using krb5-config. Takes the +dnl following arguments (in order): +dnl +dnl 1. The root directory for the library in question, generally from an +dnl Autoconf --with flag. Used by preference as the path to krb5-config. +dnl +dnl 2. The argument to krb5-config to retrieve flags for this particular +dnl library. +dnl +dnl 3. The variable prefix to use when setting CPPFLAGS and LIBS variables +dnl based on the result of krb5-config. +dnl +dnl 4. Further actions to take if krb5-config was found and supported that +dnl library type. +dnl +dnl 5. Further actions to take if krb5-config could not be used to get flags +dnl for that library type. +dnl +dnl Special-case a krb5-config argument of krb5 and run krb5-config without an +dnl argument if that option was requested and not supported. Old versions of +dnl krb5-config didn't take an argument to specify the library type, but +dnl always returned the flags for libkrb5. +AC_DEFUN([RRA_KRB5_CONFIG], +[AC_REQUIRE([_RRA_KRB5_CONFIG_PATH]) + rra_krb5_config_$3= + rra_krb5_config_$3[]_ok= + AS_IF([test x"$1" != x && test -x "$1/bin/krb5-config"], + [rra_krb5_config_$3="$1/bin/krb5-config"], + [rra_krb5_config_$3="$PATH_KRB5_CONFIG"]) + AS_IF([test x"$rra_krb5_config_$3" != x && test -x "$rra_krb5_config_$3"], + [AC_CACHE_CHECK([for $2 support in krb5-config], [rra_cv_lib_$3[]_config], + [AS_IF(["$rra_krb5_config_$3" 2>&1 | grep $2 >/dev/null 2>&1], + [rra_cv_lib_$3[]_config=yes], + [rra_cv_lib_$3[]_config=no])]) + AS_IF([test "$rra_cv_lib_$3[]_config" = yes], + [$3[]_CPPFLAGS=`"$rra_krb5_config_$3" --cflags $2 2>/dev/null` + _RRA_KRB5_CONFIG_LIBS([$rra_krb5_config_$3], [$2], [$3]) + rra_krb5_config_$3[]_ok=yes], + [AS_IF([test x"$2" = xkrb5], + [$3[]_CPPFLAGS=`"$rra_krb5_config_$3" --cflags 2>/dev/null` + $3[]_LIBS=`"$rra_krb5_config_$3" --libs $2 2>/dev/null` + rra_krb5_config_$3[]_ok=yes])])]) + AS_IF([test x"$rra_krb5_config_$3[]_ok" = xyes], + [$3[]_CPPFLAGS=`echo "$$3[]_CPPFLAGS" | sed 's%-I/usr/include %%'` + $3[]_CPPFLAGS=`echo "$$3[]_CPPFLAGS" | sed 's%-I/usr/include$%%'` + $4], + [$5])]) diff --git a/m4/krb5.m4 b/m4/krb5.m4 index 38a050e..964023a 100644 --- a/m4/krb5.m4 +++ b/m4/krb5.m4 @@ -1,17 +1,18 @@ -dnl Find the compiler and linker flags for Kerberos v5. +dnl Find the compiler and linker flags for Kerberos. 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-standard paths to the -dnl Kerberos libraries. Uses krb5-config where available unless reduced -dnl dependencies is requested. +dnl Finds the compiler and linker flags for linking with Kerberos libraries. +dnl Provides the --with-krb5, --with-krb5-include, and --with-krb5-lib +dnl configure options to specify non-standard paths to the Kerberos libraries. +dnl Uses krb5-config where available unless reduced dependencies is requested +dnl or --with-krb5-include or --with-krb5-lib are given. dnl dnl Provides the macro RRA_LIB_KRB5 and sets the substitution variables dnl KRB5_CPPFLAGS, KRB5_LDFLAGS, and KRB5_LIBS. Also provides dnl RRA_LIB_KRB5_SWITCH to set CPPFLAGS, LDFLAGS, and LIBS to include the 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 RRA_LIB_KRB5_SWITCH. HAVE_KERBEROS will always be defined if RRA_LIB_KRB5 +dnl is used. 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. @@ -32,14 +33,31 @@ 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 Also provides RRA_INCLUDES_KRB5, which are the headers to include when +dnl probing the Kerberos library properties. +dnl +dnl The canonical version of this file is maintained in the rra-c-util +dnl package, available at . +dnl dnl Written by Russ Allbery -dnl Copyright 2005, 2006, 2007, 2008, 2009, 2010 -dnl Board of Trustees, Leland Stanford Jr. University +dnl Copyright 2005, 2006, 2007, 2008, 2009, 2010, 2011 +dnl The Board of Trustees of the Leland Stanford Junior University dnl -dnl See LICENSE for licensing terms. +dnl This file is free software; the authors give unlimited permission to copy +dnl and/or distribute it, with or without modifications, as long as this +dnl notice is preserved. + +dnl Headers to include when probing for Kerberos library properties. +AC_DEFUN([RRA_INCLUDES_KRB5], [[ +#if HAVE_KRB5_H +# include +#else +# include +#endif +]]) 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 Kerberos flags. Used as a wrapper, with dnl RRA_LIB_KRB5_RESTORE, around tests. AC_DEFUN([RRA_LIB_KRB5_SWITCH], [rra_krb5_save_CPPFLAGS="$CPPFLAGS" @@ -69,44 +87,62 @@ AC_DEFUN([_RRA_LIB_KRB5_PATHS], [AS_IF([test x"$rra_krb5_root" != x/usr], [KRB5_CPPFLAGS="-I${rra_krb5_root}/include"])])])]) -dnl Does the appropriate library checks for reduced-dependency Kerberos v5 +dnl Check for a header using a file existence check rather than using +dnl AC_CHECK_HEADERS. This is used if there were arguments to configure +dnl specifying the Kerberos header path, since we may have one header in the +dnl default include path and another under our explicitly-configured Kerberos +dnl location. +AC_DEFUN([_RRA_LIB_KRB5_CHECK_HEADER], +[AC_MSG_CHECKING([for $1]) + AS_IF([test -f "${rra_krb5_incroot}/$1"], + [AC_DEFINE_UNQUOTED(AS_TR_CPP([HAVE_$1]), [1], + [Define to 1 if you have the <$1> header file.]) + AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no])])]) + +dnl Does the appropriate library checks for reduced-dependency Kerberos dnl linkage. The single argument, if true, says to fail if Kerberos could not dnl be found. AC_DEFUN([_RRA_LIB_KRB5_REDUCED], [RRA_LIB_KRB5_SWITCH AC_CHECK_LIB([krb5], [krb5_init_context], [KRB5_LIBS="-lkrb5"], [AS_IF([test x"$1" = xtrue], - [AC_MSG_ERROR([cannot find usable Kerberos v5 library])])]) + [AC_MSG_ERROR([cannot find usable Kerberos library])])]) LIBS="$KRB5_LIBS $LIBS" + AS_IF([test x"$rra_krb5_incroot" = x], + [AC_CHECK_HEADERS([krb5.h krb5/krb5.h])], + [_RRA_LIB_KRB5_CHECK_HEADER([krb5.h]) + _RRA_LIB_KRB5_CHECK_HEADER([krb5/krb5.h])]) AC_CHECK_FUNCS([krb5_get_error_message], [AC_CHECK_FUNCS([krb5_free_error_message])], - [AC_CHECK_FUNCS([krb5_get_error_string], , - [AC_CHECK_FUNCS([krb5_get_err_txt], , + [AC_CHECK_FUNCS([krb5_get_error_string], [], + [AC_CHECK_FUNCS([krb5_get_err_txt], [], [AC_CHECK_LIB([ksvc], [krb5_svc_get_msg], [KRB5_LIBS="$KRB5_LIBS -lksvc" AC_DEFINE([HAVE_KRB5_SVC_GET_MSG], [1]) - AC_CHECK_HEADERS([ibm_svc/krb5_svc.h])], + AC_CHECK_HEADERS([ibm_svc/krb5_svc.h], [], [], + [RRA_INCLUDES_KRB5])], [AC_CHECK_LIB([com_err], [com_err], [KRB5_LIBS="$KRB5_LIBS -lcom_err"], [AC_MSG_ERROR([cannot find usable com_err library])]) AC_CHECK_HEADERS([et/com_err.h])])])])]) RRA_LIB_KRB5_RESTORE]) -dnl Does the appropriate library checks for Kerberos v5 linkage when we don't +dnl Does the appropriate library checks for Kerberos linkage when we don't dnl have krb5-config or reduced dependencies. The single argument, if true, dnl says to fail if Kerberos could not be found. AC_DEFUN([_RRA_LIB_KRB5_MANUAL], [RRA_LIB_KRB5_SWITCH rra_krb5_extra= LIBS= - AC_SEARCH_LIBS([res_search], [resolv], , + AC_SEARCH_LIBS([res_search], [resolv], [], [AC_SEARCH_LIBS([__res_search], [resolv])]) AC_SEARCH_LIBS([gethostbyname], [nsl]) - AC_SEARCH_LIBS([socket], [socket], , - [AC_CHECK_LIB([nsl], [socket], [LIBS="-lnsl -lsocket $LIBS"], , + AC_SEARCH_LIBS([socket], [socket], [], + [AC_CHECK_LIB([nsl], [socket], [LIBS="-lnsl -lsocket $LIBS"], [], [-lsocket])]) AC_SEARCH_LIBS([crypt], [crypt]) - AC_SEARCH_LIBS([rk_simple_execve], [roken]) + AC_SEARCH_LIBS([roken_concat], [roken]) rra_krb5_extra="$LIBS" LIBS="$rra_krb5_save_LIBS" AC_CHECK_LIB([krb5], [krb5_init_context], @@ -119,28 +155,34 @@ AC_DEFUN([_RRA_LIB_KRB5_MANUAL], [rra_krb5_pthread="-lpthread"])]) AC_CHECK_LIB([krb5support], [krb5int_setspecific], [rra_krb5_extra="-lkrb5support $rra_krb5_extra $rra_krb5_pthread"], - , [$rra_krb5_pthread])]) + [], [$rra_krb5_pthread $rra_krb5_extra])], + [$rra_krb5_extra]) AC_CHECK_LIB([com_err], [error_message], - [rra_krb5_extra="-lcom_err $rra_krb5_extra"]) + [rra_krb5_extra="-lcom_err $rra_krb5_extra"], [], [$rra_krb5_extra]) AC_CHECK_LIB([ksvc], [krb5_svc_get_msg], - [rra_krb5_extra="-lksvc $rra_krb5_extra"]) + [rra_krb5_extra="-lksvc $rra_krb5_extra"], [], [$rra_krb5_extra]) AC_CHECK_LIB([k5crypto], [krb5int_hash_md5], - [rra_krb5_extra="-lk5crypto $rra_krb5_extra"]) + [rra_krb5_extra="-lk5crypto $rra_krb5_extra"], [], [$rra_krb5_extra]) AC_CHECK_LIB([k5profile], [profile_get_values], - [rra_krb5_extra="-lk5profile $rra_krb5_extra"]) + [rra_krb5_extra="-lk5profile $rra_krb5_extra"], [], [$rra_krb5_extra]) AC_CHECK_LIB([krb5], [krb5_cc_default], [KRB5_LIBS="-lkrb5 $rra_krb5_extra"], [AS_IF([test x"$1" = xtrue], - [AC_MSG_ERROR([cannot find usable Kerberos v5 library])])], + [AC_MSG_ERROR([cannot find usable Kerberos library])])], [$rra_krb5_extra])], [-lasn1 -lcom_err -lcrypto $rra_krb5_extra]) LIBS="$KRB5_LIBS $LIBS" + AS_IF([test x"$rra_krb5_incroot" = x], + [AC_CHECK_HEADERS([krb5.h krb5/krb5.h])], + [_RRA_LIB_KRB5_CHECK_HEADER([krb5.h]) + _RRA_LIB_KRB5_CHECK_HEADER([krb5/krb5.h])]) AC_CHECK_FUNCS([krb5_get_error_message], [AC_CHECK_FUNCS([krb5_free_error_message])], - [AC_CHECK_FUNCS([krb5_get_error_string], , - [AC_CHECK_FUNCS([krb5_get_err_txt], , + [AC_CHECK_FUNCS([krb5_get_error_string], [], + [AC_CHECK_FUNCS([krb5_get_err_txt], [], [AC_CHECK_FUNCS([krb5_svc_get_msg], - [AC_CHECK_HEADERS([ibm_svc/krb5_svc.h])], + [AC_CHECK_HEADERS([ibm_svc/krb5_svc.h], [], [], + [RRA_INCLUDES_KRB5])], [AC_CHECK_HEADERS([et/com_err.h])])])])]) RRA_LIB_KRB5_RESTORE]) @@ -158,49 +200,49 @@ AC_DEFUN([_RRA_LIB_KRB5_CHECK], _RRA_LIB_KRB5_PATHS _RRA_LIB_KRB5_MANUAL([$1])])]) +dnl Determine Kerberos compiler and linker flags from krb5-config. Does the +dnl additional probing we need to do to uncover error handling features, and +dnl falls back on the manual checks. +AC_DEFUN([_RRA_LIB_KRB5_CONFIG], +[RRA_KRB5_CONFIG([${rra_krb5_root}], [krb5], [KRB5], + [_RRA_LIB_KRB5_CHECK([$1]) + RRA_LIB_KRB5_SWITCH + AS_IF([test x"$rra_krb5_incroot" = x], + [AC_CHECK_HEADERS([krb5.h krb5/krb5.h])], + [_RRA_LIB_KRB5_CHECK_HEADER([krb5.h]) + _RRA_LIB_KRB5_CHECK_HEADER([krb5/krb5.h])]) + AC_CHECK_FUNCS([krb5_get_error_message], + [AC_CHECK_FUNCS([krb5_free_error_message])], + [AC_CHECK_FUNCS([krb5_get_error_string], [], + [AC_CHECK_FUNCS([krb5_get_err_txt], [], + [AC_CHECK_FUNCS([krb5_svc_get_msg], + [AC_CHECK_HEADERS([ibm_svc/krb5_svc.h], [], [], + [RRA_INCLUDES_KRB5])], + [AC_CHECK_HEADERS([et/com_err.h])])])])]) + RRA_LIB_KRB5_RESTORE], + [_RRA_LIB_KRB5_PATHS + _RRA_LIB_KRB5_MANUAL([$1])])]) + dnl The core of the library checking, shared between RRA_LIB_KRB5 and dnl RRA_LIB_KRB5_OPTIONAL. The single argument, if "true", says to fail if -dnl Kerberos could not be found. +dnl Kerberos could not be found. Set up rra_krb5_incroot for later header +dnl checking. AC_DEFUN([_RRA_LIB_KRB5_INTERNAL], [AC_REQUIRE([RRA_ENABLE_REDUCED_DEPENDS]) + rra_krb5_incroot= + AS_IF([test x"$rra_krb5_includedir" != x], + [rra_krb5_incroot="$rra_krb5_includedir"], + [AS_IF([test x"$rra_krb5_root" != x], + [rra_krb5_incroot="${rra_krb5_root}/include"])]) AS_IF([test x"$rra_reduced_depends" = xtrue], [_RRA_LIB_KRB5_PATHS _RRA_LIB_KRB5_REDUCED([$1])], - [AC_ARG_VAR([KRB5_CONFIG], [Path to krb5-config]) - AS_IF([test x"$rra_krb5_root" != x && test -z "$KRB5_CONFIG"], - [AS_IF([test -x "${rra_krb5_root}/bin/krb5-config"], - [KRB5_CONFIG="${rra_krb5_root}/bin/krb5-config"])], - [AC_PATH_PROG([KRB5_CONFIG], [krb5-config])]) - AS_IF([test x"$KRB5_CONFIG" != x && test -x "$KRB5_CONFIG"], - [AC_CACHE_CHECK([for krb5 support in krb5-config], - [rra_cv_lib_krb5_config], - [AS_IF(["$KRB5_CONFIG" 2>&1 | grep krb5 >/dev/null 2>&1], - [rra_cv_lib_krb5_config=yes], - [rra_cv_lib_krb5_config=no])]) - AS_IF([test x"$rra_cv_lib_krb5_config" = xyes], - [KRB5_CPPFLAGS=`"$KRB5_CONFIG" --cflags krb5 2>/dev/null` - KRB5_LIBS=`"$KRB5_CONFIG" --libs krb5 2>/dev/null`], - [KRB5_CPPFLAGS=`"$KRB5_CONFIG" --cflags 2>/dev/null` - KRB5_LIBS=`"$KRB5_CONFIG" --libs 2>/dev/null`]) - KRB5_CPPFLAGS=`echo "$KRB5_CPPFLAGS" | sed 's%-I/usr/include ?%%'` - _RRA_LIB_KRB5_CHECK([$1]) - RRA_LIB_KRB5_SWITCH - AC_CHECK_FUNCS([krb5_get_error_message], - [AC_CHECK_FUNCS([krb5_free_error_message])], - [AC_CHECK_FUNCS([krb5_get_error_string], , - [AC_CHECK_FUNCS([krb5_get_err_txt], , - [AC_CHECK_FUNCS([krb5_svc_get_msg], - [AC_CHECK_HEADERS([ibm_svc/krb5_svc.h])], - [AC_CHECK_HEADERS([et/com_err.h])])])])]) - RRA_LIB_KRB5_RESTORE], - [_RRA_LIB_KRB5_PATHS - _RRA_LIB_KRB5_MANUAL([$1])])]) + [AS_IF([test x"$rra_krb5_includedir" = x && test x"$rra_krb5_libdir" = x], + [_RRA_LIB_KRB5_CONFIG([$1])], + [_RRA_LIB_KRB5_PATHS + _RRA_LIB_KRB5_MANUAL([$1])])]) rra_krb5_uses_com_err=false - case "$LIBS" in - *-lcom_err*) - rra_krb5_uses_com_err=true - ;; - esac + AS_CASE([$LIBS], [*-lcom_err*], [rra_krb5_uses_com_err=true]) AM_CONDITIONAL([KRB5_USES_COM_ERR], [test x"$rra_krb5_uses_com_err" = xtrue])]) dnl The main macro for packages with mandatory Kerberos support. @@ -208,26 +250,28 @@ AC_DEFUN([RRA_LIB_KRB5], [rra_krb5_root= rra_krb5_libdir= rra_krb5_includedir= + rra_use_kerberos=true AC_SUBST([KRB5_CPPFLAGS]) AC_SUBST([KRB5_LDFLAGS]) AC_SUBST([KRB5_LIBS]) AC_ARG_WITH([krb5], [AS_HELP_STRING([--with-krb5=DIR], - [Location of Kerberos v5 headers and libraries])], + [Location of Kerberos headers and libraries])], [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], [rra_krb5_root="$withval"])]) AC_ARG_WITH([krb5-include], [AS_HELP_STRING([--with-krb5-include=DIR], - [Location of Kerberos v5 headers])], + [Location of Kerberos headers])], [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], [rra_krb5_includedir="$withval"])]) AC_ARG_WITH([krb5-lib], [AS_HELP_STRING([--with-krb5-lib=DIR], - [Location of Kerberos v5 libraries])], + [Location of Kerberos libraries])], [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], [rra_krb5_libdir="$withval"])]) - _RRA_LIB_KRB5_INTERNAL([true])]) + _RRA_LIB_KRB5_INTERNAL([true]) + AC_DEFINE([HAVE_KERBEROS], 1, [Define to enable Kerberos features.])]) dnl The main macro for packages with optional Kerberos support. AC_DEFUN([RRA_LIB_KRB5_OPTIONAL], @@ -241,29 +285,41 @@ AC_DEFUN([RRA_LIB_KRB5_OPTIONAL], AC_ARG_WITH([krb5], [AS_HELP_STRING([--with-krb5@<:@=DIR@:>@], - [Location of Kerberos v5 headers and libraries])], + [Location of Kerberos headers and libraries])], [AS_IF([test x"$withval" = xno], [rra_use_kerberos=false], [AS_IF([test x"$withval" != xyes], [rra_krb5_root="$withval"]) rra_use_kerberos=true])]) AC_ARG_WITH([krb5-include], [AS_HELP_STRING([--with-krb5-include=DIR], - [Location of Kerberos v5 headers])], + [Location of Kerberos headers])], [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], [rra_krb5_includedir="$withval"])]) AC_ARG_WITH([krb5-lib], [AS_HELP_STRING([--with-krb5-lib=DIR], - [Location of Kerberos v5 libraries])], + [Location of Kerberos libraries])], [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], [rra_krb5_libdir="$withval"])]) AS_IF([test x"$rra_use_kerberos" != xfalse], [AS_IF([test x"$rra_use_kerberos" = xtrue], [_RRA_LIB_KRB5_INTERNAL([true])], - [_RRA_LIB_KRB5_INTERNAL([false])])]) + [_RRA_LIB_KRB5_INTERNAL([false])])], + [AM_CONDITIONAL([KRB5_USES_COM_ERR], [false])]) AS_IF([test x"$KRB5_LIBS" != x], [AC_DEFINE([HAVE_KERBEROS], 1, [Define to enable Kerberos features.])])]) +dnl Source used by RRA_FUNC_KRB5_GET_INIT_CREDS_OPT_FREE_ARGS. +AC_DEFUN([_RRA_FUNC_KRB5_OPT_FREE_ARGS_SOURCE], [RRA_INCLUDES_KRB5] [[ +int +main(void) +{ + krb5_get_init_creds_opt *opts; + krb5_context c; + krb5_get_init_creds_opt_free(c, opts); +} +]]) + 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. @@ -272,9 +328,7 @@ 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_get_init_creds_opt *opts; krb5_context c; - krb5_get_init_creds_opt_free(c, opts);], + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_RRA_FUNC_KRB5_OPT_FREE_ARGS_SOURCE])], [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], diff --git a/m4/lib-depends.m4 b/m4/lib-depends.m4 index 039e245..b5185f3 100644 --- a/m4/lib-depends.m4 +++ b/m4/lib-depends.m4 @@ -9,11 +9,16 @@ dnl dnl This macro doesn't do much but is defined separately so that other macros dnl can require it with AC_REQUIRE. dnl +dnl The canonical version of this file is maintained in the rra-c-util +dnl package, available at . +dnl dnl Written by Russ Allbery dnl Copyright 2005, 2006, 2007 -dnl Board of Trustees, Leland Stanford Jr. University +dnl The Board of Trustees of the Leland Stanford Junior University dnl -dnl See LICENSE for licensing terms. +dnl This file is free software; the authors give unlimited permission to copy +dnl and/or distribute it, with or without modifications, as long as this +dnl notice is preserved. AC_DEFUN([RRA_ENABLE_REDUCED_DEPENDS], [rra_reduced_depends=false diff --git a/m4/lib-pathname.m4 b/m4/lib-pathname.m4 index fc326a0..fd5a5a1 100644 --- a/m4/lib-pathname.m4 +++ b/m4/lib-pathname.m4 @@ -12,10 +12,16 @@ dnl dnl This file also provides the Autoconf macro RRA_SET_LIBDIR, which sets the dnl libdir variable to PREFIX/lib{,32,64} as appropriate. dnl +dnl The canonical version of this file is maintained in the rra-c-util +dnl package, available at . +dnl dnl Written by Russ Allbery -dnl Copyright 2008, 2009 Board of Trustees, Leland Stanford Jr. University +dnl Copyright 2008, 2009 +dnl The Board of Trustees of the Leland Stanford Junior University dnl -dnl See LICENSE for licensing terms. +dnl This file is free software; the authors give unlimited permission to copy +dnl and/or distribute it, with or without modifications, as long as this +dnl notice is preserved. dnl Probe for the alternate library name that we should attempt on this dnl architecture, given the size of an int, and set rra_lib_arch_name to that diff --git a/m4/remctl.m4 b/m4/remctl.m4 index bb3a56f..588404f 100644 --- a/m4/remctl.m4 +++ b/m4/remctl.m4 @@ -10,15 +10,28 @@ dnl REMCTL_CPPFLAGS, REMCTL_LDFLAGS, and REMCTL_LIBS. Also provides dnl RRA_LIB_REMCTL_SWITCH to set CPPFLAGS, LDFLAGS, and LIBS to include the dnl remctl libraries, saving the current values first, and dnl RRA_LIB_REMCTL_RESTORE to restore those settings to before the last -dnl RRA_LIB_REMCTL_SWITCH. +dnl RRA_LIB_REMCTL_SWITCH. HAVE_REMCTL will always be defined if +dnl RRA_LIB_REMCTL is used. +dnl +dnl Provides the RRA_LIB_REMCTL_OPTIONAL macro, which should be used if +dnl Kerberos support is optional. This macro will still always est the +dnl substitution variables, but they'll be empty unless --with-remctl is +dnl given. HAVE_REMCTL will be defined if --with-remctl is given and +dnl $rra_use_remctl will be set to "true". dnl dnl Depends on RRA_ENABLE_REDUCED_DEPENDS, RRA_SET_LDFLAGS, and dnl RRA_LIB_GSSAPI. dnl +dnl The canonical version of this file is maintained in the rra-c-util +dnl package, available at . +dnl dnl Written by Russ Allbery -dnl Copyright 2008, 2009 Board of Trustees, Leland Stanford Jr. University +dnl Copyright 2008, 2009, 2011 +dnl The Board of Trustees of the Leland Stanford Junior University dnl -dnl See LICENSE for licensing terms. +dnl This file is free software; the authors give unlimited permission to copy +dnl and/or distribute it, with or without modifications, as long as this +dnl notice is preserved. dnl Save the current CPPFLAGS, LDFLAGS, and LIBS settings and switch to dnl versions that include the remctl flags. Used as a wrapper, with @@ -55,16 +68,34 @@ dnl Sanity-check the results of the remctl library search to be sure we can dnl really link a remctl program. AC_DEFUN([_RRA_LIB_REMCTL_CHECK], [RRA_LIB_REMCTL_SWITCH - AC_CHECK_FUNC([remctl_open], , - [AC_MSG_FAILURE([unable to link with remctl library])]) + AC_CHECK_FUNC([remctl_open], [], + [AS_IF([test x"$1" = xtrue], + [AC_MSG_FAILURE([unable to link with remctl library])]) + REMCTL_CPPFLAGS= + REMCTL_LDFLAGS= + REMCTL_LIBS=]) RRA_LIB_REMCTL_RESTORE]) -dnl The main macro. -AC_DEFUN([RRA_LIB_REMCTL], +dnl The core of the library checking, shared between RRA_LIB_REMCTL and +dnl RRA_LIB_REMCTL_OPTIONAL. The single argument, if "true", says to fail if +dnl remctl could not be found. +AC_DEFUN([_RRA_LIB_REMCTL_INTERNAL], [AC_REQUIRE([RRA_ENABLE_REDUCED_DEPENDS]) - rra_remctl_root= + _RRA_LIB_REMCTL_PATHS + AS_IF([test x"$rra_reduced_depends" = xtrue], + [REMCTL_LIBS="-lremctl"], + [RRA_LIB_GSSAPI + REMCTL_CPPFLAGS="$REMCTL_CPPFLAGS $GSSAPI_CPPFLAGS" + REMCTL_LDFLAGS="$REMCTL_LDFLAGS $GSSAPI_LDFLAGS" + REMCTL_LIBS="-lremctl $GSSAPI_LIBS"]) + _RRA_LIB_REMCTL_CHECK([$1])]) + +dnl The main macro for packages with mandatory remctl support. +AC_DEFUN([RRA_LIB_REMCTL], +[rra_remctl_root= rra_remctl_libdir= rra_remctl_includedir= + rra_use_remctl=true REMCTL_CPPFLAGS= REMCTL_LDFLAGS= REMCTL_LIBS= @@ -87,12 +118,43 @@ AC_DEFUN([RRA_LIB_REMCTL], [Location of remctl libraries])], [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], [rra_remctl_libdir="$withval"])]) + _RRA_LIB_REMCTL_INTERNAL([true]) + AC_DEFINE([HAVE_REMCTL], 1, [Define to enable remctl features.])]) - _RRA_LIB_REMCTL_PATHS - AS_IF([test x"$rra_reduced_depends" = xtrue], - [REMCTL_LIBS="-lremctl"], - [RRA_LIB_GSSAPI - REMCTL_CPPFLAGS="$REMCTL_CPPFLAGS $GSSAPI_CPPFLAGS" - REMCTL_LDFLAGS="$REMCTL_LDFLAGS $GSSAPI_LDFLAGS" - REMCTL_LIBS="-lremctl $GSSAPI_LIBS"]) - _RRA_LIB_REMCTL_CHECK]) +dnl The main macro for packages with optional remctl support. +AC_DEFUN([RRA_LIB_REMCTL_OPTIONAL], +[rra_remctl_root= + rra_remctl_libdir= + rra_remctl_includedir= + rra_use_remctl= + REMCTL_CPPFLAGS= + REMCTL_LDFLAGS= + REMCTL_LIBS= + AC_SUBST([REMCTL_CPPFLAGS]) + AC_SUBST([REMCTL_LDFLAGS]) + AC_SUBST([REMCTL_LIBS]) + + AC_ARG_WITH([remctl], + [AS_HELP_STRING([--with-remctl@<:@=DIR@:>@], + [Location of remctl headers and libraries])], + [AS_IF([test x"$withval" = xno], + [rra_use_remctl=false], + [AS_IF([test x"$withval" != xyes], [rra_remctl_root="$withval"]) + rra_use_remctl=true])]) + AC_ARG_WITH([remctl-include], + [AS_HELP_STRING([--with-remctl-include=DIR], + [Location of remctl headers])], + [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], + [rra_remctl_includedir="$withval"])]) + AC_ARG_WITH([remctl-lib], + [AS_HELP_STRING([--with-remctl-lib=DIR], + [Location of remctl libraries])], + [AS_IF([test x"$withval" != xyes && test x"$withval" != xno], + [rra_remctl_libdir="$withval"])]) + AS_IF([test x"$rra_use_remctl" != xfalse], + [AS_IF([test x"$rra_use_remctl" = xtrue], + [_RRA_LIB_REMCTL_INTERNAL([true])], + [_RRA_LIB_REMCTL_INTERNAL([false])])]) + AS_IF([test x"$REMCTL_LIBS" != x], + [rra_use_remctl=true + AC_DEFINE([HAVE_REMCTL], 1, [Define to enable remctl features.])])]) diff --git a/m4/snprintf.m4 b/m4/snprintf.m4 index d933f55..cd585ef 100644 --- a/m4/snprintf.m4 +++ b/m4/snprintf.m4 @@ -9,11 +9,16 @@ dnl dnl Provides RRA_FUNC_SNPRINTF, which adds snprintf.o to LIBOBJS unless a dnl fully working snprintf is found. dnl +dnl The canonical version of this file is maintained in the rra-c-util +dnl package, available at . +dnl dnl Written by Russ Allbery dnl Copyright 2006, 2008, 2009 -dnl Board of Trustees, Leland Stanford Jr. University +dnl The Board of Trustees of the Leland Stanford Junior University dnl -dnl See LICENSE for licensing terms. +dnl This file is free software; the authors give unlimited permission to copy +dnl and/or distribute it, with or without modifications, as long as this +dnl notice is preserved. dnl Source used by RRA_FUNC_SNPRINTF. AC_DEFUN([_RRA_FUNC_SNPRINTF_SOURCE], [[ diff --git a/m4/vamacros.m4 b/m4/vamacros.m4 index 855bb40..af98f6a 100644 --- a/m4/vamacros.m4 +++ b/m4/vamacros.m4 @@ -13,11 +13,16 @@ dnl #define macro(args...) fprintf(stderr, args) dnl dnl They set HAVE_C99_VAMACROS or HAVE_GNU_VAMACROS as appropriate. dnl +dnl The canonical version of this file is maintained in the rra-c-util +dnl package, available at . +dnl dnl Written by Russ Allbery dnl Copyright 2006, 2008, 2009 -dnl Board of Trustees, Leland Stanford Jr. University +dnl The Board of Trustees of the Leland Stanford Junior University dnl -dnl See LICENSE for licensing terms. +dnl This file is free software; the authors give unlimited permission to copy +dnl and/or distribute it, with or without modifications, as long as this +dnl notice is preserved. AC_DEFUN([_RRA_C_C99_VAMACROS_SOURCE], [[ #include diff --git a/portable/asprintf.c b/portable/asprintf.c index 4219a19..0093070 100644 --- a/portable/asprintf.c +++ b/portable/asprintf.c @@ -4,8 +4,18 @@ * Provides the same functionality as the standard GNU library routines * asprintf and vasprintf for those platforms that don't have them. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * This work is hereby placed in the public domain by its author. + * + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #include diff --git a/portable/dummy.c b/portable/dummy.c index 8a0d54d..50052ec 100644 --- a/portable/dummy.c +++ b/portable/dummy.c @@ -5,8 +5,18 @@ * supply, Automake builds an empty library and then calls ar with nonsensical * arguments. Ensure that libportable always contains at least one symbol. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * This work is hereby placed in the public domain by its author. + * + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ /* Prototype to avoid gcc warnings. */ diff --git a/portable/krb5-extra.c b/portable/krb5-extra.c index 89ccbde..849842c 100644 --- a/portable/krb5-extra.c +++ b/portable/krb5-extra.c @@ -6,8 +6,18 @@ * Everything in this file will be protected by #ifndef. If the native * Kerberos libraries are fully capable, this file will be skipped. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * This work is hereby placed in the public domain by its author. + * + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #include diff --git a/portable/krb5.h b/portable/krb5.h index 3b5700b..b418ae7 100644 --- a/portable/krb5.h +++ b/portable/krb5.h @@ -16,8 +16,18 @@ * prefers the generic krb5_xfree(). In this case, this header provides * krb5_free_unparsed_name() for both APIs since it's the most specific call. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * This work is hereby placed in the public domain by its author. + * + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #ifndef PORTABLE_KRB5_H @@ -32,7 +42,11 @@ #endif #include -#include +#ifdef HAVE_KRB5_H +# include +#else +# include +#endif #include BEGIN_DECLS @@ -75,7 +89,7 @@ krb5_error_code krb5_get_init_creds_opt_alloc(krb5_context, /* 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 */ +# define krb5_get_init_creds_opt_set_default_flags(c, p, r, o) /* empty */ #endif /* diff --git a/portable/macros.h b/portable/macros.h index 8d5adbd..b33d064 100644 --- a/portable/macros.h +++ b/portable/macros.h @@ -1,8 +1,18 @@ /* * Portability macros used in include files. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * This work is hereby placed in the public domain by its author. + * + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #ifndef PORTABLE_MACROS_H @@ -19,6 +29,29 @@ # endif #endif +/* + * We use __alloc_size__, but it was only available in fairly recent versions + * of GCC. Suppress warnings about the unknown attribute if GCC is too old. + * We know that we're GCC at this point, so we can use the GCC variadic macro + * extension, which will still work with versions of GCC too old to have C99 + * variadic macro support. + */ +#if !defined(__attribute__) && !defined(__alloc_size__) +# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3) +# define __alloc_size__(spec, args...) /* empty */ +# endif +#endif + +/* + * LLVM and Clang pretend to be GCC but don't support all of the __attribute__ + * settings that GCC does. For them, suppress warnings about unknown + * attributes on declarations. This unfortunately will affect the entire + * compilation context, but there's no push and pop available. + */ +#if !defined(__attribute__) && (defined(__llvm__) || defined(__clang__)) +# pragma GCC diagnostic ignored "-Wattributes" +#endif + /* * BEGIN_DECLS is used at the beginning of declarations so that C++ * compilers don't mangle their names. END_DECLS is used at the end. diff --git a/portable/mkstemp.c b/portable/mkstemp.c index dd2a485..8668db1 100644 --- a/portable/mkstemp.c +++ b/portable/mkstemp.c @@ -4,8 +4,18 @@ * Provides the same functionality as the library function mkstemp for those * systems that don't have it. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * This work is hereby placed in the public domain by its author. + * + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #include @@ -27,7 +37,7 @@ int test_mkstemp(char *); #endif /* Pick the longest available integer type. */ -#if HAVE_LONG_LONG +#if HAVE_LONG_LONG_INT typedef unsigned long long long_int_type; #else typedef unsigned long long_int_type; diff --git a/portable/setenv.c b/portable/setenv.c index d66ddcd..fd2b10c 100644 --- a/portable/setenv.c +++ b/portable/setenv.c @@ -4,8 +4,18 @@ * Provides the same functionality as the standard library routine setenv for * those platforms that don't have it. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * This work is hereby placed in the public domain by its author. + * + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #include @@ -31,10 +41,9 @@ setenv(const char *name, const char *value, int overwrite) /* * Allocate memory for the environment string. We intentionally don't use - * concat here, or the xmalloc family of allocation routines, since the - * intention is to provide a replacement for the standard library function - * which sets errno and returns in the event of a memory allocation - * failure. + * the xmalloc family of allocation routines here, since the intention is + * to provide a replacement for the standard library function that sets + * errno and returns in the event of a memory allocation failure. */ size = strlen(name) + 1 + strlen(value) + 1; envstring = malloc(size); diff --git a/portable/snprintf.c b/portable/snprintf.c index ab3121c..91c8491 100644 --- a/portable/snprintf.c +++ b/portable/snprintf.c @@ -8,6 +8,9 @@ * Please do not reformat or otherwise change this file more than necessary so * that later merges with the original source are easy. Bug fixes and * improvements should be sent back to the original author. + * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . */ /* diff --git a/portable/stdbool.h b/portable/stdbool.h index bfbf4c4..045436f 100644 --- a/portable/stdbool.h +++ b/portable/stdbool.h @@ -5,13 +5,31 @@ * following the C99 specification, on hosts that don't have stdbool.h. This * logic is based heavily on the example in the Autoconf manual. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * This work is hereby placed in the public domain by its author. + * + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #ifndef PORTABLE_STDBOOL_H #define PORTABLE_STDBOOL_H 1 +/* + * 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 +#endif + #if HAVE_STDBOOL_H # include #else diff --git a/portable/strlcat.c b/portable/strlcat.c index f696db3..3bee4ee 100644 --- a/portable/strlcat.c +++ b/portable/strlcat.c @@ -9,8 +9,18 @@ * space available in the destination buffer, not just the amount of space * remaining. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * This work is hereby placed in the public domain by its author. + * + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #include diff --git a/portable/strlcpy.c b/portable/strlcpy.c index 596e968..df75fd8 100644 --- a/portable/strlcpy.c +++ b/portable/strlcpy.c @@ -8,8 +8,18 @@ * total space required is returned. The destination string is not nul-filled * like strncpy does, just nul-terminated. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * This work is hereby placed in the public domain by its author. + * + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #include diff --git a/portable/system.h b/portable/system.h index 461601b..d1ccc94 100644 --- a/portable/system.h +++ b/portable/system.h @@ -13,13 +13,24 @@ * #include * #include * #include + * #include * #include * * Missing functions are provided via #define or prototyped if available from * the portable helper library. Also provides some standard #defines. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * This work is hereby placed in the public domain by its author. + * + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #ifndef PORTABLE_SYSTEM_H @@ -38,6 +49,9 @@ #include #include #include +#if HAVE_STRINGS_H +# include +#endif #if HAVE_INTTYPES_H # include #endif @@ -56,6 +70,38 @@ /* Get the bool type. */ #include +/* Windows provides snprintf under a different name. */ +#ifdef _WIN32 +# define snprintf _snprintf +#endif + +/* Windows does not define ssize_t. */ +#ifndef HAVE_SSIZE_T +typedef ptrdiff_t ssize_t; +#endif + +/* + * POSIX requires that these be defined in . If one of them has + * been defined, all the rest almost certainly have. + */ +#ifndef STDIN_FILENO +# define STDIN_FILENO 0 +# define STDOUT_FILENO 1 +# define STDERR_FILENO 2 +#endif + +/* + * C99 requires va_copy. Older versions of GCC provide __va_copy. Per the + * Autoconf manual, memcpy is a generally portable fallback. + */ +#ifndef va_copy +# ifdef __va_copy +# define va_copy(d, s) __va_copy((d), (s)) +# else +# define va_copy(d, s) memcpy(&(d), &(s), sizeof(va_list)) +# endif +#endif + BEGIN_DECLS /* Default to a hidden visibility for all portability functions. */ @@ -96,31 +142,4 @@ extern size_t strlcpy(char *, const char *, size_t); END_DECLS -/* Windows provides snprintf under a different name. */ -#ifdef _WIN32 -# define snprintf _snprintf -#endif - -/* - * POSIX requires that these be defined in . If one of them has - * been defined, all the rest almost certainly have. - */ -#ifndef STDIN_FILENO -# define STDIN_FILENO 0 -# define STDOUT_FILENO 1 -# define STDERR_FILENO 2 -#endif - -/* - * C99 requires va_copy. Older versions of GCC provide __va_copy. Per the - * Autoconf manual, memcpy is a generally portable fallback. - */ -#ifndef va_copy -# ifdef __va_copy -# define va_copy(d, s) __va_copy((d), (s)) -# else -# define va_copy(d, s) memcpy(&(d), &(s), sizeof(va_list)) -# endif -#endif - #endif /* !PORTABLE_SYSTEM_H */ diff --git a/portable/uio.h b/portable/uio.h index 3c9e840..3bd1f96 100644 --- a/portable/uio.h +++ b/portable/uio.h @@ -5,8 +5,18 @@ * (primarily Windows). Currently, the corresponding readv and writev * functions are not provided or prototyped here. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * This work is hereby placed in the public domain by its author. + * + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #ifndef PORTABLE_UIO_H diff --git a/tests/client/full-t.in b/tests/client/full-t.in index ce2789d..680e78f 100644 --- a/tests/client/full-t.in +++ b/tests/client/full-t.in @@ -56,19 +56,19 @@ chdir "$ENV{SOURCE}" or die "Cannot chdir to $ENV{SOURCE}: $!\n"; SKIP: { skip 'no keytab configuration', $total - unless -f "$ENV{BUILD}/data/test.keytab"; + unless -f "$ENV{BUILD}/config/keytab"; my $remctld = '@REMCTLD@'; skip 'remctld not found', $total unless $remctld; # Spawn remctld and get local tickets. Don't destroy the user's Kerberos # ticket cache. unlink ('krb5cc_test', 'test-pid'); - my $principal = contents ("$ENV{BUILD}/data/test.principal"); + my $principal = contents ("$ENV{BUILD}/config/principal"); remctld_spawn ($remctld, $principal, - "$ENV{BUILD}/data/test.keytab", + "$ENV{BUILD}/config/keytab", "$ENV{SOURCE}/data/full.conf"); $ENV{KRB5CCNAME} = 'krb5cc_test'; - getcreds ("$ENV{BUILD}/data/test.keytab", $principal); + getcreds ("$ENV{BUILD}/config/keytab", $principal); # Use Wallet::Admin to set up the database. db_setup; diff --git a/tests/client/prompt-t.in b/tests/client/prompt-t.in index 1d8b079..682cd70 100644 --- a/tests/client/prompt-t.in +++ b/tests/client/prompt-t.in @@ -21,11 +21,11 @@ chdir "$ENV{SOURCE}" or die "Cannot chdir to $ENV{SOURCE}: $!\n"; SKIP: { skip 'no password configuration', $total - unless -f "$ENV{BUILD}/data/test.password"; + unless -f "$ENV{BUILD}/config/password"; my $remctld = '@REMCTLD@'; skip 'remctld not found', $total unless $remctld; eval { require Expect }; - skip 'Exepct module not found', $total if $@; + skip 'Expect module not found', $total if $@; # Disable sending of wallet's output to our standard output. Do this # twice to avoid Perl warnings. @@ -34,14 +34,14 @@ SKIP: { # Spawn remctld and set up with a different ticket cache. unlink ('krb5cc_test', 'test-pid'); - my $principal = contents ("$ENV{BUILD}/data/test.principal"); - remctld_spawn ($remctld, $principal, "$ENV{BUILD}/data/test.keytab", + my $principal = contents ("$ENV{BUILD}/config/principal"); + remctld_spawn ($remctld, $principal, "$ENV{BUILD}/config/keytab", "$ENV{SOURCE}/data/basic.conf"); $ENV{KRB5CCNAME} = 'krb5cc_test'; # Read in the principal and password. - open (PASS, '<', "$ENV{BUILD}/data/test.password") - or die "Cannot open $ENV{BUILD}/data/test.password: $!\n"; + open (PASS, '<', "$ENV{BUILD}/config/password") + or die "Cannot open $ENV{BUILD}/config/password: $!\n"; my $user = ; my $password = ; close PASS; diff --git a/tests/config/README b/tests/config/README new file mode 100644 index 0000000..2992a11 --- /dev/null +++ b/tests/config/README @@ -0,0 +1,24 @@ +This directory contains configuration required to run the complete wallet +test suite. If there is no configuration in this directory, some of the +tests will be skipped. To enable the full test suite, create the +following files: + +keytab + + A valid Kerberos keytab for a principal, preferrably in your local + realm. This will be used to test network interactions that require + Kerberos authentication. + +principal + + The name of the Kerberos principal whose keys are stored in keytab. + +password + + This file should contain two lines. The first line is the + fully-qualified principal (including the realm) of a Kerberos + principal to use for testing authentication. The second line is + the password for that principal. The realm of the principal must + be configured in your system krb5.conf file or in DNS configuration + picked up by your Kerberos libraries and must be in the same realm as + the keytab above or have valid cross-realm trust to it. diff --git a/tests/data/perl.conf b/tests/data/perl.conf new file mode 100644 index 0000000..eaf7443 --- /dev/null +++ b/tests/data/perl.conf @@ -0,0 +1,6 @@ +# Configuration for Perl tests. -*- perl -*- + +# No special configuration yet. + +# File must end with this line. +1; diff --git a/tests/docs/pod-spelling-t b/tests/docs/pod-spelling-t index eaa7dd6..e1a95cd 100755 --- a/tests/docs/pod-spelling-t +++ b/tests/docs/pod-spelling-t @@ -1,80 +1,52 @@ #!/usr/bin/perl # -# Check for spelling errors in POD documentation +# Checks all POD files in the tree for spelling errors using Test::Spelling. +# This test is disabled unless RRA_MAINTAINER_TESTS is set, since spelling +# dictionaries vary too much between environments. # -# Checks all POD files in the tree for spelling problems using Pod::Spell and -# either aspell or ispell. aspell is preferred. This test is disabled unless -# RRA_MAINTAINER_TESTS is set, since spelling dictionaries vary too much -# between environments. +# The canonical version of this file is maintained in the rra-c-util package, +# which can be found at . # -# Copyright 2008, 2009 Russ Allbery +# Written by Russ Allbery +# Copyright 2012, 2013 +# The Board of Trustees of the Leland Stanford Junior University # -# See LICENSE for licensing terms. +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +use 5.006; use strict; -use Test::More; +use warnings; -# Skip all spelling tests unless the maintainer environment variable is set. -plan skip_all => 'spelling tests only run for maintainer' - unless $ENV{RRA_MAINTAINER_TESTS}; +use lib "$ENV{SOURCE}/tap/perl"; + +use Test::More; +use Test::RRA qw(skip_unless_maintainer use_prereq); +use Test::RRA::Automake qw(automake_setup perl_dirs); -# Load required Perl modules. -eval 'use Test::Pod 1.00'; -plan skip_all => 'Test::Pod 1.00 required for testing POD' if $@; -eval 'use Pod::Spell'; -plan skip_all => 'Pod::Spell required to test POD spelling' if $@; +# Only run this test for the maintainer. +skip_unless_maintainer('Spelling tests'); -# Locate a spell-checker. hunspell is not currently supported due to its lack -# of support for contractions (at least in the version in Debian). -my @spell; -my %options = (aspell => [ qw(-d en_US --home-dir=./ list) ], - ispell => [ qw(-d american -l -p /dev/null) ]); -SEARCH: for my $program (qw/aspell ispell/) { - for my $dir (split ':', $ENV{PATH}) { - if (-x "$dir/$program") { - @spell = ("$dir/$program", @{ $options{$program} }); - } - last SEARCH if @spell; - } -} -plan skip_all => 'aspell or ispell required to test POD spelling' - unless @spell; +# Load prerequisite modules. +use_prereq('Test::Spelling'); -# Prerequisites are satisfied, so we're going to do some testing. Figure out -# what POD files we have and from that develop our plan. -$| = 1; -my @pod = map { - my $pod = "$ENV{SOURCE}/../" . $_; - $pod =~ s,[^/.][^/]*/../,,g; - $pod; -} qw(client/wallet.pod client/wallet-rekey.pod server/keytab-backend - server/wallet-admin server/wallet-backend server/wallet-report); -plan tests => scalar @pod; +# Set up Automake testing. +automake_setup(); -# Finally, do the checks. -for my $pod (@pod) { - my $child = open (CHILD, '-|'); - if (not defined $child) { - BAIL_OUT ("cannot fork: $!"); - } elsif ($child == 0) { - my $pid = open (SPELL, '|-', @spell) - or BAIL_OUT ("cannot run @spell: $!"); - open (POD, '<', $pod) or BAIL_OUT ("cannot open $pod: $!"); - my $parser = Pod::Spell->new; - $parser->parse_from_filehandle (\*POD, \*SPELL); - close POD; - close SPELL; - exit ($? >> 8); - } else { - my @words = ; - close CHILD; - SKIP: { - skip "@spell failed for $pod", 1 unless $? == 0; - for (@words) { - s/^\s+//; - s/\s+$//; - } - is ("@words", '', $pod); - } - } -} +# Run the tests. +all_pod_files_spelling_ok(perl_dirs()); diff --git a/tests/docs/pod-t b/tests/docs/pod-t index e25ade2..2743287 100755 --- a/tests/docs/pod-t +++ b/tests/docs/pod-t @@ -1,22 +1,48 @@ #!/usr/bin/perl -w # -# Test POD formatting for documentation. +# Check all POD documents in the tree, except for any embedded Perl module +# distribution, for POD formatting errors. +# +# The canonical version of this file is maintained in the rra-c-util package, +# which can be found at . # # Written by Russ Allbery -# Copyright 2008, 2010 Board of Trustees, Leland Stanford Jr. University +# Copyright 2012, 2013 +# The Board of Trustees of the Leland Stanford Junior University +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: # -# See LICENSE for licensing terms. +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +use 5.006; use strict; +use warnings; + +use lib "$ENV{SOURCE}/tap/perl"; + use Test::More; -eval 'use Test::Pod 1.00'; -plan skip_all => 'Test::Pod 1.00 required for testing POD' if $@; +use Test::RRA qw(use_prereq); +use Test::RRA::Automake qw(automake_setup perl_dirs); + +# Load prerequisite modules. +use_prereq('Test::Pod'); + +# Set up Automake testing. +automake_setup(); -my @files = qw(client/wallet.pod client/wallet-rekey.pod server/keytab-backend - server/wallet-admin server/wallet-backend - server/wallet-report); -my $total = scalar (@files); -plan tests => $total; -for my $file (@files) { - pod_file_ok ("$ENV{SOURCE}/../$file", $file); -} +# Run the tests. +all_pod_files_ok(perl_dirs()); diff --git a/tests/portable/asprintf-t.c b/tests/portable/asprintf-t.c index 04fbd1b..4513a85 100644 --- a/tests/portable/asprintf-t.c +++ b/tests/portable/asprintf-t.c @@ -1,11 +1,18 @@ /* * asprintf and vasprintf test suite. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * Copyright 2006, 2008, 2009 - * Board of Trustees, Leland Stanford Jr. University * - * See LICENSE for licensing terms. + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #include diff --git a/tests/portable/mkstemp-t.c b/tests/portable/mkstemp-t.c index 54701f7..98c708e 100644 --- a/tests/portable/mkstemp-t.c +++ b/tests/portable/mkstemp-t.c @@ -1,11 +1,18 @@ /* * mkstemp test suite. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * Copyright 2002, 2004, 2008, 2009 - * Board of Trustees, Leland Stanford Jr. University * - * See LICENSE for licensing terms. + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #include diff --git a/tests/portable/setenv-t.c b/tests/portable/setenv-t.c index 5bc59ce..a1aecb5 100644 --- a/tests/portable/setenv-t.c +++ b/tests/portable/setenv-t.c @@ -1,14 +1,18 @@ /* * setenv test suite. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * Copyright 2009 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, - * 2002, 2003 by The Internet Software Consortium and Rich Salz * - * See LICENSE for licensing terms. + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #include diff --git a/tests/portable/snprintf-t.c b/tests/portable/snprintf-t.c index 4b64f5b..927de96 100644 --- a/tests/portable/snprintf-t.c +++ b/tests/portable/snprintf-t.c @@ -1,14 +1,20 @@ /* * snprintf test suite. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * 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, - * 2002, 2003 by The Internet Software Consortium and Rich Salz + * Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006 + * Russ Allbery + * Copyright 2009, 2010 + * The Board of Trustees of the Leland Stanford Junior University + * Copyright 1995 Patrick Powell + * Copyright 2001 Hrvoje Niksic * - * See LICENSE for licensing terms. + * This code is based on code written by Patrick Powell (papowell@astart.com) + * It may be used for any purpose as long as this notice remains intact + * on all source code distributions */ #include diff --git a/tests/portable/strlcat-t.c b/tests/portable/strlcat-t.c index e02c277..54d0d40 100644 --- a/tests/portable/strlcat-t.c +++ b/tests/portable/strlcat-t.c @@ -1,14 +1,18 @@ /* * strlcat test suite. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * Copyright 2009 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, - * 2002, 2003 by The Internet Software Consortium and Rich Salz * - * See LICENSE for licensing terms. + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #include diff --git a/tests/portable/strlcpy-t.c b/tests/portable/strlcpy-t.c index ba224ba..26aa8f2 100644 --- a/tests/portable/strlcpy-t.c +++ b/tests/portable/strlcpy-t.c @@ -1,14 +1,18 @@ /* * strlcpy test suite. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * * Written by Russ Allbery - * Copyright 2009 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, - * 2002, 2003 by The Internet Software Consortium and Rich Salz * - * See LICENSE for licensing terms. + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work. */ #include diff --git a/tests/runtests.c b/tests/runtests.c index ab77629..4249875 100644 --- a/tests/runtests.c +++ b/tests/runtests.c @@ -3,14 +3,15 @@ * * Usage: * - * runtests + * runtests [-b ] [-s ] + * runtests -o [-b ] [-s ] * - * 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), optionally preceded by "1..", - * although that line may be given anywhere in the output. Each additional - * line should be in the following format: + * In the first case, 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), optionally preceded by + * "1..", although that line may be given anywhere in the output. Each + * additional line should be in the following format: * * ok * not ok @@ -39,10 +40,21 @@ * This is a subset of TAP as documented in Test::Harness::TAP or * TAP::Parser::Grammar, which comes with Perl. * + * If the -o option is given, instead run a single test and display all of its + * output. This is intended for use with failing tests so that the person + * running the test suite can get more details about what failed. + * + * If built with the C preprocessor symbols SOURCE and BUILD defined, C TAP + * Harness will export those values in the environment so that tests can find + * the source and build directory and will look for tests under both + * directories. These paths can also be set with the -b and -s command-line + * options, which will override anything set at build time. + * * Any bug reports, bug fixes, and improvements are very much welcome and - * should be sent to the e-mail address below. + * should be sent to the e-mail address below. This program is part of C TAP + * Harness . * - * Copyright 2000, 2001, 2004, 2006, 2007, 2008, 2009, 2010 + * Copyright 2000, 2001, 2004, 2006, 2007, 2008, 2009, 2010, 2011 * Russ Allbery * * Permission is hereby granted, free of charge, to any person obtaining a @@ -64,6 +76,13 @@ * DEALINGS IN THE SOFTWARE. */ +/* Required for fdopen(), getopt(), and putenv(). */ +#if defined(__STRICT_ANSI__) || defined(PEDANTIC) +# ifndef _XOPEN_SOURCE +# define _XOPEN_SOURCE 500 +# endif +#endif + #include #include #include @@ -71,6 +90,7 @@ #include #include #include +#include #include #include #include @@ -133,10 +153,10 @@ struct testset { 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. */ + unsigned int aborted; /* Whether the set as aborted. */ int reported; /* Whether the results were reported. */ int status; /* The exit status of the test. */ - int all_skipped; /* Whether all tests were skipped. */ + unsigned int all_skipped; /* Whether all tests were skipped. */ char *reason; /* Why all tests were skipped. */ }; @@ -146,6 +166,23 @@ struct testlist { struct testlist *next; }; +/* + * Usage message. Should be used as a printf format with two arguments: the + * path to runtests, given twice. + */ +static const char usage_message[] = "\ +Usage: %s [-b ] [-s ] \n\ + %s -o [-b ] [-s ] \n\ +\n\ +Options:\n\ + -b Set the build directory to \n\ + -o Run a single test rather than a list of tests\n\ + -s Set the source directory to \n\ +\n\ +runtests normally runs each test listed in a file whose path is given as\n\ +its command-line argument. With the -o option, it instead runs a single\n\ +test and shows its complete output.\n"; + /* * Header used for test output. %s is replaced by the file name of the list * of tests. @@ -367,7 +404,9 @@ test_plan(const char *line, struct testset *ts) * 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. + * our statistics, since they're no longer relevant. strtol is called + * with a second argument to advance the line pointer past the count to + * make it simpler to detect the # skip case. */ n = strtol(line, (char **) &line, 10); if (n == 0) { @@ -437,6 +476,7 @@ test_checkline(const char *line, struct testset *ts) char *end; long number; unsigned long i, current; + int outlen; /* Before anything, check for a test abort. */ bail = strstr(line, "Bail out!"); @@ -557,7 +597,8 @@ test_checkline(const char *line, struct testset *ts) ts->results[current - 1] = status; test_backspace(ts); if (isatty(STDOUT_FILENO)) { - ts->length = printf("%lu/%lu", current, ts->count); + outlen = printf("%lu/%lu", current, ts->count); + ts->length = (outlen >= 0) ? outlen : 0; fflush(stdout); } } @@ -565,23 +606,20 @@ test_checkline(const char *line, struct testset *ts) /* * Print out a range of test numbers, returning the number of characters it - * took up. Add a comma and a space before the range if chars indicates that + * took up. Takes the first number, the last number, the number of characters + * already printed on the line, and the limit of number of characters the line + * can hold. Add a comma and a space before the range if chars indicates that * something has already been printed on the line, and print ... instead if * chars plus the space needed would go over the limit (use a limit of 0 to - * disable this. + * disable this). */ static unsigned int test_print_range(unsigned long first, unsigned long last, unsigned int chars, unsigned int limit) { unsigned int needed = 0; - unsigned int out = 0; unsigned long n; - if (chars > 0) { - needed += 2; - if (!limit || chars <= limit) out += printf(", "); - } for (n = first; n > 0; n /= 10) needed++; if (last > first) { @@ -589,15 +627,26 @@ test_print_range(unsigned long first, unsigned long last, unsigned int chars, needed++; needed++; } - if (limit && chars + needed > limit) { - if (chars <= limit) - out += printf("..."); + if (chars > 0) + needed += 2; + if (limit > 0 && chars + needed > limit) { + needed = 0; + if (chars <= limit) { + if (chars > 0) { + printf(", "); + needed += 2; + } + printf("..."); + needed += 3; + } } else { + if (chars > 0) + printf(", "); if (last > first) - out += printf("%lu-", first); - out += printf("%lu", last); + printf("%lu-", first); + printf("%lu", last); } - return out; + return needed; } @@ -825,14 +874,14 @@ test_fail_summary(const struct testlist *fails) last = i + 1; else { if (first != 0) - chars += test_print_range(first, last, chars, 20); + chars += test_print_range(first, last, chars, 19); first = i + 1; last = i + 1; } } } if (first != 0) - test_print_range(first, last, chars, 20); + test_print_range(first, last, chars, 19); putchar('\n'); free(ts->file); free(ts->path); @@ -861,10 +910,17 @@ find_test(const char *name, struct testset *ts, const char *source, const char *build) { char *path; - const char *bases[] = { ".", build, source, NULL }; + const char *bases[4]; unsigned int i; - for (i = 0; bases[i] != NULL; i++) { + bases[0] = "."; + bases[1] = build; + bases[2] = source; + bases[3] = NULL; + + for (i = 0; i < 3; i++) { + if (bases[i] == NULL) + continue; path = xmalloc(strlen(bases[i]) + strlen(name) + 4); sprintf(path, "%s/%s-t", bases[i], name); if (access(path, X_OK) != 0) @@ -993,6 +1049,7 @@ test_batch(const char *testlist, const char *source, const char *build) failed += ts.failed; } total -= skipped; + fclose(tests); /* Stop the timer and get our child resource statistics. */ gettimeofday(&end, NULL); @@ -1060,17 +1117,23 @@ int main(int argc, char *argv[]) { int option; + int status = 0; int single = 0; - char *setting; + char *source_env = NULL; + char *build_env = NULL; const char *list; const char *source = SOURCE; const char *build = BUILD; - while ((option = getopt(argc, argv, "b:os:")) != EOF) { + while ((option = getopt(argc, argv, "b:hos:")) != EOF) { switch (option) { case 'b': build = optarg; break; + case 'h': + printf(usage_message, argv[0], argv[0]); + exit(0); + break; case 'o': single = 1; break; @@ -1081,36 +1144,46 @@ main(int argc, char *argv[]) exit(1); } } - argc -= optind; - argv += optind; - if (argc != 1) { - fprintf(stderr, "Usage: runtests \n"); + if (argc - optind != 1) { + fprintf(stderr, usage_message, argv[0], argv[0]); exit(1); } + argc -= optind; + argv += optind; if (source != NULL) { - setting = xmalloc(strlen("SOURCE=") + strlen(source) + 1); - sprintf(setting, "SOURCE=%s", source); - if (putenv(setting) != 0) + source_env = xmalloc(strlen("SOURCE=") + strlen(source) + 1); + sprintf(source_env, "SOURCE=%s", source); + if (putenv(source_env) != 0) sysdie("cannot set SOURCE in the environment"); } if (build != NULL) { - setting = xmalloc(strlen("BUILD=") + strlen(build) + 1); - sprintf(setting, "BUILD=%s", build); - if (putenv(setting) != 0) + build_env = xmalloc(strlen("BUILD=") + strlen(build) + 1); + sprintf(build_env, "BUILD=%s", build); + if (putenv(build_env) != 0) sysdie("cannot set BUILD in the environment"); } - if (single) { + if (single) test_single(argv[0], source, build); - exit(0); - } else { + else { list = strrchr(argv[0], '/'); if (list == NULL) list = argv[0]; else list++; printf(banner, list); - exit(test_batch(argv[0], source, build) ? 0 : 1); + status = test_batch(argv[0], source, build) ? 0 : 1; + } + + /* For valgrind cleanliness. */ + if (source_env != NULL) { + putenv((char *) "SOURCE="); + free(source_env); + } + if (build_env != NULL) { + putenv((char *) "BUILD="); + free(build_env); } + exit(status); } diff --git a/tests/tap/basic.c b/tests/tap/basic.c index 829f91a..e8196fc 100644 --- a/tests/tap/basic.c +++ b/tests/tap/basic.c @@ -1,22 +1,38 @@ /* * Some utility routines for writing tests. * - * Herein are a variety of utility routines for writing tests. All routines - * 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, skipping tests, - * or setting up the TAP output format. + * Here are a variety of utility routines for writing tests compatible with + * the TAP protocol. All routines 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, skipping tests, reporting errors, setting + * up the TAP output format, or finding things in the test environment. * - * Copyright 2009, 2010 Russ Allbery - * Copyright 2006, 2007, 2008 - * 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, - * 2002, 2003 by The Internet Software Consortium and Rich Salz + * This file is part of C TAP Harness. The current version plus supporting + * documentation is at . * - * See LICENSE for licensing terms. + * Copyright 2009, 2010, 2011, 2012 Russ Allbery + * Copyright 2001, 2002, 2004, 2005, 2006, 2007, 2008, 2011, 2012 + * The Board of Trustees of the Leland Stanford Junior University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. */ #include @@ -24,12 +40,21 @@ #include #include #include +#ifdef _WIN32 +# include +#else +# include +#endif #include -#include -#include #include -#include +#include + +/* Windows provides mkdir and rmdir under different names. */ +#ifdef _WIN32 +# define mkdir(p, m) _mkdir(p) +# define rmdir(p) _rmdir(p) +#endif /* * The test count. Always contains the number that will be used for the next @@ -57,7 +82,9 @@ static int _lazy = 0; /* * Our exit handler. Called on completion of the test to report a summary of - * results provided we're still in the original process. + * results provided we're still in the original process. This also handles + * printing out the plan if we used plan_lazy(), although that's suppressed if + * we never ran a test (due to an early bail, for example). */ static void finish(void) @@ -66,8 +93,9 @@ finish(void) if (_planned == 0 && !_lazy) return; + fflush(stderr); if (_process != 0 && getpid() == _process) { - if (_lazy) { + if (_lazy && highest > 0) { printf("1..%lu\n", highest); _planned = highest; } @@ -98,6 +126,7 @@ plan(unsigned long count) if (setvbuf(stdout, NULL, _IOLBF, BUFSIZ) != 0) fprintf(stderr, "# cannot set stdout to line buffered: %s\n", strerror(errno)); + fflush(stderr); printf("1..%lu\n", count); testnum = 1; _planned = count; @@ -130,6 +159,7 @@ plan_lazy(void) void skip_all(const char *format, ...) { + fflush(stderr); printf("1..0 # skip"); if (format != NULL) { va_list args; @@ -162,6 +192,7 @@ print_desc(const char *format, va_list args) void ok(int success, const char *format, ...) { + fflush(stderr); printf("%sok %lu", success ? "" : "not ", testnum++); if (!success) _failed++; @@ -182,6 +213,7 @@ ok(int success, const char *format, ...) void okv(int success, const char *format, va_list args) { + fflush(stderr); printf("%sok %lu", success ? "" : "not ", testnum++); if (!success) _failed++; @@ -197,6 +229,7 @@ okv(int success, const char *format, va_list args) void skip(const char *reason, ...) { + fflush(stderr); printf("ok %lu # skip", testnum++); if (reason != NULL) { va_list args; @@ -218,6 +251,7 @@ ok_block(unsigned long count, int status, const char *format, ...) { unsigned long i; + fflush(stderr); for (i = 0; i < count; i++) { printf("%sok %lu", status ? "" : "not ", testnum++); if (!status) @@ -242,6 +276,7 @@ skip_block(unsigned long count, const char *reason, ...) { unsigned long i; + fflush(stderr); for (i = 0; i < count; i++) { printf("ok %lu # skip", testnum++); if (reason != NULL) { @@ -264,6 +299,7 @@ skip_block(unsigned long count, const char *reason, ...) void is_int(long wanted, long seen, const char *format, ...) { + fflush(stderr); if (wanted == seen) printf("ok %lu", testnum++); else { @@ -293,6 +329,7 @@ is_string(const char *wanted, const char *seen, const char *format, ...) wanted = "(null)"; if (seen == NULL) seen = "(null)"; + fflush(stderr); if (strcmp(wanted, seen) == 0) printf("ok %lu", testnum++); else { @@ -311,31 +348,6 @@ is_string(const char *wanted, const char *seen, const char *format, ...) } -/* - * Takes an expected double and a seen double and assumes the test passes if - * those two numbers match. - */ -void -is_double(double wanted, double seen, const char *format, ...) -{ - if (wanted == seen) - printf("ok %lu", testnum++); - else { - printf("# wanted: %g\n# seen: %g\n", wanted, seen); - printf("not ok %lu", testnum++); - _failed++; - } - if (format != NULL) { - va_list args; - - va_start(args, format); - print_desc(format, args); - va_end(args); - } - putchar('\n'); -} - - /* * Takes an expected unsigned long and a seen unsigned long and assumes the * test passes if the two numbers match. Otherwise, reports them in hex. @@ -343,6 +355,7 @@ is_double(double wanted, double seen, const char *format, ...) void is_hex(unsigned long wanted, unsigned long seen, const char *format, ...) { + fflush(stderr); if (wanted == seen) printf("ok %lu", testnum++); else { @@ -370,6 +383,7 @@ bail(const char *format, ...) { va_list args; + fflush(stderr); fflush(stdout); printf("Bail out! "); va_start(args, format); @@ -389,6 +403,7 @@ sysbail(const char *format, ...) va_list args; int oerrno = errno; + fflush(stderr); fflush(stdout); printf("Bail out! "); va_start(args, format); @@ -407,6 +422,7 @@ diag(const char *format, ...) { va_list args; + fflush(stderr); fflush(stdout); printf("# "); va_start(args, format); @@ -425,6 +441,7 @@ sysdiag(const char *format, ...) va_list args; int oerrno = errno; + fflush(stderr); fflush(stdout); printf("# "); va_start(args, format); @@ -434,6 +451,92 @@ sysdiag(const char *format, ...) } +/* + * Allocate cleared memory, reporting a fatal error with bail on failure. + */ +void * +bcalloc(size_t n, size_t size) +{ + void *p; + + p = calloc(n, size); + if (p == NULL) + sysbail("failed to calloc %lu", (unsigned long)(n * size)); + return p; +} + + +/* + * Allocate memory, reporting a fatal error with bail on failure. + */ +void * +bmalloc(size_t size) +{ + void *p; + + p = malloc(size); + if (p == NULL) + sysbail("failed to malloc %lu", (unsigned long) size); + return p; +} + + +/* + * Reallocate memory, reporting a fatal error with bail on failure. + */ +void * +brealloc(void *p, size_t size) +{ + p = realloc(p, size); + if (p == NULL) + sysbail("failed to realloc %lu bytes", (unsigned long) size); + return p; +} + + +/* + * Copy a string, reporting a fatal error with bail on failure. + */ +char * +bstrdup(const char *s) +{ + char *p; + size_t len; + + len = strlen(s) + 1; + p = malloc(len); + if (p == NULL) + sysbail("failed to strdup %lu bytes", (unsigned long) len); + memcpy(p, s, len); + return p; +} + + +/* + * Copy up to n characters of a string, reporting a fatal error with bail on + * failure. Don't use the system strndup function, since it may not exist and + * the TAP library doesn't assume any portability support. + */ +char * +bstrndup(const char *s, size_t n) +{ + const char *p; + char *copy; + size_t length; + + /* Don't assume that the source string is nul-terminated. */ + for (p = s; (size_t) (p - s) < n && *p != '\0'; p++) + ; + length = p - s; + copy = malloc(length + 1); + if (p == NULL) + sysbail("failed to strndup %lu bytes", (unsigned long) length); + memcpy(copy, s, length); + copy[length] = '\0'; + return copy; +} + + /* * 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 @@ -458,9 +561,7 @@ test_file_path(const char *file) if (base == NULL) continue; length = strlen(base) + 1 + strlen(file) + 1; - path = malloc(length); - if (path == NULL) - sysbail("cannot allocate memory"); + path = bmalloc(length); sprintf(path, "%s/%s", base, file); if (access(path, R_OK) == 0) break; @@ -482,3 +583,47 @@ test_file_path_free(char *path) if (path != NULL) free(path); } + + +/* + * Create a temporary directory, tmp, under BUILD if set and the current + * directory if it does not. Returns the path to the temporary directory in + * newly allocated memory, and calls bail on any failure. The return value + * should be freed with test_tmpdir_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_tmpdir(void) +{ + const char *build; + char *path = NULL; + size_t length; + + build = getenv("BUILD"); + if (build == NULL) + build = "."; + length = strlen(build) + strlen("/tmp") + 1; + path = bmalloc(length); + sprintf(path, "%s/tmp", build); + if (access(path, X_OK) < 0) + if (mkdir(path, 0777) < 0) + sysbail("error creating temporary directory %s", path); + return path; +} + + +/* + * Free a path returned from test_tmpdir() and attempt to remove the + * directory. If we can't delete the directory, don't worry; something else + * that hasn't yet cleaned up may still be using it. + */ +void +test_tmpdir_free(char *path) +{ + rmdir(path); + if (path != NULL) + free(path); +} diff --git a/tests/tap/basic.h b/tests/tap/basic.h index 9602db4..fa4adaf 100644 --- a/tests/tap/basic.h +++ b/tests/tap/basic.h @@ -1,47 +1,38 @@ /* * Basic utility routines for the TAP protocol. * - * Copyright 2009, 2010 Russ Allbery - * Copyright 2006, 2007, 2008 - * 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, - * 2002, 2003 by The Internet Software Consortium and Rich Salz + * This file is part of C TAP Harness. The current version plus supporting + * documentation is at . * - * See LICENSE for licensing terms. + * Copyright 2009, 2010, 2011, 2012 Russ Allbery + * Copyright 2001, 2002, 2004, 2005, 2006, 2007, 2008, 2011, 2012 + * The Board of Trustees of the Leland Stanford Junior University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. */ #ifndef TAP_BASIC_H #define TAP_BASIC_H 1 +#include #include /* va_list */ -#include /* pid_t */ - -/* - * __attribute__ is available in gcc 2.5 and later, but only with gcc 2.7 - * could you use the __format__ form of the attributes, which is what we use - * (to avoid confusion with other macros). - */ -#ifndef __attribute__ -# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7) -# define __attribute__(spec) /* empty */ -# endif -#endif - -/* - * BEGIN_DECLS is used at the beginning of declarations so that C++ - * compilers don't mangle their names. END_DECLS is used at the end. - */ -#undef BEGIN_DECLS -#undef END_DECLS -#ifdef __cplusplus -# define BEGIN_DECLS extern "C" { -# define END_DECLS } -#else -# define BEGIN_DECLS /* empty */ -# define END_DECLS /* empty */ -#endif +#include /* size_t */ /* * Used for iterating through arrays. ARRAY_SIZE returns the number of @@ -93,8 +84,6 @@ void skip_block(unsigned long count, const char *reason, ...) /* Check an expected value against a seen value. */ 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))); void is_string(const char *wanted, const char *seen, const char *format, ...) __attribute__((__format__(printf, 3, 4))); void is_hex(unsigned long wanted, unsigned long seen, const char *format, ...) @@ -112,6 +101,18 @@ void diag(const char *format, ...) void sysdiag(const char *format, ...) __attribute__((__nonnull__, __format__(printf, 1, 2))); +/* Allocate memory, reporting a fatal error with bail on failure. */ +void *bcalloc(size_t, size_t) + __attribute__((__alloc_size__(1, 2), __malloc__)); +void *bmalloc(size_t) + __attribute__((__alloc_size__(1), __malloc__)); +void *brealloc(void *, size_t) + __attribute__((__alloc_size__(2), __malloc__)); +char *bstrdup(const char *) + __attribute__((__malloc__, __nonnull__)); +char *bstrndup(const char *, size_t) + __attribute__((__malloc__, __nonnull__)); + /* * Find a test file under BUILD or SOURCE, returning the full path. The * returned path should be freed with test_file_path_free(). @@ -120,6 +121,14 @@ char *test_file_path(const char *file) __attribute__((__malloc__, __nonnull__)); void test_file_path_free(char *path); +/* + * Create a temporary directory relative to BUILD and return the path. The + * returned path should be freed with test_tmpdir_free. + */ +char *test_tmpdir(void) + __attribute__((__malloc__)); +void test_tmpdir_free(char *path); + END_DECLS #endif /* TAP_BASIC_H */ diff --git a/tests/tap/kerberos.c b/tests/tap/kerberos.c index a17d980..474cf4f 100644 --- a/tests/tap/kerberos.c +++ b/tests/tap/kerberos.c @@ -1,47 +1,90 @@ /* * Utility functions for tests that use Kerberos. * - * Currently only provides kerberos_setup(), which assumes a particular set of - * data files in either the SOURCE or BUILD directories and, using those, - * obtains Kerberos credentials, sets up a ticket cache, and sets the - * environment variable pointing to the Kerberos keytab to use for testing. + * The core function is kerberos_setup, which loads Kerberos test + * configuration and returns a struct of information. It also supports + * obtaining initial tickets from the configured keytab and setting up + * KRB5CCNAME and KRB5_KTNAME if a Kerberos keytab is present. Also included + * are utility functions for setting up a krb5.conf file and reporting + * Kerberos errors or warnings during testing. * - * Copyright 2006, 2007, 2009, 2010 - * Board of Trustees, Leland Stanford Jr. University + * Some of the functionality here is only available if the Kerberos libraries + * are available. * - * See LICENSE for licensing terms. + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * + * Written by Russ Allbery + * Copyright 2006, 2007, 2009, 2010, 2011, 2012 + * The Board of Trustees of the Leland Stanford Junior University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. */ #include -#include +#ifdef HAVE_KERBEROS +# include +#endif #include +#include + #include #include -#include -#include +#include +#include + +/* + * Disable the requirement that format strings be literals, since it's easier + * to handle the possible patterns for kinit commands as an array. + */ +#pragma GCC diagnostic ignored "-Wformat-nonliteral" /* - * 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. - * - * Returns the contents of test.principal in newly allocated memory or NULL if - * Kerberos tests are apparently not configured. If Kerberos tests are - * configured but something else fails, calls bail(). + * These variables hold the allocated configuration struct, the environment to + * point to a different Kerberos ticket cache, keytab, and configuration file, + * and the temporary directories used. We store them so that we can free them + * on exit for cleaner valgrind output, making it easier to find real memory + * leaks in the tested programs. + */ +static struct kerberos_config *config = NULL; +static char *krb5ccname = NULL; +static char *krb5_ktname = NULL; +static char *krb5_config = NULL; +static char *tmpdir_ticket = NULL; +static char *tmpdir_conf = NULL; + + +/* + * Obtain Kerberos tickets and fill in the principal config entry. * - * The error handling here is not great. We should have a bail_krb5 that uses - * the same logic as messages-krb5.c, which hasn't yet been imported into - * rra-c-util. + * There are two implementations of this function, one if we have native + * Kerberos libraries available and one if we don't. Uses keytab to obtain + * credentials, and fills in the cache member of the provided config struct. */ -char * -kerberos_setup(void) +#ifdef HAVE_KERBEROS + +static void +kerberos_kinit(void) { - char *path, *krbtgt; - const char *build, *realm; - FILE *file; - char principal[BUFSIZ]; + char *name, *krbtgt; krb5_error_code code; krb5_context ctx; krb5_ccache ccache; @@ -49,89 +92,397 @@ kerberos_setup(void) krb5_keytab keytab; krb5_get_init_creds_opt *opts; krb5_creds creds; + const char *realm; - /* Read the principal name and find the keytab file. */ - path = test_file_path("data/test.principal"); - if (path == NULL) - return NULL; - file = fopen(path, "r"); - if (file == NULL) { - free(path); - return NULL; - } - if (fgets(principal, sizeof(principal), file) == NULL) { - fclose(file); - bail("cannot read %s", path); - } - fclose(file); - if (principal[strlen(principal) - 1] != '\n') - bail("no newline in %s", path); - free(path); - principal[strlen(principal) - 1] = '\0'; - path = test_file_path("data/test.keytab"); - if (path == NULL) - return NULL; - - /* Set the KRB5CCNAME and KRB5_KTNAME environment variables. */ - build = getenv("BUILD"); - if (build == NULL) - build = "."; - putenv(concat("KRB5CCNAME=", build, "/data/test.cache", (char *) 0)); - putenv(concat("KRB5_KTNAME=", path, (char *) 0)); - - /* Now do the Kerberos initialization. */ + /* + * Determine the principal corresponding to that keytab. We copy the + * memory to ensure that it's allocated in the right memory domain on + * systems where that may matter (like Windows). + */ code = krb5_init_context(&ctx); if (code != 0) - bail("error initializing Kerberos"); + bail_krb5(ctx, code, "error initializing Kerberos"); + kprinc = kerberos_keytab_principal(ctx, config->keytab); + code = krb5_unparse_name(ctx, kprinc, &name); + if (code != 0) + bail_krb5(ctx, code, "error unparsing name"); + krb5_free_principal(ctx, kprinc); + config->principal = bstrdup(name); + krb5_free_unparsed_name(ctx, name); + + /* Now do the Kerberos initialization. */ code = krb5_cc_default(ctx, &ccache); if (code != 0) - bail("error setting ticket cache"); - code = krb5_parse_name(ctx, principal, &kprinc); + bail_krb5(ctx, code, "error setting ticket cache"); + code = krb5_parse_name(ctx, config->principal, &kprinc); if (code != 0) - bail("error parsing principal %s", principal); + bail_krb5(ctx, code, "error parsing principal %s", config->principal); realm = krb5_principal_get_realm(ctx, kprinc); - krbtgt = concat("krbtgt/", realm, "@", realm, (char *) 0); - code = krb5_kt_resolve(ctx, path, &keytab); + basprintf(&krbtgt, "krbtgt/%s@%s", realm, realm); + code = krb5_kt_resolve(ctx, config->keytab, &keytab); if (code != 0) - bail("cannot open keytab %s", path); + bail_krb5(ctx, code, "cannot open keytab %s", config->keytab); code = krb5_get_init_creds_opt_alloc(ctx, &opts); if (code != 0) - bail("cannot allocate credential options"); + bail_krb5(ctx, code, "cannot allocate credential options"); krb5_get_init_creds_opt_set_default_flags(ctx, NULL, realm, opts); krb5_get_init_creds_opt_set_forwardable(opts, 0); krb5_get_init_creds_opt_set_proxiable(opts, 0); code = krb5_get_init_creds_keytab(ctx, &creds, kprinc, keytab, 0, krbtgt, opts); if (code != 0) - bail("cannot get Kerberos tickets"); + bail_krb5(ctx, code, "cannot get Kerberos tickets"); code = krb5_cc_initialize(ctx, ccache, kprinc); if (code != 0) - bail("error initializing ticket cache"); + bail_krb5(ctx, code, "error initializing ticket cache"); code = krb5_cc_store_cred(ctx, ccache, &creds); if (code != 0) - bail("error storing credentials"); + bail_krb5(ctx, code, "error storing credentials"); krb5_cc_close(ctx, ccache); krb5_free_cred_contents(ctx, &creds); krb5_kt_close(ctx, keytab); krb5_free_principal(ctx, kprinc); + krb5_get_init_creds_opt_free(ctx, opts); krb5_free_context(ctx); free(krbtgt); - free(path); +} - return xstrdup(principal); +#else /* !HAVE_KERBEROS */ + +static void +kerberos_kinit(void) +{ + static const char * const format[] = { + "kinit --no-afslog -k -t %s %s >/dev/null 2>&1 /dev/null 2>&1 /dev/null 2>&1 /dev/null 2>&1 keytab); + config->keytab = NULL; + return; + } + file = fopen(path, "r"); + if (file == NULL) { + test_file_path_free(path); + return; + } + test_file_path_free(path); + if (fgets(principal, sizeof(principal), file) == NULL) + bail("cannot read %s", path); + fclose(file); + if (principal[strlen(principal) - 1] != '\n') + bail("no newline in %s", path); + principal[strlen(principal) - 1] = '\0'; + config->principal = bstrdup(principal); + + /* Now do the Kerberos initialization. */ + for (i = 0; i < ARRAY_SIZE(format); i++) { + basprintf(&command, format[i], config->keytab, principal); + status = system(command); + free(command); + if (status != -1 && WEXITSTATUS(status) == 0) + break; + } + if (status == -1 || WEXITSTATUS(status) != 0) + bail("cannot get Kerberos tickets"); } +#endif /* !HAVE_KERBEROS */ + /* - * Clean up at the end of a test. Currently, all this does is remove the - * ticket cache. + * Clean up at the end of a test. This removes the ticket cache and resets + * and frees the memory allocated for the environment variables so that + * valgrind output on test suites is cleaner. */ void kerberos_cleanup(void) { char *path; - path = concatpath(getenv("BUILD"), "data/test.cache"); - unlink(path); - free(path); + if (tmpdir_ticket != NULL) { + basprintf(&path, "%s/krb5cc_test", tmpdir_ticket); + unlink(path); + free(path); + test_tmpdir_free(tmpdir_ticket); + tmpdir_ticket = NULL; + } + if (config != NULL) { + if (config->keytab != NULL) { + test_file_path_free(config->keytab); + free(config->principal); + free(config->cache); + } + if (config->userprinc != NULL) { + free(config->userprinc); + free(config->username); + free(config->password); + } + free(config); + config = NULL; + } + if (krb5ccname != NULL) { + putenv((char *) "KRB5CCNAME="); + free(krb5ccname); + krb5ccname = NULL; + } + if (krb5_ktname != NULL) { + putenv((char *) "KRB5_KTNAME="); + free(krb5_ktname); + krb5_ktname = NULL; + } +} + + +/* + * Obtain Kerberos tickets for the principal specified in config/principal + * using the keytab specified in config/keytab, both of which are presumed to + * be in tests in either the build or the source tree. Also sets KRB5_KTNAME + * and KRB5CCNAME. + * + * Returns the contents of config/principal in newly allocated memory or NULL + * if Kerberos tests are apparently not configured. If Kerberos tests are + * configured but something else fails, calls bail. + */ +struct kerberos_config * +kerberos_setup(enum kerberos_needs needs) +{ + char *path; + char buffer[BUFSIZ]; + FILE *file = NULL; + + /* If we were called before, clean up after the previous run. */ + if (config != NULL) + kerberos_cleanup(); + config = bcalloc(1, sizeof(struct kerberos_config)); + + /* + * If we have a config/keytab file, set the KRB5CCNAME and KRB5_KTNAME + * environment variables and obtain initial tickets. + */ + config->keytab = test_file_path("config/keytab"); + if (config->keytab == NULL) { + if (needs == TAP_KRB_NEEDS_KEYTAB || needs == TAP_KRB_NEEDS_BOTH) + skip_all("Kerberos tests not configured"); + } else { + tmpdir_ticket = test_tmpdir(); + basprintf(&config->cache, "%s/krb5cc_test", tmpdir_ticket); + basprintf(&krb5ccname, "KRB5CCNAME=%s/krb5cc_test", tmpdir_ticket); + basprintf(&krb5_ktname, "KRB5_KTNAME=%s", config->keytab); + putenv(krb5ccname); + putenv(krb5_ktname); + kerberos_kinit(); + } + + /* + * If we have a config/password file, read it and fill out the relevant + * members of our config struct. + */ + path = test_file_path("config/password"); + if (path != NULL) + file = fopen(path, "r"); + if (file == NULL) { + if (needs == TAP_KRB_NEEDS_PASSWORD || needs == TAP_KRB_NEEDS_BOTH) + skip_all("Kerberos tests not configured"); + } else { + if (fgets(buffer, sizeof(buffer), file) == NULL) + bail("cannot read %s", path); + if (buffer[strlen(buffer) - 1] != '\n') + bail("no newline in %s", path); + buffer[strlen(buffer) - 1] = '\0'; + config->userprinc = bstrdup(buffer); + if (fgets(buffer, sizeof(buffer), file) == NULL) + bail("cannot read password from %s", path); + fclose(file); + if (buffer[strlen(buffer) - 1] != '\n') + bail("password too long in %s", path); + buffer[strlen(buffer) - 1] = '\0'; + config->password = bstrdup(buffer); + + /* + * Strip the realm from the principal and set realm and username. + * This is not strictly correct; it doesn't cope with escaped @-signs + * or enterprise names. + */ + config->username = bstrdup(config->userprinc); + config->realm = strchr(config->username, '@'); + if (config->realm == NULL) + bail("test principal has no realm"); + *config->realm = '\0'; + config->realm++; + } + if (path != NULL) + test_file_path_free(path); + + /* + * Register the cleanup function as an atexit handler so that the caller + * doesn't have to worry about cleanup. + */ + if (atexit(kerberos_cleanup) != 0) + sysdiag("cannot register cleanup function"); + + /* Return the configuration. */ + return config; +} + + +/* + * Clean up the krb5.conf file generated by kerberos_generate_conf and free + * the memory used to set the environment variable. This doesn't fail if the + * file and variable are already gone, allowing it to be harmlessly run + * multiple times. + * + * Normally called via an atexit handler. + */ +void +kerberos_cleanup_conf(void) +{ + char *path; + + if (tmpdir_conf != NULL) { + basprintf(&path, "%s/krb5.conf", tmpdir_conf); + unlink(path); + free(path); + test_tmpdir_free(tmpdir_conf); + tmpdir_conf = NULL; + } + putenv((char *) "KRB5_CONFIG="); + if (krb5_config != NULL) { + free(krb5_config); + krb5_config = NULL; + } } + + +/* + * Generate a krb5.conf file for testing and set KRB5_CONFIG to point to it. + * The [appdefaults] section will be stripped out and the default realm will + * be set to the realm specified, if not NULL. This will use config/krb5.conf + * in preference, so users can configure the tests by creating that file if + * the system file isn't suitable. + * + * Depends on data/generate-krb5-conf being present in the test suite. + */ +void +kerberos_generate_conf(const char *realm) +{ + char *path; + const char *argv[3]; + + if (tmpdir_conf != NULL) + kerberos_cleanup_conf(); + path = test_file_path("data/generate-krb5-conf"); + if (path == NULL) + bail("cannot find generate-krb5-conf"); + argv[0] = path; + argv[1] = realm; + argv[2] = NULL; + run_setup(argv); + test_file_path_free(path); + tmpdir_conf = test_tmpdir(); + basprintf(&krb5_config, "KRB5_CONFIG=%s/krb5.conf", tmpdir_conf); + putenv(krb5_config); + if (atexit(kerberos_cleanup_conf) != 0) + sysdiag("cannot register cleanup function"); +} + + +/* + * The remaining functions in this file are only available if Kerberos + * libraries are available. + */ +#ifdef HAVE_KERBEROS + + +/* + * Report a Kerberos error and bail out. + */ +void +bail_krb5(krb5_context ctx, krb5_error_code code, const char *format, ...) +{ + const char *k5_msg = NULL; + char *message; + va_list args; + + if (ctx != NULL) + k5_msg = krb5_get_error_message(ctx, code); + va_start(args, format); + bvasprintf(&message, format, args); + va_end(args); + if (k5_msg == NULL) + bail("%s", message); + else + bail("%s: %s", message, k5_msg); +} + + +/* + * Report a Kerberos error as a diagnostic to stderr. + */ +void +diag_krb5(krb5_context ctx, krb5_error_code code, const char *format, ...) +{ + const char *k5_msg = NULL; + char *message; + va_list args; + + if (ctx != NULL) + k5_msg = krb5_get_error_message(ctx, code); + va_start(args, format); + bvasprintf(&message, format, args); + va_end(args); + if (k5_msg == NULL) + diag("%s", message); + else + diag("%s: %s", message, k5_msg); + free(message); + if (k5_msg != NULL) + krb5_free_error_message(ctx, k5_msg); +} + + +/* + * Find the principal of the first entry of a keytab and return it. The + * caller is responsible for freeing the result with krb5_free_principal. + * Exit on error. + */ +krb5_principal +kerberos_keytab_principal(krb5_context ctx, const char *path) +{ + krb5_keytab keytab; + krb5_kt_cursor cursor; + krb5_keytab_entry entry; + krb5_principal princ; + krb5_error_code status; + + status = krb5_kt_resolve(ctx, path, &keytab); + if (status != 0) + bail_krb5(ctx, status, "error opening %s", path); + status = krb5_kt_start_seq_get(ctx, keytab, &cursor); + if (status != 0) + bail_krb5(ctx, status, "error reading %s", path); + status = krb5_kt_next_entry(ctx, keytab, &entry, &cursor); + if (status == 0) { + status = krb5_copy_principal(ctx, entry.principal, &princ); + if (status != 0) + bail_krb5(ctx, status, "error copying principal from %s", path); + krb5_kt_free_entry(ctx, &entry); + } + if (status != 0) + bail("no principal found in keytab file %s", path); + krb5_kt_end_seq_get(ctx, keytab, &cursor); + krb5_kt_close(ctx, keytab); + return princ; +} + +#endif /* HAVE_KERBEROS */ diff --git a/tests/tap/kerberos.h b/tests/tap/kerberos.h index 1c64f70..31b6343 100644 --- a/tests/tap/kerberos.h +++ b/tests/tap/kerberos.h @@ -1,32 +1,125 @@ /* * Utility functions for tests that use Kerberos. * - * Copyright 2006, 2007, 2009 - * Board of Trustees, Leland Stanford Jr. University + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . * - * See LICENSE for licensing terms. + * Written by Russ Allbery + * Copyright 2006, 2007, 2009, 2011, 2012 + * The Board of Trustees of the Leland Stanford Junior University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. */ #ifndef TAP_KERBEROS_H #define TAP_KERBEROS_H 1 #include -#include +#include + +#ifdef HAVE_KERBEROS +# include +#endif + +/* Holds the information parsed from the Kerberos test configuration. */ +struct kerberos_config { + char *keytab; /* Path to the keytab. */ + char *principal; /* Principal whose keys are in the keytab. */ + char *cache; /* Path to the Kerberos ticket cache. */ + char *userprinc; /* The fully-qualified principal. */ + char *username; /* The local (non-realm) part of principal. */ + char *realm; /* The realm part of the principal. */ + char *password; /* The password. */ +}; + +/* + * Whether to skip all tests (by calling skip_all) in kerberos_setup if + * certain configuration information isn't available. + */ +enum kerberos_needs { + TAP_KRB_NEEDS_NONE, + TAP_KRB_NEEDS_KEYTAB, + TAP_KRB_NEEDS_PASSWORD, + TAP_KRB_NEEDS_BOTH +}; BEGIN_DECLS /* - * Set up Kerberos, returning the test principal in newly allocated memory if - * we were successful. If there is no principal in tests/data/test.principal - * or no keytab in tests/data/test.keytab, return NULL. Otherwise, on - * failure, calls bail(). + * Set up Kerberos, returning the test configuration information. This + * obtains Kerberos tickets from config/keytab, if one is present, and stores + * them in a Kerberos ticket cache, sets KRB5_KTNAME and KRB5CCNAME. It also + * loads the principal and password from config/password, if it exists, and + * stores the principal, password, username, and realm in the returned struct. + * + * If there is no config/keytab file, KRB5_KTNAME and KRB5CCNAME won't be set + * and the keytab field will be NULL. If there is no config/password file, + * the principal field will be NULL. If the files exist but loading them + * fails, or authentication fails, kerberos_setup calls bail. + * + * kerberos_cleanup will be set up to run from an atexit handler. This means + * that any child processes that should not remove the Kerberos ticket cache + * should call _exit instead of exit. The principal will be automatically + * freed when kerberos_cleanup is called or if kerberos_setup is called again. + * The caller doesn't need to worry about it. */ -char *kerberos_setup(void) +struct kerberos_config *kerberos_setup(enum kerberos_needs) __attribute__((__malloc__)); - -/* Clean up at the end of a test. */ void kerberos_cleanup(void); +/* + * Generate a krb5.conf file for testing and set KRB5_CONFIG to point to it. + * The [appdefaults] section will be stripped out and the default realm will + * be set to the realm specified, if not NULL. This will use config/krb5.conf + * in preference, so users can configure the tests by creating that file if + * the system file isn't suitable. + * + * Depends on data/generate-krb5-conf being present in the test suite. + * + * kerberos_cleanup_conf will clean up after this function, but usually + * doesn't need to be called directly since it's registered as an atexit + * handler. + */ +void kerberos_generate_conf(const char *realm); +void kerberos_cleanup_conf(void); + +/* Thes interfaces are only available with native Kerberos support. */ +#ifdef HAVE_KERBEROS + +/* Bail out with an error, appending the Kerberos error message. */ +void bail_krb5(krb5_context, krb5_error_code, const char *format, ...) + __attribute__((__noreturn__, __nonnull__, __format__(printf, 3, 4))); + +/* Report a diagnostic with Kerberos error to stderr prefixed with #. */ +void diag_krb5(krb5_context, krb5_error_code, const char *format, ...) + __attribute__((__nonnull__, __format__(printf, 3, 4))); + +/* + * Given a Kerberos context and the path to a keytab, retrieve the principal + * for the first entry in the keytab and return it. Calls bail on failure. + * The returned principal should be freed with krb5_free_principal. + */ +krb5_principal kerberos_keytab_principal(krb5_context, const char *path) + __attribute__((__nonnull__)); + +#endif /* HAVE_KERBEROS */ + END_DECLS #endif /* !TAP_MESSAGES_H */ diff --git a/tests/tap/kerberos.sh b/tests/tap/kerberos.sh index 904cae5..d2f174d 100644 --- a/tests/tap/kerberos.sh +++ b/tests/tap/kerberos.sh @@ -1,30 +1,61 @@ # Shell function library to initialize Kerberos credentials # +# Note that while many of the functions in this library could benefit from +# using "local" to avoid possibly hammering global variables, Solaris /bin/sh +# doesn't support local and this library aspires to be portable to Solaris +# Bourne shell. Instead, all private variables are prefixed with "tap_". +# +# The canonical version of this file is maintained in the rra-c-util package, +# which can be found at . +# # Written by Russ Allbery -# Copyright 2009, 2010 Board of Trustees, Leland Stanford Jr. University +# Copyright 2009, 2010, 2011, 2012 +# The Board of Trustees of the Leland Stanford Junior University +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. # -# See LICENSE for licensing terms. +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. + +# We use test_tmpdir. +. "${SOURCE}/tap/libtap.sh" # Set up Kerberos, including the ticket cache environment variable. Bail out # if not successful, return 0 if successful, and return 1 if Kerberos is not # configured. Sets the global principal variable to the principal to use. kerberos_setup () { - local keytab - keytab=`test_file_path data/test.keytab` - principal=`test_file_path data/test.principal` + tap_keytab=`test_file_path config/keytab` + principal=`test_file_path config/principal` principal=`cat "$principal" 2>/dev/null` - if [ -z "$keytab" ] || [ -z "$principal" ] ; then + if [ -z "$tap_keytab" ] || [ -z "$principal" ] ; then return 1 fi - KRB5CCNAME="$BUILD/data/test.cache"; export KRB5CCNAME - kinit -k -t "$keytab" "$principal" >/dev/null /dev/null /dev/null /dev/null /dev/null /dev/null /dev/null ktutil-tmp 2>/dev/null ; then + tap_tmp=`test_tmpdir` + if klist -keK "$1" > "$tap_tmp"/ktutil-tmp 2>/dev/null ; then : else - ktutil -k "$1" list --keys > ktutil-tmp < /dev/null 2>/dev/null + ktutil -k "$1" list --keys > "$tap_tmp"/ktutil-tmp /dev/null fi - sed -e '/Keytab name:/d' -e "/^[^ ]*:/d" ktutil-tmp > "$2" - rm -f ktutil-tmp + sed -e '/Keytab name:/d' -e "/^[^ ]*:/d" "$tap_tmp"/ktutil-tmp > "$2" + rm -f "$tap_tmp"/ktutil-tmp } diff --git a/tests/tap/libtap.sh b/tests/tap/libtap.sh index a9b46d4..f9347d8 100644 --- a/tests/tap/libtap.sh +++ b/tests/tap/libtap.sh @@ -1,10 +1,36 @@ # Shell function library for test cases. # +# Note that while many of the functions in this library could benefit from +# using "local" to avoid possibly hammering global variables, Solaris /bin/sh +# doesn't support local and this library aspires to be portable to Solaris +# Bourne shell. Instead, all private variables are prefixed with "tap_". +# +# This file provides a TAP-compatible shell function library useful for +# writing test cases. It is part of C TAP Harness, which can be found at +# . +# # Written by Russ Allbery -# Copyright 2009, 2010 Russ Allbery -# Copyright 2006, 2007, 2008 Board of Trustees, Leland Stanford Jr. University +# Copyright 2009, 2010, 2011, 2012 Russ Allbery +# Copyright 2006, 2007, 2008 +# The Board of Trustees of the Leland Stanford Junior University +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: # -# See LICENSE for licensing terms. +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. # Print out the number of test cases we expect to run. plan () { @@ -25,33 +51,35 @@ plan_lazy () { # Report the test status on exit. finish () { - local highest looks - highest=`expr "$count" - 1` + tap_highest=`expr "$count" - 1` if [ "$planned" = 0 ] ; then - echo "1..$highest" - planned="$highest" + echo "1..$tap_highest" + planned="$tap_highest" fi - looks='# Looks like you' + tap_looks='# Looks like you' if [ "$planned" -gt 0 ] ; then - if [ "$planned" -gt "$highest" ] ; then + if [ "$planned" -gt "$tap_highest" ] ; then if [ "$planned" -gt 1 ] ; then - echo "$looks planned $planned tests but only ran $highest" + echo "$tap_looks planned $planned tests but only ran" \ + "$tap_highest" else - echo "$looks planned $planned test but only ran $highest" + echo "$tap_looks planned $planned test but only ran" \ + "$tap_highest" fi - elif [ "$planned" -lt "$highest" ] ; then - local extra - extra=`expr "$highest" - "$planned"` + elif [ "$planned" -lt "$tap_highest" ] ; then + tap_extra=`expr "$tap_highest" - "$planned"` if [ "$planned" -gt 1 ] ; then - echo "$looks planned $planned tests but ran $extra extra" + echo "$tap_looks planned $planned tests but ran" \ + "$tap_extra extra" else - echo "$looks planned $planned test but ran $extra extra" + echo "$tap_looks planned $planned test but ran" \ + "$tap_extra extra" fi elif [ "$failed" -gt 0 ] ; then if [ "$failed" -gt 1 ] ; then - echo "$looks failed $failed tests of $planned" + echo "$tap_looks failed $failed tests of $planned" else - echo "$looks failed $failed test of $planned" + echo "$tap_looks failed $failed test of $planned" fi elif [ "$planned" -gt 1 ] ; then echo "# All $planned tests successful or skipped" @@ -63,10 +91,9 @@ finish () { # Skip the entire test suite. Should be run instead of plan. skip_all () { - local desc - desc="$1" - if [ -n "$desc" ] ; then - echo "1..0 # skip $desc" + tap_desc="$1" + if [ -n "$tap_desc" ] ; then + echo "1..0 # skip $tap_desc" else echo "1..0 # skip" fi @@ -77,16 +104,15 @@ skip_all () { # command is successful, false otherwise. The count starts at 1 and is # updated each time ok is printed. ok () { - local desc - desc="$1" - if [ -n "$desc" ] ; then - desc=" - $desc" + tap_desc="$1" + if [ -n "$tap_desc" ] ; then + tap_desc=" - $tap_desc" fi shift if "$@" ; then - echo ok $count$desc + echo ok "$count$tap_desc" else - echo not ok $count$desc + echo not ok "$count$tap_desc" failed=`expr $failed + 1` fi count=`expr $count + 1` @@ -101,58 +127,80 @@ skip () { # Report the same status on a whole set of tests. Takes the count of tests, # the description, and then the command to run to determine the status. ok_block () { - local end i desc - i=$count - end=`expr $count + $1` - shift - desc="$1" + tap_i=$count + tap_end=`expr $count + $1` shift - while [ "$i" -lt "$end" ] ; do - ok "$desc" "$@" - i=`expr $i + 1` + while [ "$tap_i" -lt "$tap_end" ] ; do + ok "$@" + tap_i=`expr $tap_i + 1` done } # Skip a whole set of tests. Takes the count and then the reason for skipping # the test. skip_block () { - local i end - i=$count - end=`expr $count + $1` + tap_i=$count + tap_end=`expr $count + $1` shift - while [ "$i" -lt "$end" ] ; do + while [ "$tap_i" -lt "$tap_end" ] ; do skip "$@" - i=`expr $i + 1` + tap_i=`expr $tap_i + 1` done } +# Portable variant of printf '%s\n' "$*". In the majority of cases, this +# function is slower than printf, because the latter is often implemented +# as a builtin command. The value of the variable IFS is ignored. +# +# This macro must not be called via backticks inside double quotes, since this +# will result in bizarre escaping behavior and lots of extra backslashes on +# Solaris. +puts () { + cat << EOH +$@ +EOH +} + # Run a program expected to succeed, and print ok if it does and produces the # correct output. Takes the description, expected exit status, the expected -# output, the command to run, and then any arguments for that command. Strip -# a colon and everything after it off the output if the expected status is -# non-zero, since this is probably a system-specific error message. +# output, the command to run, and then any arguments for that command. +# Standard output and standard error are combined when analyzing the output of +# the command. +# +# If the command may contain system-specific error messages in its output, +# add strip_colon_error before the command to post-process its output. ok_program () { - local desc w_status w_output output status - desc="$1" + tap_desc="$1" shift - w_status="$1" + tap_w_status="$1" shift - w_output="$1" + tap_w_output="$1" shift - output=`"$@" 2>&1` - status=$? - if [ "$w_status" -ne 0 ] ; then - output=`echo "$output" | sed 's/^\([^:]* [^:]*\):.*/\1/'` - fi - if [ $status = $w_status ] && [ x"$output" = x"$w_output" ] ; then - ok "$desc" true + tap_output=`"$@" 2>&1` + tap_status=$? + if [ $tap_status = $tap_w_status ] \ + && [ x"$tap_output" = x"$tap_w_output" ] ; then + ok "$tap_desc" true else - echo "# saw: ($status) $output" - echo "# not: ($w_status) $w_output" - ok "$desc" false + echo "# saw: ($tap_status) $tap_output" + echo "# not: ($tap_w_status) $tap_w_output" + ok "$tap_desc" false fi } +# Strip a colon and everything after it off the output of a command, as long +# as that colon comes after at least one whitespace character. (This is done +# to avoid stripping the name of the program from the start of an error +# message.) This is used to remove system-specific error messages (coming +# from strerror, for example). +strip_colon_error() { + tap_output=`"$@" 2>&1` + tap_status=$? + tap_output=`puts "$tap_output" | sed 's/^\([^ ]* [^:]*\):.*/\1/'` + puts "$tap_output" + return $tap_status +} + # Bail out with an error message. bail () { echo 'Bail out!' "$@" @@ -167,12 +215,32 @@ diag () { # 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. +# +# This macro uses puts, so don't run it using backticks inside double quotes +# or bizarre quoting behavior will happen with Solaris sh. test_file_path () { - if [ -f "$BUILD/$1" ] ; then - echo "$BUILD/$1" - elif [ -f "$SOURCE/$1" ] ; then - echo "$SOURCE/$1" + if [ -n "$BUILD" ] && [ -f "$BUILD/$1" ] ; then + puts "$BUILD/$1" + elif [ -n "$SOURCE" ] && [ -f "$SOURCE/$1" ] ; then + puts "$SOURCE/$1" else echo '' fi } + +# Create $BUILD/tmp for use by tests for storing temporary files and return +# the path (via standard output). +# +# This macro uses puts, so don't run it using backticks inside double quotes +# or bizarre quoting behavior will happen with Solaris sh. +test_tmpdir () { + if [ -z "$BUILD" ] ; then + tap_tmpdir="./tmp" + else + tap_tmpdir="$BUILD"/tmp + fi + if [ ! -d "$tap_tmpdir" ] ; then + mkdir "$tap_tmpdir" || bail "Error creating $tap_tmpdir" + fi + puts "$tap_tmpdir" +} diff --git a/tests/tap/macros.h b/tests/tap/macros.h new file mode 100644 index 0000000..33fee42 --- /dev/null +++ b/tests/tap/macros.h @@ -0,0 +1,88 @@ +/* + * Helpful macros for TAP header files. + * + * This is not, strictly speaking, related to TAP, but any TAP add-on is + * probably going to need these macros, so define them in one place so that + * everyone can pull them in. + * + * This file is part of C TAP Harness. The current version plus supporting + * documentation is at . + * + * Copyright 2008, 2012 Russ Allbery + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef TAP_MACROS_H +#define TAP_MACROS_H 1 + +/* + * __attribute__ is available in gcc 2.5 and later, but only with gcc 2.7 + * could you use the __format__ form of the attributes, which is what we use + * (to avoid confusion with other macros), and only with gcc 2.96 can you use + * the attribute __malloc__. 2.96 is very old, so don't bother trying to get + * the other attributes to work with GCC versions between 2.7 and 2.96. + */ +#ifndef __attribute__ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 96) +# define __attribute__(spec) /* empty */ +# endif +#endif + +/* + * We use __alloc_size__, but it was only available in fairly recent versions + * of GCC. Suppress warnings about the unknown attribute if GCC is too old. + * We know that we're GCC at this point, so we can use the GCC variadic macro + * extension, which will still work with versions of GCC too old to have C99 + * variadic macro support. + */ +#if !defined(__attribute__) && !defined(__alloc_size__) +# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3) +# define __alloc_size__(spec, args...) /* empty */ +# endif +#endif + +/* + * LLVM and Clang pretend to be GCC but don't support all of the __attribute__ + * settings that GCC does. For them, suppress warnings about unknown + * attributes on declarations. This unfortunately will affect the entire + * compilation context, but there's no push and pop available. + */ +#if !defined(__attribute__) && (defined(__llvm__) || defined(__clang__)) +# pragma GCC diagnostic ignored "-Wattributes" +#endif + +/* Used for unused parameters to silence gcc warnings. */ +#define UNUSED __attribute__((__unused__)) + +/* + * BEGIN_DECLS is used at the beginning of declarations so that C++ + * compilers don't mangle their names. END_DECLS is used at the end. + */ +#undef BEGIN_DECLS +#undef END_DECLS +#ifdef __cplusplus +# define BEGIN_DECLS extern "C" { +# define END_DECLS } +#else +# define BEGIN_DECLS /* empty */ +# define END_DECLS /* empty */ +#endif + +#endif /* TAP_MACROS_H */ diff --git a/tests/tap/messages.c b/tests/tap/messages.c index 3bb9a1a..abc2c49 100644 --- a/tests/tap/messages.c +++ b/tests/tap/messages.c @@ -5,24 +5,39 @@ * into a buffer that can be inspected later, allowing testing of error * handling. * - * Copyright 2006, 2007, 2009 - * 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, - * 2002, 2003 by The Internet Software Consortium and Rich Salz + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . * - * See LICENSE for licensing terms. + * Copyright 2002, 2004, 2005 Russ Allbery + * Copyright 2006, 2007, 2009, 2012 + * The Board of Trustees of the Leland Stanford Junior University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. */ #include #include +#include #include -#include -#include +#include #include -#include /* A global buffer into which message_log_buffer stores error messages. */ char *errors = NULL; @@ -33,18 +48,18 @@ char *errors = NULL; * error_capture. */ static void -message_log_buffer(int len, const char *fmt, va_list args, int error UNUSED) +message_log_buffer(int len UNUSED, const char *fmt, va_list args, + int error UNUSED) { char *message; - message = xmalloc(len + 1); - vsnprintf(message, len + 1, fmt, args); - if (errors == NULL) { - errors = concat(message, "\n", (char *) 0); - } else { + bvasprintf(&message, fmt, args); + if (errors == NULL) + basprintf(&errors, "%s\n", message); + else { char *new_errors; - new_errors = concat(errors, message, "\n", (char *) 0); + basprintf(&new_errors, "%s%s\n", errors, message); free(errors); errors = new_errors; } diff --git a/tests/tap/messages.h b/tests/tap/messages.h index 2b9a7db..0544f2d 100644 --- a/tests/tap/messages.h +++ b/tests/tap/messages.h @@ -1,21 +1,37 @@ /* * Utility functions to test message handling. * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at . + * + * Copyright 2002 Russ Allbery * Copyright 2006, 2007, 2009 - * 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, - * 2002, 2003 by The Internet Software Consortium and Rich Salz + * The Board of Trustees of the Leland Stanford Junior University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * - * See LICENSE for licensing terms. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. */ #ifndef TAP_MESSAGES_H #define TAP_MESSAGES_H 1 #include -#include +#include /* A global buffer into which errors_capture stores errors. */ extern char *errors; diff --git a/tests/tap/perl/Test/RRA.pm b/tests/tap/perl/Test/RRA.pm new file mode 100644 index 0000000..2d119f4 --- /dev/null +++ b/tests/tap/perl/Test/RRA.pm @@ -0,0 +1,222 @@ +# Helper functions for test programs written in Perl. +# +# This module provides a collection of helper functions used by test programs +# written in Perl. This is a general collection of functions that can be used +# by both C packages with Automake and by stand-alone Perl modules. See +# Test::RRA::Automake for additional functions specifically for C Automake +# distributions. +# +# The canonical version of this file is maintained in the rra-c-util package, +# which can be found at . +# +# Written by Russ Allbery +# Copyright 2013 +# The Board of Trustees of the Leland Stanford Junior University +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. + +package Test::RRA; + +use 5.006; +use strict; +use warnings; + +use Exporter; +use Test::More; + +# For Perl 5.006 compatibility. +## no critic (ClassHierarchies::ProhibitExplicitISA) + +# Declare variables that should be set in BEGIN for robustness. +our (@EXPORT_OK, @ISA, $VERSION); + +# Set $VERSION and everything export-related in a BEGIN block for robustness +# against circular module loading (not that we load any modules, but +# consistency is good). +BEGIN { + @ISA = qw(Exporter); + @EXPORT_OK = qw(skip_unless_maintainer use_prereq); + + # This version should match the corresponding rra-c-util release, but with + # two digits for the minor version, including a leading zero if necessary, + # so that it will sort properly. + $VERSION = '4.08'; +} + +# Skip this test unless maintainer tests are requested. Takes a short +# description of what tests this script would perform, which is used in the +# skip message. Calls plan skip_all, which will terminate the program. +# +# $description - Short description of the tests +# +# Returns: undef +sub skip_unless_maintainer { + my ($description) = @_; + if (!$ENV{RRA_MAINTAINER_TESTS}) { + plan skip_all => "$description only run for maintainer"; + } + return; +} + +# Attempt to load a module and skip the test if the module could not be +# loaded. If the module could be loaded, call its import function manually. +# If the module could not be loaded, calls plan skip_all, which will terminate +# the program. +# +# The special logic here is based on Test::More and is required to get the +# imports to happen in the caller's namespace. +# +# $module - Name of the module to load +# @imports - Any arguments to import, possibly including a version +# +# Returns: undef +sub use_prereq { + my ($module, @imports) = @_; + + # If the first import looks like a version, pass it as a bare string. + my $version = q{}; + if (@imports >= 1 && $imports[0] =~ m{ \A \d+ (?: [.]\d+ )* \z }xms) { + $version = shift(@imports); + } + + # Get caller information to put imports in the correct package. + my ($package) = caller; + + # Do the import with eval, and try to isolate it from the surrounding + # context as much as possible. Based heavily on Test::More::_eval. + ## no critic (BuiltinFunctions::ProhibitStringyEval) + ## no critic (ValuesAndExpressions::ProhibitImplicitNewlines) + my ($result, $error, $sigdie); + { + local $@ = undef; + local $! = undef; + local $SIG{__DIE__} = undef; + $result = eval qq{ + package $package; + use $module $version \@imports; + 1; + }; + $error = $@; + $sigdie = $SIG{__DIE__} || undef; + } + + # If the use failed for any reason, skip the test. + if (!$result || $error) { + plan skip_all => "$module required for test"; + } + + # If the module set $SIG{__DIE__}, we cleared that via local. Restore it. + ## no critic (Variables::RequireLocalizedPunctuationVars) + if (defined($sigdie)) { + $SIG{__DIE__} = $sigdie; + } + return; +} + +1; +__END__ + +=for stopwords +Allbery Allbery's DESC bareword sublicense MERCHANTABILITY NONINFRINGEMENT +rra-c-util + +=head1 NAME + +Test::RRA - Support functions for Perl tests + +=head1 SYNOPSIS + + use Test::RRA qw(skip_unless_maintainer use_prereq); + + # Skip this test unless maintainer tests are requested. + skip_unless_maintainer('Coding style tests'); + + # Load modules, skipping the test if they're not available. + use_prereq('File::Slurp'); + use_prereq('Test::Script::Run', '0.04'); + +=head1 DESCRIPTION + +This module collects utility functions that are useful for Perl test +scripts. It assumes Russ Allbery's Perl module layout and test +conventions and will only be useful for other people if they use the +same conventions. + +=head1 FUNCTIONS + +None of these functions are imported by default. The ones used by a +script should be explicitly imported. + +=over 4 + +=item skip_unless_maintainer(DESC) + +Checks whether RRA_MAINTAINER_TESTS is set in the environment and skips +the whole test (by calling C from Test::More) if it is not. +DESC is a description of the tests being skipped. A space and C will be appended to it and used as the skip reason. + +=item use_prereq(MODULE[, VERSION][, IMPORT ...]) + +Attempts to load MODULE with the given VERSION and import arguments. If +this fails for any reason, the test will be skipped (by calling C from Test::More) with a skip reason saying that MODULE is +required for the test. + +VERSION will be passed to C as a version bareword if it looks like a +version number. The remaining IMPORT arguments will be passed as the +value of an array. + +=back + +=head1 AUTHOR + +Russ Allbery + +=head1 COPYRIGHT AND LICENSE + +Copyright 2013 The Board of Trustees of the Leland Stanford Junior +University. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +=head1 SEE ALSO + +Test::More(3), Test::RRA::Automake(3), Test::RRA::Config(3) + +This module is maintained in the rra-c-util package. The current version +is available from L. + +=cut diff --git a/tests/tap/perl/Test/RRA/Automake.pm b/tests/tap/perl/Test/RRA/Automake.pm new file mode 100644 index 0000000..2aadb6a --- /dev/null +++ b/tests/tap/perl/Test/RRA/Automake.pm @@ -0,0 +1,362 @@ +# Helper functions for Perl test programs in Automake distributions. +# +# This module provides a collection of helper functions used by test programs +# written in Perl and included in C source distributions that use Automake. +# They embed knowledge of how I lay out my source trees and test suites with +# Autoconf and Automake. They may be usable by others, but doing so will +# require closely following the conventions implemented by the rra-c-util +# utility collection. +# +# All the functions here assume that BUILD and SOURCE are set in the +# environment. This is normally done via the C TAP Harness runtests wrapper. +# +# The canonical version of this file is maintained in the rra-c-util package, +# which can be found at . +# +# Written by Russ Allbery +# Copyright 2013 +# The Board of Trustees of the Leland Stanford Junior University +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. + +package Test::RRA::Automake; + +use 5.006; +use strict; +use warnings; + +# For Perl 5.006 compatibility. +## no critic (ClassHierarchies::ProhibitExplicitISA) + +use Exporter; +use File::Spec; +use Test::More; +use Test::RRA::Config qw($LIBRARY_PATH); + +# Used below for use lib calls. +my ($PERL_BLIB_ARCH, $PERL_BLIB_LIB); + +# Determine the path to the build tree of any embedded Perl module package in +# this source package. We do this in a BEGIN block because we're going to use +# the results in a use lib command below. +BEGIN { + $PERL_BLIB_ARCH = File::Spec->catdir(qw(perl blib arch)); + $PERL_BLIB_LIB = File::Spec->catdir(qw(perl blib lib)); + + # If BUILD is set, we can come up with better values. + if (defined($ENV{BUILD})) { + my ($vol, $dirs) = File::Spec->splitpath($ENV{BUILD}, 1); + my @dirs = File::Spec->splitdir($dirs); + pop(@dirs); + $PERL_BLIB_ARCH = File::Spec->catdir(@dirs, qw(perl blib arch)); + $PERL_BLIB_LIB = File::Spec->catdir(@dirs, qw(perl blib lib)); + } +} + +# Prefer the modules built as part of our source package. Otherwise, we may +# not find Perl modules while testing, or find the wrong versions. +use lib $PERL_BLIB_ARCH; +use lib $PERL_BLIB_LIB; + +# Declare variables that should be set in BEGIN for robustness. +our (@EXPORT_OK, @ISA, $VERSION); + +# Set $VERSION and everything export-related in a BEGIN block for robustness +# against circular module loading (not that we load any modules, but +# consistency is good). +BEGIN { + @ISA = qw(Exporter); + @EXPORT_OK = qw(automake_setup perl_dirs test_file_path); + + # This version should match the corresponding rra-c-util release, but with + # two digits for the minor version, including a leading zero if necessary, + # so that it will sort properly. + $VERSION = '4.08'; +} + +# Perl directories to skip globally for perl_dirs. We ignore the perl +# directory if it exists since, in my packages, it is treated as a Perl module +# distribution and has its own standalone test suite. +my @GLOBAL_SKIP = qw(.git perl); + +# Perform initial test setup for running a Perl test in an Automake package. +# This verifies that BUILD and SOURCE are set and then changes directory to +# the SOURCE directory by default. Sets LD_LIBRARY_PATH if the $LIBRARY_PATH +# configuration option is set. Calls BAIL_OUT if BUILD or SOURCE are missing +# or if anything else fails. +# +# $args_ref - Reference to a hash of arguments to configure behavior: +# chdir_build - If set to a true value, changes to BUILD instead of SOURCE +# +# Returns: undef +sub automake_setup { + my ($args_ref) = @_; + + # Bail if BUILD or SOURCE are not set. + if (!$ENV{BUILD}) { + BAIL_OUT('BUILD not defined (run under runtests)'); + } + if (!$ENV{SOURCE}) { + BAIL_OUT('SOURCE not defined (run under runtests)'); + } + + # BUILD or SOURCE will be the test directory. Change to the parent. + my $start = $args_ref->{chdir_build} ? $ENV{BUILD} : $ENV{SOURCE}; + my ($vol, $dirs) = File::Spec->splitpath($start, 1); + my @dirs = File::Spec->splitdir($dirs); + pop(@dirs); + if ($dirs[-1] eq File::Spec->updir) { + pop(@dirs); + pop(@dirs); + } + my $root = File::Spec->catpath($vol, File::Spec->catdir(@dirs), q{}); + chdir($root) or BAIL_OUT("cannot chdir to $root: $!"); + + # If BUILD is a subdirectory of SOURCE, add it to the global ignore list. + my ($buildvol, $builddirs) = File::Spec->splitpath($ENV{BUILD}, 1); + my @builddirs = File::Spec->splitdir($builddirs); + pop(@builddirs); + if ($buildvol eq $vol && @builddirs == @dirs + 1) { + while (@dirs && $builddirs[0] eq $dirs[0]) { + shift(@builddirs); + shift(@dirs); + } + if (@builddirs == 1) { + push(@GLOBAL_SKIP, $builddirs[0]); + } + } + + # Set LD_LIBRARY_PATH if the $LIBRARY_PATH configuration option is set. + ## no critic (Variables::RequireLocalizedPunctuationVars) + if (defined($LIBRARY_PATH)) { + @builddirs = File::Spec->splitdir($builddirs); + pop(@builddirs); + my $libdir = File::Spec->catdir(@builddirs, $LIBRARY_PATH); + my $path = File::Spec->catpath($buildvol, $libdir, q{}); + if (-d "$path/.libs") { + $path .= '/.libs'; + } + if ($ENV{LD_LIBRARY_PATH}) { + $ENV{LD_LIBRARY_PATH} .= ":$path"; + } else { + $ENV{LD_LIBRARY_PATH} = $path; + } + } + return; +} + +# Returns a list of directories that may contain Perl scripts and that should +# be passed to Perl test infrastructure that expects a list of directories to +# recursively check. The list will be all eligible top-level directories in +# the package except for the tests directory, which is broken out to one +# additional level. Calls BAIL_OUT on any problems +# +# $args_ref - Reference to a hash of arguments to configure behavior: +# skip - A reference to an array of directories to skip +# +# Returns: List of directories possibly containing Perl scripts to test +sub perl_dirs { + my ($args_ref) = @_; + + # Add the global skip list. + my @skip = $args_ref->{skip} ? @{ $args_ref->{skip} } : (); + push(@skip, @GLOBAL_SKIP); + + # Separate directories to skip under tests from top-level directories. + my @skip_tests = grep { m{ \A tests/ }xms } @skip; + @skip = grep { !m{ \A tests }xms } @skip; + for my $skip_dir (@skip_tests) { + $skip_dir =~ s{ \A tests/ }{}xms; + } + + # Convert the skip lists into hashes for convenience. + my %skip = map { $_ => 1 } @skip, 'tests'; + my %skip_tests = map { $_ => 1 } @skip_tests; + + # Build the list of top-level directories to test. + opendir(my $rootdir, q{.}) or BAIL_OUT("cannot open .: $!"); + my @dirs = grep { -d $_ && !$skip{$_} } readdir($rootdir); + closedir($rootdir); + @dirs = File::Spec->no_upwards(@dirs); + + # Add the list of subdirectories of the tests directory. + if (-d 'tests') { + opendir(my $testsdir, q{tests}) or BAIL_OUT("cannot open tests: $!"); + + # Skip if found in %skip_tests or if not a directory. + my $is_skipped = sub { + my ($dir) = @_; + return 1 if $skip_tests{$dir}; + $dir = File::Spec->catdir('tests', $dir); + return -d $dir ? 0 : 1; + }; + + # Build the filtered list of subdirectories of tests. + my @test_dirs = grep { !$is_skipped->($_) } readdir($testsdir); + closedir($testsdir); + @test_dirs = File::Spec->no_upwards(@test_dirs); + + # Add the tests directory to the start of the directory name. + push(@dirs, map { File::Spec->catdir('tests', $_) } @test_dirs); + } + return @dirs; +} + +# Find a configuration file for the test suite. Searches relative to BUILD +# first and then SOURCE and returns whichever is found first. Calls BAIL_OUT +# if the file could not be found. +# +# $file - Partial path to the file +# +# Returns: Full path to the file +sub test_file_path { + my ($file) = @_; + BASE: + for my $base ($ENV{BUILD}, $ENV{SOURCE}) { + next if !defined($base); + if (-f "$base/$file") { + return "$base/$file"; + } + } + BAIL_OUT("cannot find $file"); + return; +} + +1; +__END__ + +=for stopwords +Allbery Automake Automake-aware Automake-based rra-c-util ARGS +subdirectories sublicense MERCHANTABILITY NONINFRINGEMENT + +=head1 NAME + +Test::RRA::Automake - Automake-aware support functions for Perl tests + +=head1 SYNOPSIS + + use Test::RRA::Automake qw(automake_setup perl_dirs test_file_path); + automake_setup({ chdir_build => 1 }); + + # Paths to directories that may contain Perl scripts. + my @dirs = perl_dirs({ skip => [qw(lib)] }); + + # Configuration for Kerberos tests. + my $keytab = test_file_path('config/keytab'); + +=head1 DESCRIPTION + +This module collects utility functions that are useful for test scripts +written in Perl and included in a C Automake-based package. They assume +the layout of a package that uses rra-c-util and C TAP Harness for the +test structure. + +Loading this module will also add the directories C and +C to the Perl library search path, relative to BUILD if +that environment variable is set. This is harmless for C Automake +projects that don't contain an embedded Perl module, and for those +projects that do, this will allow subsequent C calls to find modules +that are built as part of the package build process. + +The automake_setup() function should be called before calling any other +functions provided by this module. + +=head1 FUNCTIONS + +None of these functions are imported by default. The ones used by a +script should be explicitly imported. On failure, all of these functions +call BAIL_OUT (from Test::More). + +=over 4 + +=item automake_setup([ARGS]) + +Verifies that the BUILD and SOURCE environment variables are set and +then changes directory to the top of the source tree (which is one +directory up from the SOURCE path, since SOURCE points to the top of +the tests directory). + +If ARGS is given, it should be a reference to a hash of configuration +options. Only one option is supported: C. If it is set +to a true value, automake_setup() changes directories to the top of +the build tree instead. + +=item perl_dirs([ARGS]) + +Returns a list of directories that may contain Perl scripts that should be +tested by test scripts that test all Perl in the source tree (such as +syntax or coding style checks). The paths will be simple directory names +relative to the current directory or two-part directory names under the +F directory. (Directories under F are broken out separately +since it's common to want to apply different policies to different +subdirectories of F.) + +If ARGS is given, it should be a reference to a hash of configuration +options. Only one option is supported: C, whose value should be a +reference to an array of additional top-level directories or directories +starting with C that should be skipped. + +=item test_file_path(FILE) + +Given FILE, which should be a relative path, locates that file relative to +the test directory in either the source or build tree. FILE will be +checked for relative to the environment variable BUILD first, and then +relative to SOURCE. test_file_path() returns the full path to FILE or +calls BAIL_OUT if FILE could not be found. + +=back + +=head1 AUTHOR + +Russ Allbery + +=head1 COPYRIGHT AND LICENSE + +Copyright 2013 The Board of Trustees of the Leland Stanford Junior +University. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +=head1 SEE ALSO + +Test::More(3), Test::RRA(3), Test::RRA::Config(3) + +The C TAP Harness test driver and libraries for TAP-based C testing are +available from L. + +This module is maintained in the rra-c-util package. The current version +is available from L. + +=cut diff --git a/tests/tap/perl/Test/RRA/Config.pm b/tests/tap/perl/Test/RRA/Config.pm new file mode 100644 index 0000000..0091b26 --- /dev/null +++ b/tests/tap/perl/Test/RRA/Config.pm @@ -0,0 +1,200 @@ +# Configuration for Perl test cases. +# +# In order to reuse the same Perl test cases in multiple packages, I use a +# configuration file to store some package-specific data. This module loads +# that configuration and provides the namespace for the configuration +# settings. +# +# The canonical version of this file is maintained in the rra-c-util package, +# which can be found at . + +package Test::RRA::Config; + +use 5.006; +use strict; +use warnings; + +# For Perl 5.006 compatibility. +## no critic (ClassHierarchies::ProhibitExplicitISA) + +use Exporter; +use Test::More; + +# Declare variables that should be set in BEGIN for robustness. +our (@EXPORT_OK, @ISA, $VERSION); + +# Set $VERSION and everything export-related in a BEGIN block for robustness +# against circular module loading (not that we load any modules, but +# consistency is good). +BEGIN { + @ISA = qw(Exporter); + @EXPORT_OK = qw( + $COVERAGE_LEVEL @COVERAGE_SKIP_TESTS @CRITIC_IGNORE $LIBRARY_PATH + $MINIMUM_VERSION %MINIMUM_VERSION @POD_COVERAGE_EXCLUDE + ); + + # This version should match the corresponding rra-c-util release, but with + # two digits for the minor version, including a leading zero if necessary, + # so that it will sort properly. + $VERSION = '4.08'; +} + +# If BUILD or SOURCE are set in the environment, look for data/perl.conf under +# those paths for a C Automake package. Otherwise, look in t/data/perl.conf +# for a standalone Perl module. Don't use Test::RRA::Automake since it may +# not exist. +our $PATH; +for my $base ($ENV{BUILD}, $ENV{SOURCE}, 't') { + next if !defined($base); + my $path = "$base/data/perl.conf"; + if (-r $path) { + $PATH = $path; + last; + } +} +if (!defined($PATH)) { + BAIL_OUT('cannot find data/perl.conf'); +} + +# Pre-declare all of our variables and set any defaults. +our $COVERAGE_LEVEL = 100; +our @COVERAGE_SKIP_TESTS; +our @CRITIC_IGNORE; +our $LIBRARY_PATH; +our $MINIMUM_VERSION = '5.008'; +our %MINIMUM_VERSION; +our @POD_COVERAGE_EXCLUDE; + +# Load the configuration. +if (!do($PATH)) { + my $error = $@ || $! || 'loading file did not return true'; + BAIL_OUT("cannot load data/perl.conf: $error"); +} + +1; +__END__ + +=for stopwords +Allbery rra-c-util Automake perlcritic .libs namespace sublicense +MERCHANTABILITY NONINFRINGEMENT + +=head1 NAME + +Test::RRA::Config - Perl test configuration + +=head1 SYNOPSIS + + use Test::RRA::Config qw($MINIMUM_VERSION); + print "Required Perl version is $MINIMUM_VERSION\n"; + +=head1 DESCRIPTION + +Test::RRA::Config encapsulates per-package configuration for generic Perl +test programs that are shared between multiple packages using the +rra-c-util infrastructure. It handles locating and loading the test +configuration file for both C Automake packages and stand-alone Perl +modules. + +Test::RRA::Config looks for a file named F relative to the +root of the test directory. That root is taken from the environment +variables BUILD or SOURCE (in that order) if set, which will be the case +for C Automake packages using C TAP Harness. If neither is set, it +expects the root of the test directory to be a directory named F +relative to the current directory, which will be the case for stand-alone +Perl modules. + +The following variables are supported: + +=over 4 + +=item $COVERAGE_LEVEL + +The coverage level achieved by the test suite for Perl test coverage +testing using Test::Strict, as a percentage. The test will fail if test +coverage less than this percentage is achieved. If not given, defaults +to 100. + +=item @COVERAGE_SKIP_TESTS + +Directories under F whose tests should be skipped when doing coverage +testing. This can be tests that won't contribute to coverage or tests +that don't run properly under Devel::Cover for some reason (such as ones +that use taint checking). F and F