From 574a9c0456c182831b3d01a4d7ee0c737b91b107 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Tue, 9 Jun 2009 14:39:39 -0700 Subject: Remove Subversion Id strings --- tests/portable/strlcpy-t.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'tests/portable/strlcpy-t.c') diff --git a/tests/portable/strlcpy-t.c b/tests/portable/strlcpy-t.c index 8fb1f9c..74c9ecd 100644 --- a/tests/portable/strlcpy-t.c +++ b/tests/portable/strlcpy-t.c @@ -1,5 +1,4 @@ -/* $Id$ - * +/* * strlcpy test suite. * * Copyright (c) 2004, 2005, 2006 -- cgit v1.2.3 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. --- .gitignore | 4 +- Makefile.am | 52 ++++--- NEWS | 8 + README | 8 +- configure.ac | 1 - tests/TESTS | 3 + tests/client/basic-t.in | 220 +++++++++----------------- tests/libtest.c | 203 ------------------------ tests/libtest.h | 69 --------- tests/libtest.sh | 80 ---------- tests/portable/asprintf-t.c | 34 +++-- tests/portable/mkstemp-t.c | 73 +++++++++ tests/portable/mkstemp.c | 2 + tests/portable/setenv-t.c | 46 ++++++ tests/portable/setenv.c | 2 + tests/portable/snprintf-t.c | 123 +++++++-------- tests/portable/strlcat-t.c | 84 +++++----- tests/portable/strlcpy-t.c | 73 +++++---- tests/runtests.c | 327 ++++++++++++++++++++++++++++++--------- tests/tap/basic.c | 356 +++++++++++++++++++++++++++++++++++++++++++ tests/tap/basic.h | 98 ++++++++++++ tests/tap/kerberos.c | 164 ++++++++++++++++++++ tests/tap/kerberos.h | 32 ++++ tests/tap/kerberos.sh | 48 ++++++ tests/tap/libtap.sh | 148 ++++++++++++++++++ tests/tap/messages.c | 80 ++++++++++ tests/tap/messages.h | 35 +++++ tests/tap/process.c | 100 ++++++++++++ tests/tap/process.h | 37 +++++ tests/tap/remctl.sh | 46 ++++++ tests/util/concat-t.c | 60 +++----- tests/util/messages-krb5-t.c | 99 ++++++++++++ tests/util/messages-t.c | 201 +++++++----------------- tests/util/xmalloc-t | 127 +++++++++++++++ tests/util/xmalloc-t.in | 126 --------------- tests/util/xmalloc.c | 85 ++++++----- 36 files changed, 2138 insertions(+), 1116 deletions(-) delete mode 100644 tests/libtest.c delete mode 100644 tests/libtest.h delete mode 100644 tests/libtest.sh create mode 100644 tests/portable/mkstemp-t.c create mode 100644 tests/portable/mkstemp.c create mode 100644 tests/portable/setenv-t.c create mode 100644 tests/portable/setenv.c create mode 100644 tests/tap/basic.c create mode 100644 tests/tap/basic.h create mode 100644 tests/tap/kerberos.c create mode 100644 tests/tap/kerberos.h create mode 100644 tests/tap/kerberos.sh create mode 100644 tests/tap/libtap.sh create mode 100644 tests/tap/messages.c create mode 100644 tests/tap/messages.h create mode 100644 tests/tap/process.c create mode 100644 tests/tap/process.h create mode 100644 tests/tap/remctl.sh create mode 100644 tests/util/messages-krb5-t.c create mode 100755 tests/util/xmalloc-t delete mode 100644 tests/util/xmalloc-t.in (limited to 'tests/portable/strlcpy-t.c') diff --git a/.gitignore b/.gitignore index 4599484..09ae109 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,8 @@ /tests/data/test.krbtype /tests/kasetkey/basic-t /tests/portable/asprintf-t +/tests/portable/mkstemp-t +/tests/portable/setenv-t /tests/portable/snprintf-t /tests/portable/strlcat-t /tests/portable/strlcpy-t @@ -37,9 +39,9 @@ /tests/server/keytab-t /tests/server/pod-t /tests/util/concat-t +/tests/util/messages-krb5-t /tests/util/messages-t /tests/util/xmalloc -/tests/util/xmalloc-t /wallet-*.tar.gz /stamp-h1 .deps diff --git a/Makefile.am b/Makefile.am index 27a6e39..056229b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -117,33 +117,45 @@ distclean-local: fi # The bits below are for the test suite, not for the main package. -check_PROGRAMS = tests/runtests tests/portable/asprintf-t \ - tests/portable/snprintf-t tests/portable/strlcat-t \ - tests/portable/strlcpy-t tests/util/concat-t tests/util/messages-t \ - tests/util/xmalloc -check_LIBRARIES = tests/libtest.a -tests_libtest_a_SOURCES = tests/libtest.c tests/libtest.h +check_PROGRAMS = tests/runtests tests/portable/asprintf-t \ + tests/portable/mkstemp-t tests/portable/setenv-t \ + tests/portable/snprintf-t tests/portable/strlcat-t \ + tests/portable/strlcpy-t tests/util/concat-t \ + tests/util/messages-krb5-t tests/util/messages-t tests/util/xmalloc +tests_runtests_CPPFLAGS = -DSOURCE='"$(abs_top_srcdir)/tests"' \ + -DBUILD='"$(abs_top_builddir)/tests"' +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 # All of the test programs. tests_portable_asprintf_t_SOURCES = tests/portable/asprintf-t.c \ - tests/portable/asprintf.c -tests_portable_asprintf_t_LDADD = tests/libtest.a util/libutil.a \ - portable/libportable.a + tests/portable/asprintf.c +tests_portable_asprintf_t_LDADD = tests/tap/libtap.a portable/libportable.a +tests_portable_mkstemp_t_SOURCES = tests/portable/mkstemp-t.c \ + tests/portable/mkstemp.c +tests_portable_mkstemp_t_LDADD = tests/tap/libtap.a portable/libportable.a +tests_portable_setenv_t_SOURCES = tests/portable/setenv-t.c \ + tests/portable/setenv.c +tests_portable_setenv_t_LDADD = tests/tap/libtap.a portable/libportable.a tests_portable_snprintf_t_SOURCES = tests/portable/snprintf-t.c \ - tests/portable/snprintf.c -tests_portable_snprintf_t_LDADD = tests/libtest.a util/libutil.a \ - portable/libportable.a + tests/portable/snprintf.c +tests_portable_snprintf_t_LDADD = tests/tap/libtap.a portable/libportable.a tests_portable_strlcat_t_SOURCES = tests/portable/strlcat-t.c \ - tests/portable/strlcat.c -tests_portable_strlcat_t_LDADD = tests/libtest.a util/libutil.a \ - portable/libportable.a + tests/portable/strlcat.c +tests_portable_strlcat_t_LDADD = tests/tap/libtap.a portable/libportable.a tests_portable_strlcpy_t_SOURCES = tests/portable/strlcpy-t.c \ - tests/portable/strlcpy.c -tests_portable_strlcpy_t_LDADD = tests/libtest.a util/libutil.a \ - portable/libportable.a -tests_util_concat_t_LDADD = tests/libtest.a util/libutil.a \ + tests/portable/strlcpy.c +tests_portable_strlcpy_t_LDADD = tests/tap/libtap.a portable/libportable.a +tests_util_concat_t_LDADD = tests/tap/libtap.a util/libutil.a \ portable/libportable.a -tests_util_messages_t_LDADD = tests/libtest.a util/libutil.a \ +tests_util_messages_krb5_t_CPPFLAGS = $(KRB5_CPPFLAGS) +tests_util_messages_krb5_t_LDFLAGS = $(KRB5_LDFLAGS) +tests_util_messages_krb5_t_LDADD = tests/tap/libtap.a util/libutil.a \ + portable/libportable.a $(KRB5_LIBS) +tests_util_messages_t_LDADD = tests/tap/libtap.a util/libutil.a \ portable/libportable.a tests_util_xmalloc_LDADD = util/libutil.a portable/libportable.a diff --git a/NEWS b/NEWS index 1d3a5e3..96962f8 100644 --- a/NEWS +++ b/NEWS @@ -67,6 +67,14 @@ wallet 0.10 (unreleased) * Use AC_TYPE_LONG_LONG_INT instead of AC_CHECK_TYPES([long long]). * Provide a proper bool type with Sun Studio 12 on Solaris 10. * Break util/util.h into separate header files per module. + * Update portable and util tests for C TAP Harness 1.1. + + Update to 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. wallet 0.9 (2008-04-24) diff --git a/README b/README index 7302c06..eb9b39c 100644 --- a/README +++ b/README @@ -233,8 +233,12 @@ TESTING not available, but this has not yet been fully tested in all of its possible permutations. - If a test case fails, please run that individual test program directly - and send me the output when reporting the problem. + If a test fails, you can run a single test with verbose output via: + + tests/runtests -o + + Do this instead of running the test program directly since it will + ensure that necessary environment variables are set up. CONFIGURATION diff --git a/configure.ac b/configure.ac index f66a682..0330aa9 100644 --- a/configure.ac +++ b/configure.ac @@ -70,5 +70,4 @@ AC_CONFIG_FILES([tests/server/admin-t], [chmod +x tests/server/admin-t]) AC_CONFIG_FILES([tests/server/backend-t], [chmod +x tests/server/backend-t]) AC_CONFIG_FILES([tests/server/keytab-t], [chmod +x tests/server/keytab-t]) AC_CONFIG_FILES([tests/server/pod-t], [chmod +x tests/server/pod-t]) -AC_CONFIG_FILES([tests/util/xmalloc-t], [chmod +x tests/util/xmalloc-t]) AC_OUTPUT diff --git a/tests/TESTS b/tests/TESTS index a446643..ac6fd82 100644 --- a/tests/TESTS +++ b/tests/TESTS @@ -3,6 +3,8 @@ client/full client/pod client/prompt portable/asprintf +portable/mkstemp +portable/setenv portable/snprintf portable/strlcat portable/strlcpy @@ -12,4 +14,5 @@ server/keytab server/pod util/concat util/messages +util/messages-krb5 util/xmalloc diff --git a/tests/client/basic-t.in b/tests/client/basic-t.in index 96b165e..1dbc0b9 100644 --- a/tests/client/basic-t.in +++ b/tests/client/basic-t.in @@ -9,43 +9,10 @@ # See LICENSE for licensing terms. # Load the test library. -. "@abs_top_srcdir@/tests/libtest.sh" - -# Print the number of tests. -total=31 -count=1 -echo "$total" - -# Find the client program. -chdir_data '../client/wallet' -if [ ! -f 'data/test.keytab' ] || [ -z '@REMCTLD@' ] ; then - skip 1 "$total" 'no Kerberos configuration' - exit 0 -fi -wallet='../client/wallet' - -# Start the remctld daemon and wait for it to start. -principal=`cat data/test.principal` -rm -f data/pid -( @REMCTLD@ -m -p 14373 -s "$principal" -P data/pid -f data/basic.conf \ - -S -F -k data/test.keytab &) -KRB5CCNAME=data/test.cache; export KRB5CCNAME -kinit -k -t data/test.keytab "$principal" > /dev/null 2>&1 -if [ $? != 0 ] ; then - kinit -t data/test.keytab "$principal" > /dev/null 2>&1 -fi -if [ $? != 0 ] ; then - kinit -T /bin/true -k -K data/test.keytab "$principal" > /dev/null 2>&1 -fi -if [ $? != 0 ] ; then - echo 'Unable to obtain Kerberos tickets' >&2 - exit 1 -fi -[ -f data/pid ] || sleep 1 -if [ ! -f data/pid ] ; then - echo 'remctld did not start' >&2 - exit 1 -fi +. "$SOURCE/tap/libtap.sh" +. "$SOURCE/tap/kerberos.sh" +. "$SOURCE/tap/remctl.sh" +cd "$BUILD" # We need a modified krb5.conf file to test wallet configuration settings in # krb5.conf. Despite the hard-coding of test-k5.stanford.edu, this test isn't @@ -73,43 +40,39 @@ EOF fi done if [ -z "$krb5conf" ] ; then - echo 'No krb5.conf found -- put one in tests/data/krb5.conf' >&2 - exit 1 + skip_all 'no krb5.conf found, put one in tests/data/krb5.conf' +fi + +# Test setup. +kerberos_setup +if [ $? != 0 ] ; then + skip_all 'Kerberos tests not configured' +elif [ -z '@REMCTLD@' ] ; then + skip_all 'No remctld found' +else + plan 34 fi +remctld_start '@REMCTLD@' "$SOURCE/data/basic.conf" +wallet="$BUILD/../client/wallet" # Make sure everything's clean. rm -f output output.bak keytab keytab.bak srvtab srvtab.bak autocreated # Now, we can finally run our tests. First, basic operations. -runsuccess "" "$wallet" -k "$principal" -p 14373 -s localhost -c fake-wallet \ - -f output get file fake-test -if cmp output data/fake-data >/dev/null 2>&1 ; then - printcount "ok" -else - printcount "not ok" -fi -if [ -f output.bak ] || [ -f output.new ] ; then - printcount "not ok" -else - printcount "ok" -fi -if [ -f autocreated ] ; then - printcount "ok" -else - printcount "not ok" -fi -runsuccess "" "$wallet" -k "$principal" -p 14373 -s localhost -c fake-wallet \ - -f output get file fake-test -if cmp output data/fake-data >/dev/null 2>&1 ; then - printcount "ok" -else - printcount "not ok" -fi -if [ -f output.new ] || [ ! -f output.bak ] ; then - printcount "not ok" -else - printcount "ok" -fi +ok_program 'get file' 0 '' \ + "$wallet" -k "$principal" -p 14373 -s localhost -c fake-wallet -f output \ + get file fake-test +ok '...and file is correct' cmp output data/fake-data +ok '...and no backup files' [ ! -f output.bak ] +ok '...and no new files' [ ! -f output.new ] +ok '...and we tried autocreation' [ -f autocreated ] +ok_program 'get file again' 0 '' \ + "$wallet" -k "$principal" -p 14373 -s localhost -c fake-wallet -f output \ + get file fake-test +ok '...and file is correct' cmp output data/fake-data +ok '...and now there is a backup file' [ -f output.bak ] +ok '...which has the right contents' cmp output.bak data/fake-data +ok '...but there is no new file' [ ! -f output.new ] # Now, append configuration to krb5.conf and test getting configuration from # there. @@ -123,116 +86,79 @@ cat >> krb5.conf </dev/null 2>&1 ; then - printcount "ok" -else - printcount "not ok" -fi +ok_program 'get file with configuration' 0 '' \ + "$wallet" -f output get file fake-test +ok '...and file is correct' cmp output data/fake-data rm -f output output.bak # Test keytab support. -runsuccess "" "$wallet" -f keytab get keytab service/fake-srvtab -if cmp keytab data/fake-keytab >/dev/null 2>&1 ; then - printcount "ok" - rm keytab -else - printcount "not ok" -fi +ok_program 'get keytab' 0 '' \ + "$wallet" -f keytab get keytab service/fake-srvtab +ok '...and keytab is correct' cmp keytab data/fake-keytab +rm -f keytab # Test srvtab support. -runsuccess "" "$wallet" -f keytab -S srvtab get keytab service/fake-srvtab -if cmp keytab data/fake-keytab >/dev/null 2>&1 ; then - printcount "ok" -else - printcount "not ok" -fi -rm keytab -runsuccess "" "$wallet" -f keytab -S srvtab get keytab service/fake-srvtab -if cmp keytab data/fake-keytab >/dev/null 2>&1 ; then - printcount "ok" -else - printcount "not ok" -fi -if cmp srvtab data/fake-srvtab >/dev/null 2>&1 ; then - printcount "ok" -else - printcount "not ok" -fi -if cmp srvtab.bak data/fake-srvtab >/dev/null 2>&1 ; then - printcount "ok" -else - printcount "not ok" -fi +ok_program 'get srvtab' 0 '' \ + "$wallet" -f keytab -S srvtab get keytab service/fake-srvtab +ok '...and keytab is correct' cmp keytab data/fake-keytab +rm -f keytab +ok_program 'get srvtab again' 0 '' \ + "$wallet" -f keytab -S srvtab get keytab service/fake-srvtab +ok '...and keytab is correct' cmp keytab data/fake-keytab +ok '...and srvtab is correct' cmp srvtab data/fake-srvtab +ok '...and srvtab backup is correct' cmp srvtab.bak data/fake-srvtab rm -f srvtab srvtab.bak # Test keytab merging. -runsuccess "" "$wallet" -f keytab get keytab service/fake-keytab +ok_program 'keytab merging' 0 '' \ + "$wallet" -f keytab get keytab service/fake-keytab (klist -keK keytab 2>&1) | sed '/Keytab name:/d' > klist-seen (klist -keK data/fake-keytab-merge 2>&1) | sed '/Keytab name:/d' > klist-good -if cmp klist-seen klist-good >/dev/null 2>&1 ; then - printcount "ok" - rm -f keytab klist-seen klist-good -else - printcount "not ok" -fi +ok '...and the merged keytab is correct' cmp klist-seen klist-good +rm -f keytab klist-seen klist-good # Test srvtab download into a merged keytab with an older version. cp data/fake-keytab-old keytab -runsuccess "" "$wallet" -f keytab -S srvtab get keytab service/fake-srvtab -if cmp srvtab data/fake-srvtab >/dev/null 2>&1 ; then - printcount "ok" -else - printcount "not ok" -fi +ok_program 'keytab merging with srvtab creation' 0 '' \ + "$wallet" -f keytab -S srvtab get keytab service/fake-srvtab +ok '...and the srvtab is correct' cmp srvtab data/fake-srvtab rm -f keytab srvtab # Test store from standard input. -echo "This is a test of store" | runsuccess "" "$wallet" store file fake-test -count=`expr $count + 1` +echo "This is a test of store" > input +ok_program 'store from stdin' 0 '' "$wallet" store file fake-test < input +rm -f input echo "file fake-test" > store-correct echo "This is a test of store" >> store-correct -if cmp store-output store-correct >/dev/null 2>&1 ; then - printcount "ok" -else - printcount "not ok" - echo == store-output == - cat store-output - echo == store-correct == - cat store-correct -fi +ok '...and the correct data was stored' diff store-output store-correct rm -f store-output store-correct # Test store with -f. echo "This is more store input" > store-input echo "file fake-test" > store-correct cat store-input >> store-correct -runsuccess "" "$wallet" -f store-input store file fake-test -if cmp store-output store-correct >/dev/null 2>&1 ; then - printcount "ok" -else - printcount "not ok" -fi +ok_program 'store from a file' 0 '' \ + "$wallet" -f store-input store file fake-test +ok '...and the correct data was stored' cmp store-output store-correct rm -f store-input store-output store-correct # Test various other client functions and errors. -runsuccess "This is a fake keytab." "$wallet" get keytab service/fake-output -runsuccess "Some stuff about file fake-test" \ +ok_program 'get output to stdout' 0 'This is a fake keytab.' \ + "$wallet" get keytab service/fake-output +ok_program 'show output' 0 'Some stuff about file fake-test' \ "$wallet" show file fake-test -runfailure 1 "wallet: Unknown object type srvtab" \ +ok_program 'unknown object type' 1 'wallet: Unknown object type srvtab' \ "$wallet" get srvtab service/fake-test -runfailure 1 "wallet: Unknown keytab service/unknown" \ +ok_program 'unknown keytab name in show' 1 \ + 'wallet: Unknown keytab service/unknown' \ "$wallet" show keytab service/unknown -runfailure 1 "wallet: Unknown keytab service/unknown" \ +ok_program 'unknown keytab name in get' 1 \ + 'wallet: Unknown keytab service/unknown' \ "$wallet" get keytab service/unknown -runsuccess "Expiration date of keytab service/fake-test" \ +ok_program 'expiration date' 0 'Expiration date of keytab service/fake-test' \ "$wallet" expires keytab service/fake-test # Clean up. -KRB5_CONFIG= -rm krb5.conf -rm -f autocreated data/test.cache -if [ -f data/pid ] ; then - kill `cat data/pid` - rm -f data/pid -fi +rm -f autocreated krb5.conf +remctld_stop +kerberos_cleanup diff --git a/tests/libtest.c b/tests/libtest.c deleted file mode 100644 index bddaf91..0000000 --- a/tests/libtest.c +++ /dev/null @@ -1,203 +0,0 @@ -/* - * 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 - * 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. - * - * Copyright 2006, 2007 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 code is derived from software contributed to the Internet Software - * Consortium by Rich Salz. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE - * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#include -#include - -#include -#include - -#include -#include - -/* A global buffer into which message_log_buffer stores error messages. */ -char *errors = NULL; - - -/* - * Initialize things. Turns on line buffering on stdout and then prints out - * the number of tests in the test suite. - */ -void -test_init(int count) -{ - if (setvbuf(stdout, NULL, _IOLBF, BUFSIZ) != 0) - syswarn("cannot set stdout to line buffered"); - printf("%d\n", count); -} - - -/* - * Takes a boolean success value and assumes the test passes if that value - * is true and fails if that value is false. - */ -void -ok(int n, int success) -{ - printf("%sok %d\n", success ? "" : "not ", n); -} - - -/* - * Takes an expected integer and a seen integer and assumes the test passes - * if those two numbers match. - */ -void -ok_int(int n, int wanted, int seen) -{ - if (wanted == seen) - printf("ok %d\n", n); - else - printf("not ok %d\n wanted: %d\n seen: %d\n", n, wanted, seen); -} - - -/* - * Takes a string and what the string should be, and assumes the test passes - * if those strings match (using strcmp). - */ -void -ok_string(int n, const char *wanted, const char *seen) -{ - if (wanted == NULL) - wanted = "(null)"; - if (seen == NULL) - seen = "(null)"; - if (strcmp(wanted, seen) != 0) - printf("not ok %d\n wanted: %s\n seen: %s\n", n, wanted, seen); - else - printf("ok %d\n", n); -} - - -/* - * Takes an expected integer and a seen integer and assumes the test passes if - * those two numbers match. - */ -void -ok_double(int n, double wanted, double seen) -{ - if (wanted == seen) - printf("ok %d\n", n); - else - printf("not ok %d\n wanted: %g\n seen: %g\n", n, wanted, seen); -} - - -/* - * Skip a test. - */ -void -skip(int n, const char *reason) -{ - printf("ok %d # skip", n); - if (reason != NULL) - printf(" - %s", reason); - putchar('\n'); -} - - -/* - * Report the same status on the next count tests. - */ -void -ok_block(int n, int count, int status) -{ - int i; - - for (i = 0; i < count; i++) - ok(n++, status); -} - - -/* - * Skip the next count tests. - */ -void -skip_block(int n, int count, const char *reason) -{ - int i; - - for (i = 0; i < count; i++) - skip(n++, reason); -} - - -/* - * An error handler that appends all errors to the errors global. Used by - * error_capture. - */ -static void -message_log_buffer(int len, 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 { - char *new_errors; - - new_errors = concat(errors, message, "\n", (char *) 0); - free(errors); - errors = new_errors; - } - free(message); -} - - -/* - * Turn on the capturing of errors. Errors will be stored in the global - * errors variable where they can be checked by the test suite. Capturing is - * turned off with errors_uncapture. - */ -void -errors_capture(void) -{ - if (errors != NULL) { - free(errors); - errors = NULL; - } - message_handlers_warn(1, message_log_buffer); - message_handlers_notice(1, message_log_buffer); -} - - -/* - * Turn off the capturing of errors again. - */ -void -errors_uncapture(void) -{ - message_handlers_warn(1, message_log_stderr); - message_handlers_notice(1, message_log_stdout); -} diff --git a/tests/libtest.h b/tests/libtest.h deleted file mode 100644 index ad4f591..0000000 --- a/tests/libtest.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Some utility routines for writing tests. - * - * Copyright 2006, 2007 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 code is derived from software contributed to the Internet Software - * Consortium by Rich Salz. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE - * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef LIBTEST_H -#define LIBTEST_H 1 - -#include -#include - -/* - * Used for iterating through arrays. ARRAY_SIZE returns the number of - * elements in the array (useful for a < upper bound in a for loop) and - * ARRAY_END returns a pointer to the element past the end (ISO C99 makes it - * legal to refer to such a pointer as long as it's never dereferenced). - */ -#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) -#define ARRAY_END(array) (&(array)[ARRAY_SIZE(array)]) - -/* A global buffer into which errors_capture stores errors. */ -extern char *errors; - -BEGIN_DECLS - -void ok(int n, int success); -void ok_int(int n, int wanted, int seen); -void ok_double(int n, double wanted, double seen); -void ok_string(int n, const char *wanted, const char *seen); -void skip(int n, const char *reason); - -/* Report the same status on, or skip, the next count tests. */ -void ok_block(int n, int count, int success); -void skip_block(int n, int count, const char *reason); - -/* Print out the number of tests and set standard output to line buffered. */ -void test_init(int count); - -/* - * Turn on capturing of errors with errors_capture. Errors reported by warn - * will be stored in the global errors variable. Turn this off again with - * errors_uncapture. Caller is responsible for freeing errors when done. - */ -void errors_capture(void); -void errors_uncapture(void); - -END_DECLS - -#endif /* LIBTEST_H */ diff --git a/tests/libtest.sh b/tests/libtest.sh deleted file mode 100644 index 74f5ee6..0000000 --- a/tests/libtest.sh +++ /dev/null @@ -1,80 +0,0 @@ -# Shell function library for test cases. -# -# Written by Russ Allbery -# Copyright 2006, 2007, 2008 Board of Trustees, Leland Stanford Jr. University -# -# See LICENSE for licensing terms. - -# The count starts at 1 and is updated each time ok is printed. printcount -# takes "ok" or "not ok". -count=1 -printcount () { - echo "$1 $count $2" - count=`expr $count + 1` -} - -# Run a program expected to succeed, and print ok if it does and produces -# the correct output. Takes the output as the first argument, the command to -# run as the second argument, and then all subsequent arguments are arguments -# to the command. -runsuccess () { - w_output="$1" - shift - output=`"$@" 2>&1` - status=$? - if [ $status = 0 ] && [ x"$output" = x"$w_output" ] ; then - printcount 'ok' - else - printcount 'not ok' - echo " saw: $output" - echo " not: $w_output" - fi -} - -# Run a program expected to fail and make sure it fails with the correct exit -# status and the correct failure message. Takes the expected status, the -# expected output, and then everything else is the command and arguments. -# Strip the second colon and everything after it off the error message since -# it's system-specific. -runfailure () { - w_status="$1" - shift - w_output="$1" - shift - output=`"$@" 2>&1` - status=$? - output=`echo "$output" | sed 's/\(:[^:]*\):.*/\1/'` - if [ $status = $w_status ] && [ x"$output" = x"$w_output" ] ; then - printcount 'ok' - else - printcount 'not ok' - echo " saw: ($status) $output" - echo " not: ($w_status) $w_output" - fi -} - -# Skip tests from $1 to $2 inclusive with reason $3. -skip () { - n="$1" - while [ "$n" -le "$2" ] ; do - echo ok "$n # skip $3" - n=`expr "$n" + 1` - done -} - -# Given a file name or relative file path, try to cd to the correct directory -# so that the relative file path is valid. Exits with an error if that isn't -# possible. -chdir_data () { - if [ -f "../$1" ] ; then - cd .. - else - if [ -f "tests/$1" ] ; then - cd tests - fi - fi - if [ ! -f "$1" ] ; then - echo "Cannot locate $1" >&2 - exit 1 - fi -} diff --git a/tests/portable/asprintf-t.c b/tests/portable/asprintf-t.c index 689e7c7..04fbd1b 100644 --- a/tests/portable/asprintf-t.c +++ b/tests/portable/asprintf-t.c @@ -2,7 +2,8 @@ * asprintf and vasprintf test suite. * * Written by Russ Allbery - * Copyright 2006, 2008 Board of Trustees, Leland Stanford Jr. University + * Copyright 2006, 2008, 2009 + * Board of Trustees, Leland Stanford Jr. University * * See LICENSE for licensing terms. */ @@ -10,9 +11,10 @@ #include #include -#include +#include -int test_asprintf(char **, const char *, ...); +int test_asprintf(char **, const char *, ...) + __attribute__((__format__(printf, 2, 3))); int test_vasprintf(char **, const char *, va_list); static int @@ -32,25 +34,25 @@ main(void) { char *result = NULL; - test_init(12); + plan(12); - ok_int(1, 7, test_asprintf(&result, "%s", "testing")); - ok_string(2, "testing", result); + is_int(7, test_asprintf(&result, "%s", "testing"), "asprintf length"); + is_string("testing", result, "asprintf result"); free(result); - ok(3, 1); - ok_int(4, 0, test_asprintf(&result, "%s", "")); - ok_string(5, "", result); + ok(3, "free asprintf"); + is_int(0, test_asprintf(&result, "%s", ""), "asprintf empty length"); + is_string("", result, "asprintf empty string"); free(result); - ok(6, 1); + ok(6, "free asprintf of empty string"); - ok_int(7, 6, vatest(&result, "%d %s", 2, "test")); - ok_string(8, "2 test", result); + is_int(6, vatest(&result, "%d %s", 2, "test"), "vasprintf length"); + is_string("2 test", result, "vasprintf result"); free(result); - ok(9, 1); - ok_int(10, 0, vatest(&result, "%s", "")); - ok_string(11, "", result); + ok(9, "free vasprintf"); + is_int(0, vatest(&result, "%s", ""), "vasprintf empty length"); + is_string("", result, "vasprintf empty string"); free(result); - ok(12, 1); + ok(12, "free vasprintf of empty string"); return 0; } diff --git a/tests/portable/mkstemp-t.c b/tests/portable/mkstemp-t.c new file mode 100644 index 0000000..54701f7 --- /dev/null +++ b/tests/portable/mkstemp-t.c @@ -0,0 +1,73 @@ +/* + * mkstemp test suite. + * + * Written by Russ Allbery + * Copyright 2002, 2004, 2008, 2009 + * Board of Trustees, Leland Stanford Jr. University + * + * See LICENSE for licensing terms. + */ + +#include +#include + +#include +#include + +#include + +int test_mkstemp(char *template); + +int +main(void) +{ + int fd; + char template[] = "tsXXXXXXX"; + char tooshort[] = "XXXXX"; + char bad1[] = "/foo/barXXXXX"; + char bad2[] = "/foo/barXXXXXX.out"; + char buffer[256]; + struct stat st1, st2; + ssize_t length; + + plan(20); + + /* First, test a few error messages. */ + errno = 0; + is_int(-1, test_mkstemp(tooshort), "too short of template"); + is_int(EINVAL, errno, "...with correct errno"); + is_string("XXXXX", tooshort, "...and template didn't change"); + errno = 0; + is_int(-1, test_mkstemp(bad1), "bad template"); + is_int(EINVAL, errno, "...with correct errno"); + is_string("/foo/barXXXXX", bad1, "...and template didn't change"); + errno = 0; + is_int(-1, test_mkstemp(bad2), "template doesn't end in XXXXXX"); + is_int(EINVAL, errno, "...with correct errno"); + is_string("/foo/barXXXXXX.out", bad2, "...and template didn't change"); + errno = 0; + + /* Now try creating a real file. */ + fd = test_mkstemp(template); + ok(fd >= 0, "mkstemp works with valid template"); + ok(strcmp(template, "tsXXXXXXX") != 0, "...and template changed"); + ok(strncmp(template, "tsX", 3) == 0, "...and didn't touch first X"); + ok(access(template, F_OK) == 0, "...and the file exists"); + + /* Make sure that it's the same file as template refers to now. */ + ok(stat(template, &st1) == 0, "...and stat of template works"); + ok(fstat(fd, &st2) == 0, "...and stat of open file descriptor works"); + ok(st1.st_ino == st2.st_ino, "...and they're the same file"); + unlink(template); + + /* Make sure the open mode is correct. */ + length = strlen(template); + is_int(length, write(fd, template, length), "write to open file works"); + ok(lseek(fd, 0, SEEK_SET) == 0, "...and rewind works"); + is_int(length, read(fd, buffer, length), "...and the data is there"); + buffer[length] = '\0'; + is_string(template, buffer, "...and matches what we wrote"); + close(fd); + + return 0; +} diff --git a/tests/portable/mkstemp.c b/tests/portable/mkstemp.c new file mode 100644 index 0000000..4632d3d --- /dev/null +++ b/tests/portable/mkstemp.c @@ -0,0 +1,2 @@ +#define TESTING 1 +#include diff --git a/tests/portable/setenv-t.c b/tests/portable/setenv-t.c new file mode 100644 index 0000000..5bc59ce --- /dev/null +++ b/tests/portable/setenv-t.c @@ -0,0 +1,46 @@ +/* + * setenv test suite. + * + * 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. + */ + +#include +#include + +#include + +#include + +int test_setenv(const char *name, const char *value, int overwrite); + +static const char test_var[] = "SETENV_TEST"; +static const char test_value1[] = "Do not taunt Happy Fun Ball."; +static const char test_value2[] = "Do not use Happy Fun Ball on concrete."; + + +int +main(void) +{ + plan(8); + + if (getenv(test_var)) + bail("%s already in the environment!", test_var); + + ok(test_setenv(test_var, test_value1, 0) == 0, "set string 1"); + is_string(test_value1, getenv(test_var), "...and getenv correct"); + ok(test_setenv(test_var, test_value2, 0) == 0, "set string 2"); + is_string(test_value1, getenv(test_var), "...and getenv unchanged"); + ok(test_setenv(test_var, test_value2, 1) == 0, "overwrite string 2"); + is_string(test_value2, getenv(test_var), "...and getenv changed"); + ok(test_setenv(test_var, "", 1) == 0, "overwrite with empty string"); + is_string("", getenv(test_var), "...and getenv correct"); + + return 0; +} diff --git a/tests/portable/setenv.c b/tests/portable/setenv.c new file mode 100644 index 0000000..79a7efd --- /dev/null +++ b/tests/portable/setenv.c @@ -0,0 +1,2 @@ +#define TESTING 1 +#include diff --git a/tests/portable/snprintf-t.c b/tests/portable/snprintf-t.c index 18c2326..ca6ae61 100644 --- a/tests/portable/snprintf-t.c +++ b/tests/portable/snprintf-t.c @@ -1,32 +1,25 @@ /* * snprintf test suite. * + * 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 * - * This code is derived from software contributed to the Internet Software - * Consortium by Rich Salz. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE - * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. + * See LICENSE for licensing terms. */ #include #include -#include +#include +/* + * Intentionally don't add the printf attribute here since we pass a + * zero-length printf format during testing and don't want warnings. + */ int test_snprintf(char *str, size_t count, const char *fmt, ...); int test_vsnprintf(char *str, size_t count, const char *fmt, va_list args); @@ -93,7 +86,7 @@ static unsigned long long ullong_nums[] = { static void -test_format(int n, int truncate, const char *expected, int count, +test_format(bool truncate, const char *expected, int count, const char *format, ...) { char buf[128]; @@ -103,16 +96,8 @@ test_format(int n, int truncate, const char *expected, int count, va_start(args, format); result = test_vsnprintf(buf, truncate ? 32 : sizeof(buf), format, args); va_end(args); - if (!strcmp(buf, expected) && result == count) { - printf("ok %d\n", n); - } else { - printf("not ok %d\n", n); - printf(" format: %s\n", format); - if (strcmp(buf, expected)) - printf(" saw: %s\n want: %s\n", buf, expected); - if (result != count) - printf(" %d != %d\n", result, count); - } + is_string(expected, buf, "format %s, wanted %s", format, expected); + is_int(count, result, "...and output length correct"); } @@ -124,75 +109,69 @@ main(void) long lcount; char lgbuf[128]; - test_init((26 + (ARRAY_SIZE(fp_formats) - 1) * ARRAY_SIZE(fp_nums) - + (ARRAY_SIZE(int_formats) - 1) * ARRAY_SIZE(int_nums) - + (ARRAY_SIZE(uint_formats) - 1) * ARRAY_SIZE(uint_nums) - + (ARRAY_SIZE(llong_formats) - 1) * ARRAY_SIZE(llong_nums) - + (ARRAY_SIZE(ullong_formats) - 1) * ARRAY_SIZE(ullong_nums))); - - ok(1, test_snprintf(NULL, 0, "%s", "abcd") == 4); - ok(2, test_snprintf(NULL, 0, "%d", 20) == 2); - ok(3, test_snprintf(NULL, 0, "Test %.2s", "abcd") == 7); - ok(4, test_snprintf(NULL, 0, "%c", 'a') == 1); - ok(5, test_snprintf(NULL, 0, "") == 0); - - test_format(6, 1, "abcd", 4, "%s", "abcd"); - test_format(7, 1, "20", 2, "%d", 20); - test_format(8, 1, "Test ab", 7, "Test %.2s", "abcd"); - test_format(9, 1, "a", 1, "%c", 'a'); - test_format(10, 1, "", 0, ""); - test_format(11, 1, "abcdefghijklmnopqrstuvwxyz01234", 36, "%s", - string); - test_format(12, 1, "abcdefghij", 10, "%.10s", string); - test_format(13, 1, " abcdefghij", 12, "%12.10s", string); - test_format(14, 1, " abcdefghijklmnopqrstuvwxyz0", 40, "%40s", - string); - test_format(15, 1, "abcdefghij ", 14, "%-14.10s", string); - test_format(16, 1, " abcdefghijklmnopq", 50, "%50s", - string); - test_format(17, 1, "%abcd%", 6, "%%%0s%%", "abcd"); - test_format(18, 1, "", 0, "%.0s", string); - test_format(19, 1, "abcdefghijklmnopqrstuvwxyz 444", 32, "%.26s %d", + plan(8 + + (18 + (ARRAY_SIZE(fp_formats) - 1) * ARRAY_SIZE(fp_nums) + + (ARRAY_SIZE(int_formats) - 1) * ARRAY_SIZE(int_nums) + + (ARRAY_SIZE(uint_formats) - 1) * ARRAY_SIZE(uint_nums) + + (ARRAY_SIZE(llong_formats) - 1) * ARRAY_SIZE(llong_nums) + + (ARRAY_SIZE(ullong_formats) - 1) * ARRAY_SIZE(ullong_nums)) * 2); + + is_int(4, test_snprintf(NULL, 0, "%s", "abcd"), "simple string length"); + is_int(2, test_snprintf(NULL, 0, "%d", 20), "number length"); + is_int(7, test_snprintf(NULL, 0, "Test %.2s", "abcd"), "limited string"); + is_int(1, test_snprintf(NULL, 0, "%c", 'a'), "character length"); + is_int(0, test_snprintf(NULL, 0, ""), "empty format length"); + + test_format(true, "abcd", 4, "%s", "abcd"); + test_format(true, "20", 2, "%d", 20); + test_format(true, "Test ab", 7, "Test %.2s", "abcd"); + test_format(true, "a", 1, "%c", 'a'); + test_format(true, "", 0, ""); + test_format(true, "abcdefghijklmnopqrstuvwxyz01234", 36, "%s", string); + test_format(true, "abcdefghij", 10, "%.10s", string); + test_format(true, " abcdefghij", 12, "%12.10s", string); + test_format(true, " abcdefghijklmnopqrstuvwxyz0", 40, "%40s", string); + test_format(true, "abcdefghij ", 14, "%-14.10s", string); + test_format(true, " abcdefghijklmnopq", 50, "%50s", string); + test_format(true, "%abcd%", 6, "%%%0s%%", "abcd"); + test_format(true, "", 0, "%.0s", string); + test_format(true, "abcdefghijklmnopqrstuvwxyz 444", 32, "%.26s %d", string, 4444); - test_format(20, 1, "abcdefghijklmnopqrstuvwxyz -2.", 32, - "%.26s %.1f", string, -2.5); - test_format(21, 1, "abcdefghij4444", 14, "%.10s%n%d", string, &count, - 4444); - ok(22, count == 10); - test_format(23, 1, "abcdefghijklmnopqrstuvwxyz01234", 36, "%n%s%ln", + test_format(true, "abcdefghijklmnopqrstuvwxyz -2.", 32, "%.26s %.1f", + string, -2.5); + test_format(true, "abcdefghij4444", 14, "%.10s%n%d", string, &count, 4444); + is_int(10, count, "correct output from %%n"); + test_format(true, "abcdefghijklmnopqrstuvwxyz01234", 36, "%n%s%ln", &count, string, &lcount); - ok(24, count == 0); - ok(25, lcount == 31); - test_format(26, 1, "(null)", 6, "%s", NULL); + is_int(0, count, "correct output from two %%n"); + is_int(31, lcount, "correct output from long %%ln"); + test_format(true, "(null)", 6, "%s", NULL); n = 26; for (i = 0; fp_formats[i] != NULL; i++) for (j = 0; j < ARRAY_SIZE(fp_nums); j++) { count = sprintf(lgbuf, fp_formats[i], fp_nums[j]); - test_format(++n, 0, lgbuf, count, fp_formats[i], fp_nums[j]); + test_format(false, lgbuf, count, fp_formats[i], fp_nums[j]); } for (i = 0; int_formats[i] != NULL; i++) for (j = 0; j < ARRAY_SIZE(int_nums); j++) { count = sprintf(lgbuf, int_formats[i], int_nums[j]); - test_format(++n, 0, lgbuf, count, int_formats[i], - int_nums[j]); + test_format(false, lgbuf, count, int_formats[i], int_nums[j]); } for (i = 0; uint_formats[i] != NULL; i++) for (j = 0; j < ARRAY_SIZE(uint_nums); j++) { count = sprintf(lgbuf, uint_formats[i], uint_nums[j]); - test_format(++n, 0, lgbuf, count, uint_formats[i], - uint_nums[j]); + test_format(false, lgbuf, count, uint_formats[i], uint_nums[j]); } for (i = 0; llong_formats[i] != NULL; i++) for (j = 0; j < ARRAY_SIZE(llong_nums); j++) { count = sprintf(lgbuf, llong_formats[i], llong_nums[j]); - test_format(++n, 0, lgbuf, count, llong_formats[i], - llong_nums[j]); + test_format(false, lgbuf, count, llong_formats[i], llong_nums[j]); } for (i = 0; ullong_formats[i] != NULL; i++) for (j = 0; j < ARRAY_SIZE(ullong_nums); j++) { count = sprintf(lgbuf, ullong_formats[i], ullong_nums[j]); - test_format(++n, 0, lgbuf, count, ullong_formats[i], + test_format(false, lgbuf, count, ullong_formats[i], ullong_nums[j]); } diff --git a/tests/portable/strlcat-t.c b/tests/portable/strlcat-t.c index 2f39925..e02c277 100644 --- a/tests/portable/strlcat-t.c +++ b/tests/portable/strlcat-t.c @@ -1,31 +1,20 @@ /* * strlcat test suite. * + * 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 * - * This code is derived from software contributed to the Internet Software - * Consortium by Rich Salz. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE - * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. + * See LICENSE for licensing terms. */ #include #include -#include +#include size_t test_strlcat(char *, const char *, size_t); @@ -35,42 +24,51 @@ main(void) { char buffer[10] = ""; - test_init(27); + plan(27); - ok_int(1, 3, test_strlcat(buffer, "foo", sizeof(buffer))); - ok_string(2, "foo", buffer); - ok_int(3, 7, test_strlcat(buffer, " bar", sizeof(buffer))); - ok_string(4, "foo bar", buffer); - ok_int(5, 9, test_strlcat(buffer, "!!", sizeof(buffer))); - ok_string(6, "foo bar!!", buffer); - ok_int(7, 10, test_strlcat(buffer, "!", sizeof(buffer))); - ok_string(8, "foo bar!!", buffer); - ok(9, buffer[9] == '\0'); + is_int(3, test_strlcat(buffer, "foo", sizeof(buffer)), + "strlcat into empty buffer"); + is_string("foo", buffer, "...with right output"); + is_int(7, test_strlcat(buffer, " bar", sizeof(buffer)), + "...and append more"); + is_string("foo bar", buffer, "...and output is still correct"); + is_int(9, test_strlcat(buffer, "!!", sizeof(buffer)), + "...and append to buffer limit"); + is_string("foo bar!!", buffer, "...output is still correct"); + is_int(10, test_strlcat(buffer, "!", sizeof(buffer)), + "...append one more character"); + is_string("foo bar!!", buffer, "...and output didn't change"); + ok(buffer[9] == '\0', "...buffer still nul-terminated"); buffer[0] = '\0'; - ok_int(10, 11, test_strlcat(buffer, "hello world", sizeof(buffer))); - ok_string(11, "hello wor", buffer); - ok(12, buffer[9] == '\0'); + is_int(11, test_strlcat(buffer, "hello world", sizeof(buffer)), + "append single long string"); + is_string("hello wor", buffer, "...string truncates properly"); + ok(buffer[9] == '\0', "...buffer still nul-terminated"); buffer[0] = '\0'; - ok_int(13, 7, test_strlcat(buffer, "sausage", 5)); - ok_string(14, "saus", buffer); - ok_int(15, 14, test_strlcat(buffer, "bacon eggs", sizeof(buffer))); - ok_string(16, "sausbacon", buffer); + is_int(7, test_strlcat(buffer, "sausage", 5), "lie about buffer length"); + is_string("saus", buffer, "...contents are correct"); + is_int(14, test_strlcat(buffer, "bacon eggs", sizeof(buffer)), + "...add more up to real size"); + is_string("sausbacon", buffer, "...and result is correct"); /* Make sure that with a size of 0, the destination isn't changed. */ - ok_int(17, 11, test_strlcat(buffer, "!!", 0)); - ok_string(18, "sausbacon", buffer); + is_int(11, test_strlcat(buffer, "!!", 0), "no change with size of 0"); + is_string("sausbacon", buffer, "...and content is the same"); /* Now play with empty strings. */ - ok_int(19, 9, test_strlcat(buffer, "", 0)); - ok_string(20, "sausbacon", buffer); + is_int(9, test_strlcat(buffer, "", 0), + "correct count when appending empty string"); + is_string("sausbacon", buffer, "...and contents are unchanged"); buffer[0] = '\0'; - ok_int(21, 0, test_strlcat(buffer, "", sizeof(buffer))); - ok_string(22, "", buffer); - ok_int(23, 3, test_strlcat(buffer, "foo", 2)); - ok_string(24, "f", buffer); - ok(25, buffer[1] == '\0'); - ok_int(26, 1, test_strlcat(buffer, "", sizeof(buffer))); - ok(27, buffer[1] == '\0'); + is_int(0, test_strlcat(buffer, "", sizeof(buffer)), + "correct count when appending empty string to empty buffer"); + is_string("", buffer, "...and buffer content is correct"); + is_int(3, test_strlcat(buffer, "foo", 2), "append to length 2 buffer"); + is_string("f", buffer, "...and got only a single character"); + ok(buffer[1] == '\0', "...and buffer is still nul-terminated"); + is_int(1, test_strlcat(buffer, "", sizeof(buffer)), + "append an empty string"); + ok(buffer[1] == '\0', "...and buffer is still nul-terminated"); return 0; } diff --git a/tests/portable/strlcpy-t.c b/tests/portable/strlcpy-t.c index 74c9ecd..ba224ba 100644 --- a/tests/portable/strlcpy-t.c +++ b/tests/portable/strlcpy-t.c @@ -1,31 +1,20 @@ /* * strlcpy test suite. * + * 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 * - * This code is derived from software contributed to the Internet Software - * Consortium by Rich Salz. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE - * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. + * See LICENSE for licensing terms. */ #include #include -#include +#include size_t test_strlcpy(char *, const char *, size_t); @@ -35,37 +24,43 @@ main(void) { char buffer[10]; - test_init(23); + plan(23); - ok_int(1, 3, test_strlcpy(buffer, "foo", sizeof(buffer))); - ok_string(2, "foo", buffer); - ok_int(3, 9, test_strlcpy(buffer, "hello wor", sizeof(buffer))); - ok_string(4, "hello wor", buffer); - ok_int(5, 10, test_strlcpy(buffer, "world hell", sizeof(buffer))); - ok_string(6, "world hel", buffer); - ok(7, buffer[9] == '\0'); - ok_int(8, 11, test_strlcpy(buffer, "hello world", sizeof(buffer))); - ok_string(9, "hello wor", buffer); - ok(10, buffer[9] == '\0'); + is_int(3, test_strlcpy(buffer, "foo", sizeof(buffer)), "simple strlcpy"); + is_string("foo", buffer, "...result is correct"); + is_int(9, test_strlcpy(buffer, "hello wor", sizeof(buffer)), + "strlcpy exact length of buffer"); + is_string("hello wor", buffer, "...result is correct"); + is_int(10, test_strlcpy(buffer, "world hell", sizeof(buffer)), + "strlcpy one more than buffer length"); + is_string("world hel", buffer, "...result is correct"); + ok(buffer[9] == '\0', "...buffer is nul-terminated"); + is_int(11, test_strlcpy(buffer, "hello world", sizeof(buffer)), + "strlcpy more than buffer length"); + is_string("hello wor", buffer, "...result is correct"); + ok(buffer[9] == '\0', "...buffer is nul-terminated"); /* Make sure that with a size of 0, the destination isn't changed. */ - ok_int(11, 3, test_strlcpy(buffer, "foo", 0)); - ok_string(12, "hello wor", buffer); + is_int(3, test_strlcpy(buffer, "foo", 0), "buffer unchanged if size 0"); + is_string("hello wor", buffer, "...contents still the same"); /* Now play with empty strings. */ - ok_int(13, 0, test_strlcpy(buffer, "", 0)); - ok_string(14, "hello wor", buffer); - ok_int(15, 0, test_strlcpy(buffer, "", sizeof(buffer))); - ok_string(16, "", buffer); - ok_int(17, 3, test_strlcpy(buffer, "foo", 2)); - ok_string(18, "f", buffer); - ok(19, buffer[1] == '\0'); - ok_int(20, 0, test_strlcpy(buffer, "", 1)); - ok(21, buffer[0] == '\0'); + is_int(0, test_strlcpy(buffer, "", 0), "copy empty string with size 0"); + is_string("hello wor", buffer, "...buffer unchanged"); + is_int(0, test_strlcpy(buffer, "", sizeof(buffer)), + "copy empty string into full buffer"); + is_string("", buffer, "...buffer now empty string"); + is_int(3, test_strlcpy(buffer, "foo", 2), + "copy string into buffer of size 2"); + is_string("f", buffer, "...got one character"); + ok(buffer[1] == '\0', "...buffer is nul-terminated"); + is_int(0, test_strlcpy(buffer, "", 1), + "copy empty string into buffer of size 1"); + ok(buffer[0] == '\0', "...buffer is empty string"); /* Finally, check using strlcpy as strlen. */ - ok_int(22, 3, test_strlcpy(NULL, "foo", 0)); - ok_int(23, 11, test_strlcpy(NULL, "hello world", 0)); + is_int(3, test_strlcpy(NULL, "foo", 0), "use strlcpy as strlen"); + is_int(11, test_strlcpy(NULL, "hello world", 0), "...again"); return 0; } diff --git a/tests/runtests.c b/tests/runtests.c index 060c8ad..1670012 100644 --- a/tests/runtests.c +++ b/tests/runtests.c @@ -17,12 +17,13 @@ * * 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). + * 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. * * 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 + * Copyright 2000, 2001, 2004, 2006, 2007, 2008, 2009 * Russ Allbery * * Permission is hereby granted, free of charge, to any person obtaining a @@ -44,16 +45,19 @@ * DEALINGS IN THE SOFTWARE. */ -#include -#include - #include #include #include +#include +#include +#include +#include #include #include +#include #include #include +#include /* sys/time.h must be included before sys/resource.h on some platforms. */ #include @@ -63,6 +67,19 @@ # define WCOREDUMP(status) ((unsigned)(status) & 0x80) #endif +/* + * The source and build versions of the tests directory. This is used to set + * the SOURCE and BUILD environment variables and find test programs, if set. + * Normally, this should be set as part of the build process to the test + * subdirectories of $(abs_top_srcdir) and $(abs_top_builddir) respectively. + */ +#ifndef SOURCE +# define SOURCE NULL +#endif +#ifndef BUILD +# define BUILD NULL +#endif + /* Test status codes. */ enum test_status { TEST_FAIL, @@ -78,7 +95,8 @@ enum test_status { /* Structure to hold data for a set of tests. */ struct testset { - const char *file; /* The file name of the test. */ + 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. */ @@ -89,6 +107,8 @@ struct testset { 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. */ + char *reason; /* Why all tests were skipped. */ }; /* Structure to hold a linked list of test sets. */ @@ -103,8 +123,7 @@ struct testlist { */ static const char banner[] = "\n\ Running all tests listed in %s. If any tests fail, run the failing\n\ -test program by hand to see more details. The test program will have the\n\ -same name as the test set but with \"-t\" appended.\n\n"; +test program with runtests -o to see more details.\n\n"; /* Header for reports of failed tests. */ static const char header[] = "\n\ @@ -115,22 +134,6 @@ Failed Set Fail/Total (%) Skip Stat Failing Tests\n\ #define xmalloc(size) x_malloc((size), __FILE__, __LINE__) #define xstrdup(p) x_strdup((p), __FILE__, __LINE__) -/* Internal prototypes. */ -static void sysdie(const char *format, ...); -static void *x_malloc(size_t, const char *file, int line); -static char *x_strdup(const char *, const char *file, int line); -static int test_analyze(struct testset *); -static int test_batch(const char *testlist); -static void test_checkline(const char *line, struct testset *); -static void test_fail_summary(const struct testlist *); -static int test_init(const char *line, struct testset *); -static int test_print_range(int first, int last, int chars, int limit); -static void test_summarize(struct testset *, int status); -static pid_t test_start(const char *path, int *fd); -static double tv_diff(const struct timeval *, const struct timeval *); -static double tv_seconds(const struct timeval *); -static double tv_sum(const struct timeval *, const struct timeval *); - /* * Report a fatal error, including the results of strerror, and exit. @@ -218,6 +221,19 @@ tv_sum(const struct timeval *tv1, const struct timeval *tv2) } +/* + * Given a pointer to a string, skip any leading whitespace and return a + * pointer to the first non-whitespace character. + */ +static const char * +skip_whitespace(const char *p) +{ + while (isspace((unsigned char)(*p))) + p++; + return 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 @@ -234,15 +250,34 @@ test_init(const char *line, struct testset *ts) * such as 1..10, accept that too for compatibility with Perl's * Test::Harness. */ - while (isspace((unsigned char)(*line))) - line++; + line = skip_whitespace(line); if (strncmp(line, "1..", 3) == 0) line += 3; - /* Get the count, check it for validity, and initialize the struct. */ - i = atoi(line); + /* + * 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("invalid test count"); + puts("ABORTED (invalid test count)"); ts->aborted = 1; ts->reported = 1; return 0; @@ -329,8 +364,28 @@ static void test_checkline(const char *line, struct testset *ts) { enum test_status status = TEST_PASS; + const char *bail; + char *end; int 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; + + length = strlen(bail); + if (bail[length - 1] == '\n') + length--; + test_backspace(ts); + printf("ABORTED (%.*s)\n", length, bail); + ts->reported = 1; + } + ts->aborted = 1; + return; + } + /* * If the given line isn't newline-terminated, it was too big for an * fgets(), which means ignore it. @@ -343,37 +398,40 @@ test_checkline(const char *line, struct testset *ts) status = TEST_FAIL; line += 4; } - if (strncmp(line, "ok ", 3) != 0) + if (strncmp(line, "ok", 2) != 0) return; - line += 3; - current = atoi(line); - if (current == 0) - return; - if (current < 0 || current > ts->count) { + line = skip_whitespace(line + 2); + errno = 0; + current = strtol(line, &end, 10); + if (errno != 0 || end == line) + current = ts->current + 1; + if (current <= 0 || current > ts->count) { test_backspace(ts); - printf("invalid test number %d\n", current); + printf("ABORTED (invalid test number %d)\n", current); ts->aborted = 1; ts->reported = 1; return; } - while (isspace((unsigned char)(*line))) - line++; + + /* + * Handle directives. We should probably do something more interesting + * with unexpected passes of todo tests. + */ while (isdigit((unsigned char)(*line))) line++; - while (isspace((unsigned char)(*line))) - line++; + line = skip_whitespace(line); if (*line == '#') { - line++; - while (isspace((unsigned char)(*line))) - line++; - if (strncmp(line, "skip", 4) == 0) + line = skip_whitespace(line + 1); + if (strncasecmp(line, "skip", 4) == 0) status = TEST_SKIP; + if (strncasecmp(line, "todo", 4) == 0) + status = (status == TEST_FAIL) ? TEST_SKIP : TEST_FAIL; } /* Make sure that the test number is in range and not a duplicate. */ if (ts->results[current - 1] != TEST_INVALID) { test_backspace(ts); - printf("duplicate test number %d\n", current); + printf("ABORTED (duplicate test number %d)\n", current); ts->aborted = 1; ts->reported = 1; return; @@ -449,9 +507,9 @@ test_summarize(struct testset *ts, int status) int last = 0; if (ts->aborted) { - fputs("aborted", stdout); + fputs("ABORTED", stdout); if (ts->count > 0) - printf(", passed %d/%d", ts->passed, ts->count - ts->skipped); + printf(" (passed %d/%d)", ts->passed, ts->count - ts->skipped); } else { for (i = 0; i < ts->count; i++) { if (ts->results[i] == TEST_INVALID) { @@ -520,19 +578,25 @@ test_analyze(struct testset *ts) { if (ts->reported) return 0; - if (WIFEXITED(ts->status) && WEXITSTATUS(ts->status) != 0) { + if (ts->all_skipped) { + if (ts->reason == NULL) + puts("skipped"); + else + printf("skipped (%s)\n", ts->reason); + return 1; + } else if (WIFEXITED(ts->status) && WEXITSTATUS(ts->status) != 0) { switch (WEXITSTATUS(ts->status)) { case CHILDERR_DUP: if (!ts->reported) - puts("can't dup file descriptors"); + puts("ABORTED (can't dup file descriptors)"); break; case CHILDERR_EXEC: if (!ts->reported) - puts("execution failed (not found?)"); + puts("ABORTED (execution failed -- not found?)"); break; case CHILDERR_STDERR: if (!ts->reported) - puts("can't open /dev/null"); + puts("ABORTED (can't open /dev/null)"); break; default: test_summarize(ts, WEXITSTATUS(ts->status)); @@ -561,17 +625,12 @@ test_run(struct testset *ts) int outfd, i, status; FILE *output; char buffer[BUFSIZ]; - char *file; /* * Initialize the test and our data structures, flagging this set in error * if the initialization fails. */ - file = xmalloc(strlen(ts->file) + 3); - strcpy(file, ts->file); - strcat(file, "-t"); - testpid = test_start(file, &outfd); - free(file); + testpid = test_start(ts->path, &outfd); output = fdopen(outfd, "r"); if (!output) { puts("ABORTED"); @@ -580,11 +639,8 @@ test_run(struct testset *ts) } if (!fgets(buffer, sizeof(buffer), output)) ts->aborted = 1; - if (!ts->aborted && !test_init(buffer, ts)) { - while (fgets(buffer, sizeof(buffer), output)) - ; + 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)) @@ -594,16 +650,23 @@ test_run(struct testset *ts) test_backspace(ts); /* - * Close the output descriptor, retrieve the exit status, and pass that - * information to test_analyze() for eventual output. + * Consume the rest of the test output, close the output descriptor, + * retrieve the exit status, and pass that information to test_analyze() + * for eventual output. */ + while (fgets(buffer, sizeof(buffer), output)) + ; fclose(output); child = waitpid(testpid, &ts->status, 0); if (child == (pid_t) -1) { - puts("ABORTED"); - fflush(stdout); + if (!ts->reported) { + puts("ABORTED"); + fflush(stdout); + } sysdie("waitpid for %u failed", (unsigned int) testpid); } + if (ts->all_skipped) + ts->aborted = 0; status = test_analyze(ts); /* Convert missing tests to failed tests. */ @@ -665,13 +728,54 @@ test_fail_summary(const struct testlist *fails) } +/* + * Given the name of a test, a pointer to the testset struct, and the source + * and build directories, find the test. We try first relative to the current + * directory, then in the build directory (if not NULL), then in the source + * directory. In each of those directories, we first try a "-t" extension and + * then a ".t" extension. When we find an executable program, we fill in the + * path member of the testset struct. If none of those paths are executable, + * just fill in the name of the test with "-t" appended. + * + * The caller is responsible for freeing the path member of the testset + * struct. + */ +static void +find_test(const char *name, struct testset *ts, const char *source, + const char *build) +{ + char *path; + const char *bases[] = { ".", build, source, NULL }; + int i; + + for (i = 0; bases[i] != NULL; i++) { + path = xmalloc(strlen(bases[i]) + strlen(name) + 4); + sprintf(path, "%s/%s-t", bases[i], name); + if (access(path, X_OK) != 0) + path[strlen(path) - 2] = '.'; + if (access(path, X_OK) == 0) + break; + free(path); + path = NULL; + } + if (path == NULL) { + path = xmalloc(strlen(name) + 3); + sprintf(path, "%s-t", name); + } + ts->path = path; +} + + /* * Run a batch of tests from a given file listing each test on a line by - * itself. The file must be rewindable. Returns true iff all tests + * itself. Takes two additional parameters: the root of the source directory + * and the root of the build directory. Test programs will be first searched + * for in the current directory, then the build directory, then the source + * directory. The file must be rewindable. Returns true iff all tests * passed. */ static int -test_batch(const char *testlist) +test_batch(const char *testlist, const char *source, const char *build) { FILE *tests; size_t length, i; @@ -741,7 +845,14 @@ test_batch(const char *testlist) fflush(stdout); memset(&ts, 0, sizeof(ts)); ts.file = xstrdup(buffer); - if (!test_run(&ts)) { + find_test(buffer, &ts, source, build); + ts.reason = NULL; + if (test_run(&ts)) { + free(ts.file); + free(ts.path); + if (ts.reason != NULL) + free(ts.reason); + } else { tmp = xmalloc(sizeof(struct testset)); memcpy(tmp, &ts, sizeof(struct testset)); if (!failhead) { @@ -757,9 +868,9 @@ test_batch(const char *testlist) } } aborted += ts.aborted; - total += ts.count; + total += ts.count + ts.all_skipped; passed += ts.passed; - skipped += ts.skipped; + skipped += ts.skipped + ts.all_skipped; failed += ts.failed; } total -= skipped; @@ -769,7 +880,8 @@ test_batch(const char *testlist) getrusage(RUSAGE_CHILDREN, &stats); /* Print out our final results. */ - if (failhead) test_fail_summary(failhead); + if (failhead) + test_fail_summary(failhead); putchar('\n'); if (aborted != 0) { if (aborted == 1) @@ -800,15 +912,80 @@ test_batch(const char *testlist) /* - * Main routine. Given a file listing tests, run each test listed. + * Run a single test case. This involves just running the test program after + * having done the environment setup and finding the test program. + */ +static void +test_single(const char *program, const char *source, const char *build) +{ + struct testset ts; + + memset(&ts, 0, sizeof(ts)); + find_test(program, &ts, source, build); + if (execl(ts.path, ts.path, (char *) 0) == -1) + sysdie("cannot exec %s", ts.path); +} + + +/* + * Main routine. Set the SOURCE and BUILD environment variables and then, + * given a file listing tests, run each test listed. */ int main(int argc, char *argv[]) { - if (argc != 2) { + int option; + int single = 0; + char *setting; + const char *list; + const char *source = SOURCE; + const char *build = BUILD; + + while ((option = getopt(argc, argv, "b:os:")) != EOF) { + switch (option) { + case 'b': + build = optarg; + break; + case 'o': + single = 1; + break; + case 's': + source = optarg; + break; + default: + exit(1); + } + } + argc -= optind; + argv += optind; + if (argc != 1) { fprintf(stderr, "Usage: runtests \n"); exit(1); } - printf(banner, argv[1]); - exit(test_batch(argv[1]) ? 0 : 1); + + if (source != NULL) { + setting = xmalloc(strlen("SOURCE=") + strlen(source) + 1); + sprintf(setting, "SOURCE=%s", source); + if (putenv(setting) != 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) + sysdie("cannot set BUILD in the environment"); + } + + if (single) { + test_single(argv[0], source, build); + exit(0); + } 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); + } } diff --git a/tests/tap/basic.c b/tests/tap/basic.c new file mode 100644 index 0000000..5ca9ff4 --- /dev/null +++ b/tests/tap/basic.c @@ -0,0 +1,356 @@ +/* + * 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 + * 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. + * + * Copyright 2009 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 + * + * See LICENSE for licensing terms. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * The test count. Always contains the number that will be used for the next + * test status. + */ +int testnum = 1; + +/* + * Status information stored so that we can give a test summary at the end of + * the test case. We store the planned final test and the count of failures. + * We can get the highest test count from testnum. + * + * 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. + */ +static int _planned = 0; +static int _failed = 0; +static pid_t _process = 0; + + +/* + * Our exit handler. Called on completion of the test to report a summary of + * results provided we're still in the original process. + */ +static void +finish(void) +{ + int highest = testnum - 1; + + if (_process != 0 && getpid() == _process && _planned > 0) { + if (_planned > highest) + printf("# Looks like you planned %d test%s but only ran %d\n", + _planned, (_planned > 1 ? "s" : ""), highest); + else if (_planned < highest) + printf("# Looks like you planned %d test%s but ran %d extra\n", + _planned, (_planned > 1 ? "s" : ""), highest - _planned); + else if (_failed > 0) + printf("# Looks like you failed %d test%s of %d\n", _failed, + (_failed > 1 ? "s" : ""), _planned); + else if (_planned > 1) + printf("# All %d tests successful or skipped\n", _planned); + else + printf("# %d test successful or skipped\n", _planned); + } +} + + +/* + * Initialize things. Turns on line buffering on stdout and then prints out + * the number of tests in the test suite. + */ +void +plan(int 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); + testnum = 1; + _planned = count; + _process = getpid(); + 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. + */ +void +skip_all(const char *format, ...) +{ + printf("1..0 # skip"); + if (format != NULL) { + va_list args; + + putchar(' '); + va_start(args, format); + vprintf(format, args); + va_end(args); + } + putchar('\n'); + exit(0); +} + + +/* + * Print the test description. + */ +static void +print_desc(const char *format, va_list args) +{ + printf(" - "); + vprintf(format, args); +} + + +/* + * Takes a boolean success value and assumes the test passes if that value + * is true and fails if that value is false. + */ +void +ok(int success, const char *format, ...) +{ + printf("%sok %d", success ? "" : "not ", testnum++); + if (!success) + _failed++; + if (format != NULL) { + va_list args; + + va_start(args, format); + print_desc(format, args); + va_end(args); + } + putchar('\n'); +} + + +/* + * Skip a test. + */ +void +skip(const char *reason, ...) +{ + printf("ok %d # skip", testnum++); + if (reason != NULL) { + va_list args; + + va_start(args, reason); + putchar(' '); + vprintf(reason, args); + va_end(args); + } + putchar('\n'); +} + + +/* + * Report the same status on the next count tests. + */ +void +ok_block(int count, int status, const char *format, ...) +{ + int i; + + for (i = 0; i < count; i++) { + printf("%sok %d", status ? "" : "not ", testnum++); + if (!status) + _failed++; + if (format != NULL) { + va_list args; + + va_start(args, format); + print_desc(format, args); + va_end(args); + } + putchar('\n'); + } +} + + +/* + * Skip the next count tests. + */ +void +skip_block(int count, const char *reason, ...) +{ + int i; + + for (i = 0; i < count; i++) { + printf("ok %d # skip", testnum++); + if (reason != NULL) { + va_list args; + + va_start(args, reason); + putchar(' '); + vprintf(reason, args); + va_end(args); + } + putchar('\n'); + } +} + + +/* + * Takes an expected integer and a seen integer and assumes the test passes + * if those two numbers match. + */ +void +is_int(int wanted, int seen, const char *format, ...) +{ + if (wanted == seen) + printf("ok %d", testnum++); + else { + printf("# wanted: %d\n# seen: %d\n", wanted, seen); + printf("not ok %d", testnum++); + _failed++; + } + if (format != NULL) { + va_list args; + + va_start(args, format); + print_desc(format, args); + va_end(args); + } + putchar('\n'); +} + + +/* + * Takes a string and what the string should be, and assumes the test passes + * if those strings match (using strcmp). + */ +void +is_string(const char *wanted, const char *seen, const char *format, ...) +{ + if (wanted == NULL) + wanted = "(null)"; + if (seen == NULL) + seen = "(null)"; + if (strcmp(wanted, seen) == 0) + printf("ok %d", testnum++); + else { + printf("# wanted: %s\n# seen: %s\n", wanted, seen); + printf("not ok %d", testnum++); + _failed++; + } + if (format != NULL) { + va_list args; + + va_start(args, format); + print_desc(format, args); + va_end(args); + } + putchar('\n'); +} + + +/* + * 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 %d", testnum++); + else { + printf("# wanted: %g\n# seen: %g\n", wanted, seen); + printf("not ok %d", 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. + */ +void +is_hex(unsigned long wanted, unsigned long seen, const char *format, ...) +{ + if (wanted == seen) + printf("ok %d", testnum++); + else { + printf("# wanted: %lx\n# seen: %lx\n", (unsigned long) wanted, + (unsigned long) seen); + printf("not ok %d", testnum++); + _failed++; + } + if (format != NULL) { + va_list args; + + va_start(args, format); + print_desc(format, args); + va_end(args); + } + putchar('\n'); +} + + +/* + * Bail out with an error. + */ +void +bail(const char *format, ...) +{ + va_list args; + + fflush(stdout); + printf("Bail out! "); + va_start(args, format); + vprintf(format, args); + va_end(args); + printf("\n"); + exit(1); +} + + +/* + * Bail out with an error, appending strerror(errno). + */ +void +sysbail(const char *format, ...) +{ + va_list args; + int oerrno = errno; + + fflush(stdout); + printf("Bail out! "); + va_start(args, format); + vprintf(format, args); + va_end(args); + printf(": %s\n", strerror(oerrno)); + exit(1); +} diff --git a/tests/tap/basic.h b/tests/tap/basic.h new file mode 100644 index 0000000..efe94ba --- /dev/null +++ b/tests/tap/basic.h @@ -0,0 +1,98 @@ +/* + * Basic utility routines for the TAP protocol. + * + * 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 + * + * See LICENSE for licensing terms. + */ + +#ifndef TAP_BASIC_H +#define TAP_BASIC_H 1 + +#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 + +/* + * Used for iterating through arrays. ARRAY_SIZE returns the number of + * elements in the array (useful for a < upper bound in a for loop) and + * ARRAY_END returns a pointer to the element past the end (ISO C99 makes it + * legal to refer to such a pointer as long as it's never dereferenced). + */ +#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) +#define ARRAY_END(array) (&(array)[ARRAY_SIZE(array)]) + +BEGIN_DECLS + +/* + * The test count. Always contains the number that will be used for the next + * test status. + */ +extern int testnum; + +/* Print out the number of tests and set standard output to line buffered. */ +void plan(int count); + +/* Skip the entire test suite. Call instead of plan. */ +void skip_all(const char *format, ...) + __attribute__((__noreturn__, __format__(printf, 1, 2))); + +/* Basic reporting functions. */ +void ok(int success, const char *format, ...) + __attribute__((__format__(printf, 2, 3))); +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, ...) + __attribute__((__format__(printf, 3, 4))); +void skip_block(int 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, ...) + __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, ...) + __attribute__((__format__(printf, 3, 4))); + +/* Bail out with an error. sysbail appends strerror(errno). */ +void bail(const char *format, ...) + __attribute__((__noreturn__, __nonnull__, __format__(printf, 1, 2))); +void sysbail(const char *format, ...) + __attribute__((__noreturn__, __nonnull__, __format__(printf, 1, 2))); + +END_DECLS + +#endif /* LIBTEST_H */ diff --git a/tests/tap/kerberos.c b/tests/tap/kerberos.c new file mode 100644 index 0000000..700212e --- /dev/null +++ b/tests/tap/kerberos.c @@ -0,0 +1,164 @@ +/* + * 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. + * + * Copyright 2006, 2007, 2009, 2010 + * Board of Trustees, Leland Stanford Jr. University + * + * See LICENSE for licensing terms. + */ + +#include +#include +#include + +#include +#include +#include +#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 + * 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(). + * + * 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. + */ +char * +kerberos_setup(void) +{ + char *path, *krbtgt; + const char *build, *realm; + FILE *file; + char principal[BUFSIZ]; + krb5_error_code code; + krb5_context ctx; + krb5_ccache ccache; + krb5_principal kprinc; + krb5_keytab keytab; + krb5_get_init_creds_opt *opts; + krb5_creds creds; + + /* Read the principal name and find the keytab file. */ + path = find_file("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 = find_file("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. */ + code = krb5_init_context(&ctx); + if (code != 0) + bail("error initializing Kerberos"); + code = krb5_cc_default(ctx, &ccache); + if (code != 0) + bail("error setting ticket cache"); + code = krb5_parse_name(ctx, principal, &kprinc); + if (code != 0) + bail("error parsing principal %s", principal); + realm = krb5_principal_get_realm(ctx, kprinc); + krbtgt = concat("krbtgt/", realm, "@", realm, (char *) 0); + code = krb5_kt_resolve(ctx, path, &keytab); + if (code != 0) + bail("cannot open keytab %s", path); + code = krb5_get_init_creds_opt_alloc(ctx, &opts); + if (code != 0) + bail("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"); + code = krb5_cc_initialize(ctx, ccache, kprinc); + if (code != 0) + bail("error initializing ticket cache"); + code = krb5_cc_store_cred(ctx, ccache, &creds); + if (code != 0) + bail("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_free_context(ctx); + free(krbtgt); + free(path); + + return xstrdup(principal); +} + + +/* + * Clean up at the end of a test. Currently, all this does is remove the + * ticket cache. + */ +void +kerberos_cleanup(void) +{ + char *path; + + path = concatpath(getenv("BUILD"), "data/test.cache"); + unlink(path); + free(path); +} diff --git a/tests/tap/kerberos.h b/tests/tap/kerberos.h new file mode 100644 index 0000000..1c64f70 --- /dev/null +++ b/tests/tap/kerberos.h @@ -0,0 +1,32 @@ +/* + * Utility functions for tests that use Kerberos. + * + * Copyright 2006, 2007, 2009 + * Board of Trustees, Leland Stanford Jr. University + * + * See LICENSE for licensing terms. + */ + +#ifndef TAP_KERBEROS_H +#define TAP_KERBEROS_H 1 + +#include +#include + +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(). + */ +char *kerberos_setup(void) + __attribute__((__malloc__)); + +/* Clean up at the end of a test. */ +void kerberos_cleanup(void); + +END_DECLS + +#endif /* !TAP_MESSAGES_H */ diff --git a/tests/tap/kerberos.sh b/tests/tap/kerberos.sh new file mode 100644 index 0000000..da07e66 --- /dev/null +++ b/tests/tap/kerberos.sh @@ -0,0 +1,48 @@ +# Shell function library to initialize Kerberos credentials +# +# Written by Russ Allbery +# Copyright 2009 Board of Trustees, Leland Stanford Jr. University +# +# See LICENSE for licensing terms. + +# 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='' + 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 + if [ -z "$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 +# 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 +} diff --git a/tests/tap/messages.c b/tests/tap/messages.c new file mode 100644 index 0000000..3bb9a1a --- /dev/null +++ b/tests/tap/messages.c @@ -0,0 +1,80 @@ +/* + * Utility functions to test message handling. + * + * These functions set up a message handler to trap warn and notice output + * 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 + * + * See LICENSE for licensing terms. + */ + +#include +#include + +#include +#include +#include +#include +#include + +/* A global buffer into which message_log_buffer stores error messages. */ +char *errors = NULL; + + +/* + * An error handler that appends all errors to the errors global. Used by + * error_capture. + */ +static void +message_log_buffer(int len, 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 { + char *new_errors; + + new_errors = concat(errors, message, "\n", (char *) 0); + free(errors); + errors = new_errors; + } + free(message); +} + + +/* + * Turn on the capturing of errors. Errors will be stored in the global + * errors variable where they can be checked by the test suite. Capturing is + * turned off with errors_uncapture. + */ +void +errors_capture(void) +{ + if (errors != NULL) { + free(errors); + errors = NULL; + } + message_handlers_warn(1, message_log_buffer); + message_handlers_notice(1, message_log_buffer); +} + + +/* + * Turn off the capturing of errors again. + */ +void +errors_uncapture(void) +{ + message_handlers_warn(1, message_log_stderr); + message_handlers_notice(1, message_log_stdout); +} diff --git a/tests/tap/messages.h b/tests/tap/messages.h new file mode 100644 index 0000000..2b9a7db --- /dev/null +++ b/tests/tap/messages.h @@ -0,0 +1,35 @@ +/* + * Utility functions to test message 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 + * + * See LICENSE for licensing terms. + */ + +#ifndef TAP_MESSAGES_H +#define TAP_MESSAGES_H 1 + +#include +#include + +/* A global buffer into which errors_capture stores errors. */ +extern char *errors; + +BEGIN_DECLS + +/* + * Turn on capturing of errors with errors_capture. Errors reported by warn + * will be stored in the global errors variable. Turn this off again with + * errors_uncapture. Caller is responsible for freeing errors when done. + */ +void errors_capture(void); +void errors_uncapture(void); + +END_DECLS + +#endif /* !TAP_MESSAGES_H */ diff --git a/tests/tap/process.c b/tests/tap/process.c new file mode 100644 index 0000000..16154c7 --- /dev/null +++ b/tests/tap/process.c @@ -0,0 +1,100 @@ +/* + * Utility functions for tests that use subprocesses. + * + * Provides utility functions for subprocess manipulation. Currently, only + * one utility function is provided: is_function_output, which runs a function + * in a subprocess and checks its output and exit status against expected + * values. + * + * 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 + * + * See LICENSE for licensing terms. + */ + +#include +#include + +#include + +#include +#include +#include + + +/* + * Given a function, an expected exit status, and expected output, runs that + * function in a subprocess, capturing stdout and stderr via a pipe, and + * compare the combination of stdout and stderr with the expected output and + * the exit status with the expected status. Expects the function to always + * exit (not die from a signal). + */ +void +is_function_output(test_function_type function, int status, const char *output, + const char *format, ...) +{ + int fds[2]; + pid_t child; + char *buf, *msg; + ssize_t count, ret, buflen; + int rval; + va_list args; + + /* Flush stdout before we start to avoid odd forking issues. */ + fflush(stdout); + + /* Set up the pipe and call the function, collecting its output. */ + if (pipe(fds) == -1) + sysbail("can't create pipe"); + child = fork(); + if (child == (pid_t) -1) { + sysbail("can't fork"); + } else if (child == 0) { + /* In child. Set up our stdout and stderr. */ + close(fds[0]); + if (dup2(fds[1], 1) == -1) + _exit(255); + if (dup2(fds[1], 2) == -1) + _exit(255); + + /* Now, run the function and exit successfully if it returns. */ + (*function)(); + fflush(stdout); + _exit(0); + } else { + /* + * In the parent; close the extra file descriptor, read the output if + * any, and then collect the exit status. + */ + close(fds[1]); + buflen = BUFSIZ; + buf = xmalloc(buflen); + count = 0; + do { + ret = read(fds[0], buf + count, buflen - count - 1); + if (ret > 0) + count += ret; + if (count >= buflen - 1) { + buflen += BUFSIZ; + buf = xrealloc(buf, buflen); + } + } while (ret > 0); + buf[count < 0 ? 0 : count] = '\0'; + if (waitpid(child, &rval, 0) == (pid_t) -1) + sysbail("waitpid failed"); + } + + /* Now, check the results against what we expected. */ + va_start(args, format); + if (xvasprintf(&msg, format, args) < 0) + bail("cannot format test description"); + va_end(args); + ok(WIFEXITED(rval), "%s (exited)", msg); + is_int(status, WEXITSTATUS(rval), "%s (status)", msg); + is_string(output, buf, "%s (output)", msg); + free(buf); + free(msg); +} diff --git a/tests/tap/process.h b/tests/tap/process.h new file mode 100644 index 0000000..b7d3b11 --- /dev/null +++ b/tests/tap/process.h @@ -0,0 +1,37 @@ +/* + * Utility functions for tests that use subprocesses. + * + * 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 + * + * See LICENSE for licensing terms. + */ + +#ifndef TAP_PROCESS_H +#define TAP_PROCESS_H 1 + +#include +#include + +BEGIN_DECLS + +/* + * Run a function in a subprocess and check the exit status and expected + * output (stdout and stderr combined) against the provided values. Expects + * the function to always exit (not die from a signal). + * + * This reports as three separate tests: whether the function exited rather + * than was killed, whether the exit status was correct, and whether the + * output was correct. + */ +typedef void (*test_function_type)(void); +void is_function_output(test_function_type, int status, const char *output, + const char *format, ...) + __attribute__((__format__(printf, 4, 5))); + +END_DECLS + +#endif /* TAP_PROCESS_H */ diff --git a/tests/tap/remctl.sh b/tests/tap/remctl.sh new file mode 100644 index 0000000..b9667ef --- /dev/null +++ b/tests/tap/remctl.sh @@ -0,0 +1,46 @@ +# Shell function library to start and stop remctld +# +# Written by Russ Allbery +# Copyright 2009 Board of Trustees, Leland Stanford Jr. University +# +# See LICENSE for licensing terms. + +# Start remctld. Takes the path to remctld, which may be found via configure, +# and the path to the configuration file. +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 + 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 \ + -S -F -k "$keytab" &) + [ -f "$BUILD/data/remctld.pid" ] || sleep 5 + else + ( "$1" -m -p 14373 -s "$principal" -P "$BUILD/data/remctld.pid" \ + -f "$2" -d -S -F -k "$keytab" &) + fi + [ -f "$BUILD/data/remctld.pid" ] || sleep 1 + if [ ! -f "$BUILD/data/remctld.pid" ] ; then + bail 'remctld did not start' + fi +} + +# Stop remctld and clean up. +remctld_stop () { + if [ -f "$BUILD/data/remctld.pid" ] ; then + kill -TERM `cat "$BUILD/data/remctld.pid"` + rm -f "$BUILD/data/remctld.pid" + fi +} diff --git a/tests/util/concat-t.c b/tests/util/concat-t.c index 81824c8..ca7de2c 100644 --- a/tests/util/concat-t.c +++ b/tests/util/concat-t.c @@ -1,58 +1,46 @@ /* * concat test suite. * - * Copyright 2004, 2005, 2006 + * 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 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, - * 2003 by The Internet Software Consortium and Rich Salz + * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + * 2002, 2003 by The Internet Software Consortium and Rich Salz * - * This code is derived from software contributed to the Internet Software - * Consortium by Rich Salz. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE - * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. + * See LICENSE for licensing terms. */ #include #include -#include -#include +#include +#include #define END (char *) 0 - /* * Memory leaks everywhere! Whoo-hoo! */ int main(void) { - test_init(13); - - ok_string( 1, "a", concat("a", END)); - ok_string( 2, "ab", concat("a", "b", END)); - ok_string( 3, "ab", concat("ab", "", END)); - ok_string( 4, "ab", concat("", "ab", END)); - ok_string( 5, "", concat("", END)); - ok_string( 6, "abcde", concat("ab", "c", "", "de", END)); - ok_string( 7, "abcde", concat("abc", "de", END, "f", END)); - - ok_string( 8, "/foo", concatpath("/bar", "/foo")); - ok_string( 9, "/foo/bar", concatpath("/foo", "bar")); - ok_string(10, "./bar", concatpath("/foo", "./bar")); - ok_string(11, "/bar/baz/foo/bar", concatpath("/bar/baz", "foo/bar")); - ok_string(12, "./foo", concatpath(NULL, "foo")); - ok_string(13, "/foo/bar", concatpath(NULL, "/foo/bar")); + plan(13); + + is_string("a", concat("a", END), "concat 1"); + is_string("ab", concat("a", "b", END), "concat 2"); + is_string("ab", concat("ab", "", END), "concat 3"); + is_string("ab", concat("", "ab", END), "concat 4"); + is_string("", concat("", END), "concat 5"); + is_string("abcde", concat("ab", "c", "", "de", END), "concat 6"); + is_string("abcde", concat("abc", "de", END, "f", END), "concat 7"); + + is_string("/foo", concatpath("/bar", "/foo"), "path 1"); + is_string("/foo/bar", concatpath("/foo", "bar"), "path 2"); + is_string("./bar", concatpath("/foo", "./bar"), "path 3"); + is_string("/bar/baz/foo/bar", concatpath("/bar/baz", "foo/bar"), "path 4"); + is_string("./foo", concatpath(NULL, "foo"), "path 5"); + is_string("/foo/bar", concatpath(NULL, "/foo/bar"), "path 6"); return 0; } diff --git a/tests/util/messages-krb5-t.c b/tests/util/messages-krb5-t.c new file mode 100644 index 0000000..02d8f92 --- /dev/null +++ b/tests/util/messages-krb5-t.c @@ -0,0 +1,99 @@ +/* + * Test suite for Kerberos error handling routines. + * + * Written by Russ Allbery + * Copyright 2010 Board of Trustees, Leland Stanford Jr. University + * + * See LICENSE for licensing terms. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + + +/* + * Test functions. + */ +static void +test_warn(void) +{ + krb5_context ctx; + krb5_error_code code; + krb5_principal princ; + + code = krb5_init_context(&ctx); + if (code < 0) + die_krb5(ctx, code, "cannot create context"); + code = krb5_parse_name(ctx, "foo@bar@EXAMPLE.COM", &princ); + if (code < 0) + warn_krb5(ctx, code, "principal parse failed"); + else + die("unexpected success parsing principal"); + exit(0); +} + +static void +test_die(void) +{ + krb5_context ctx; + krb5_error_code code; + krb5_principal princ; + + code = krb5_init_context(&ctx); + if (code < 0) + die_krb5(ctx, code, "cannot create context"); + code = krb5_parse_name(ctx, "foo@bar@EXAMPLE.COM", &princ); + if (code < 0) + die_krb5(ctx, code, "principal parse failed"); + else + die("unexpected success parsing principal"); + exit(0); +} + + +/* + * Run the tests. + */ +int +main(void) +{ + krb5_context ctx; + krb5_error_code code; + krb5_principal princ; + const char *message; + char *wanted; + + plan(6 * 3); + + /* First, we have to get what the correct error message is. */ + code = krb5_init_context(&ctx); + if (code < 0) + bail("cannot create context"); + code = krb5_parse_name(ctx, "foo@bar@EXAMPLE.COM", &princ); + message = krb5_get_error_message(ctx, code); + + xasprintf(&wanted, "principal parse failed: %s\n", message); + is_function_output(test_warn, 0, wanted, "warn_krb5"); + is_function_output(test_die, 1, wanted, "die_krb5"); + free(wanted); + + message_program_name = "msg-test"; + xasprintf(&wanted, "msg-test: principal parse failed: %s\n", message); + is_function_output(test_warn, 0, wanted, "warn_krb5 with name"); + is_function_output(test_die, 1, wanted, "die_krb5 with name"); + free(wanted); + + message_handlers_warn(0); + is_function_output(test_warn, 0, "", "warn_krb5 with no handlers"); + message_handlers_die(0); + is_function_output(test_die, 1, "", "warn_krb5 with no handlers"); + + return 0; +} diff --git a/tests/util/messages-t.c b/tests/util/messages-t.c index 3f7860e..fb82a42 100644 --- a/tests/util/messages-t.c +++ b/tests/util/messages-t.c @@ -1,25 +1,14 @@ /* * Test suite for error handling routines. * - * Copyright 2004, 2005, 2006 + * 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 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, - * 2003 by The Internet Software Consortium and Rich Salz + * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + * 2002, 2003 by The Internet Software Consortium and Rich Salz * - * This code is derived from software contributed to the Internet Software - * Consortium by Rich Salz. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE - * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. + * See LICENSE for licensing terms. */ #include @@ -30,67 +19,11 @@ #include #include -#include -#include - -#define END (char *) 0 - -/* Test function type. */ -typedef void (*test_function_t)(void); - - -/* - * Fork and execute the provided function, connecting stdout and stderr to a - * pipe. Captures the output into the provided buffer and returns the exit - * status as a waitpid status value. - */ -static int -run_test(test_function_t function, char *buf, size_t buflen) -{ - int fds[2]; - pid_t child; - ssize_t count, status; - int rval; - - /* Flush stdout before we start to avoid odd forking issues. */ - fflush(stdout); - - /* Set up the pipe and call the function, collecting its output. */ - if (pipe(fds) == -1) - sysdie("can't create pipe"); - child = fork(); - if (child == (pid_t) -1) { - sysdie("can't fork"); - } else if (child == 0) { - /* In child. Set up our stdout and stderr. */ - close(fds[0]); - if (dup2(fds[1], 1) == -1) - _exit(255); - if (dup2(fds[1], 2) == -1) - _exit(255); - - /* Now, run the function and exit successfully if it returns. */ - (*function)(); - fflush(stdout); - _exit(0); - } else { - /* - * In the parent; close the extra file descriptor, read the output if - * any, and then collect the exit status. - */ - close(fds[1]); - count = 0; - do { - status = read(fds[0], buf + count, buflen - count - 1); - if (status > 0) - count += status; - } while (status > 0); - buf[count < 0 ? 0 : count] = '\0'; - if (waitpid(child, &rval, 0) == (pid_t) -1) - sysdie("waitpid failed"); - } - return rval; -} +#include +#include +#include +#include +#include /* @@ -203,43 +136,20 @@ static void test24(void) { /* - * Given the test number, intended exit status and message, and the function - * to run, print ok or not ok. - */ -static void -test_error(int n, int status, const char *output, test_function_t function) -{ - int real_status; - char buf[256]; - int succeeded = 1; - - real_status = run_test(function, buf, sizeof(buf)); - if (!WIFEXITED(real_status) || status != WEXITSTATUS(real_status)) { - printf(" unexpected exit status %d\n", real_status); - succeeded = 0; - } - if (strcmp(output, buf)) { - printf(" unexpected output: %s", buf); - printf(" expected output: %s", output); - succeeded = 0; - } - printf("%sok %d\n", succeeded ? "" : "not ", n); -} - - -/* - * Given the test number, intended status, intended message sans the appended - * strerror output, errno, and the function to run, print ok or not ok. + * Given the intended status, intended message sans the appended strerror + * output, errno, and the function to run, check the output. */ static void -test_strerror(int n, int status, const char *output, int error, - test_function_t function) +test_strerror(int status, const char *output, int error, + test_function_type function) { - char *full_output; + char *full_output, *name; - full_output = concat(output, ": ", strerror(error), "\n", END); - test_error(n, status, full_output, function); + full_output = concat(output, ": ", strerror(error), "\n", (char *) NULL); + xasprintf(&name, "strerror %d", testnum / 3 + 1); + is_function_output(function, status, full_output, name); free(full_output); + free(name); } @@ -250,46 +160,47 @@ int main(void) { char buff[32]; - - test_init(24); - - test_error(1, 0, "warning\n", test1); - test_error(2, 1, "fatal\n", test2); - test_strerror(3, 0, "permissions", EPERM, test3); - test_strerror(4, 1, "fatal access", EACCES, test4); - test_error(5, 0, "test5: warning\n", test5); - test_error(6, 1, "test6: fatal\n", test6); - test_strerror(7, 0, "test7: perms 7", EPERM, test7); - test_strerror(8, 1, "test8: fatal", EACCES, test8); - test_error(9, 10, "fatal\n", test9); - test_strerror(10, 10, "fatal perm", EPERM, test10); - test_strerror(11, 10, "1st test11: fatal", EPERM, test11); - test_error(12, 0, "7 0 warning\n", test12); - test_error(13, 1, "5 0 fatal\n", test13); + char *output; + + plan(24 * 3); + + is_function_output(test1, 0, "warning\n", "test1"); + is_function_output(test2, 1, "fatal\n", "test2"); + test_strerror(0, "permissions", EPERM, test3); + test_strerror(1, "fatal access", EACCES, test4); + is_function_output(test5, 0, "test5: warning\n", "test5"); + is_function_output(test6, 1, "test6: fatal\n", "test6"); + test_strerror(0, "test7: perms 7", EPERM, test7); + test_strerror(1, "test8: fatal", EACCES, test8); + is_function_output(test9, 10, "fatal\n", "test9"); + test_strerror(10, "fatal perm", EPERM, test10); + test_strerror(10, "1st test11: fatal", EPERM, test11); + is_function_output(test12, 0, "7 0 warning\n", "test12"); + is_function_output(test13, 1, "5 0 fatal\n", "test13"); sprintf(buff, "%d", EPERM); - test_error(14, 0, - concat("7 ", buff, " warning\n7 ", buff, " warning\n", END), - test14); - test_error(15, 10, - concat("5 ", buff, " fatal\n5 ", buff, " fatal\n", END), - test15); - test_error(16, 0, - concat("test16: warning: ", strerror(EPERM), "\n7 ", buff, - " warning\n", END), - test16); - - test_error(17, 0, "notice\n", test17); - test_error(18, 0, "test18: notice\n", test18); - test_error(19, 0, "", test19); - test_error(20, 0, "3 0 foo\n", test20); - test_error(21, 0, "test23: baz\n", test21); + xasprintf(&output, "7 %d warning\n7 %d warning\n", EPERM, EPERM); + is_function_output(test14, 0, output, "test14"); + free(output); + xasprintf(&output, "5 %d fatal\n5 %d fatal\n", EPERM, EPERM); + is_function_output(test15, 10, output, "test15"); + free(output); + xasprintf(&output, "test16: warning: %s\n7 %d warning\n", strerror(EPERM), + EPERM); + is_function_output(test16, 0, output, "test16"); + free(output); + + is_function_output(test17, 0, "notice\n", "test17"); + is_function_output(test18, 0, "test18: notice\n", "test18"); + is_function_output(test19, 0, "", "test19"); + is_function_output(test20, 0, "3 0 foo\n", "test20"); + is_function_output(test21, 0, "test23: baz\n", "test21"); /* Make sure that it's possible to turn off a message type entirely. */ - test_error(22, 1, "", test22); - test_error(23, 0, "", test23); - test_error(24, 0, "first\nthird\n", test24); + is_function_output(test22, 1, "", "test22"); + is_function_output(test23, 0, "", "test23"); + is_function_output(test24, 0, "first\nthird\n", "test24"); return 0; } diff --git a/tests/util/xmalloc-t b/tests/util/xmalloc-t new file mode 100755 index 0000000..02f54b5 --- /dev/null +++ b/tests/util/xmalloc-t @@ -0,0 +1,127 @@ +#! /bin/sh +# +# Test suite for xmalloc and friends. +# +# Written by Russ Allbery +# Copyright 2008, 2009, 2010 Board of Trustees, Leland Stanford Jr. University +# Copyright 2004, 2005, 2006 +# by Internet Systems Consortium, Inc. ("ISC") +# Copyright 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003 by The Internet Software Consortium and Rich Salz +# +# See LICENSE for licensing terms. + +. "$SOURCE/tap/libtap.sh" +cd "$BUILD/util" + +# Run an xmalloc test. Takes the description, the expectd exit status, the +# output, and the arguments. +ok_xmalloc () { + local desc w_status w_output output status + desc="$1" + shift + w_status="$1" + shift + w_output="$1" + shift + output=`./xmalloc "$@" 2>&1` + status=$? + if [ "$w_status" -ne 0 ] ; then + output=`echo "$output" | sed 's/:.*//'` + fi + if [ $status = $w_status ] && [ x"$output" = x"$w_output" ] ; then + ok "$desc" true + elif [ $status = 2 ] ; then + skip "no data limit support" + else + echo "# saw: ($status) $output" + echo "# not: ($w_status) $w_output" + ok "$desc" false + fi +} + +# Skip this test suite unless maintainer-mode tests are enabled. All of the +# failures in automated testing have been problems with the assumptions around +# memory allocation or problems with the test suite, not problems with the +# underlying xmalloc code. +if [ -z "$RRA_MAINTAINER_TESTS" ] ; then + skip_all 'xmalloc tests only run for maintainer' +fi + +# Total tests. +plan 36 + +# First run the tests expected to succeed. +ok_xmalloc "malloc small" 0 "" "m" "21" "0" +ok_xmalloc "malloc large" 0 "" "m" "3500000" "0" +ok_xmalloc "malloc zero" 0 "" "m" "0" "0" +ok_xmalloc "realloc small" 0 "" "r" "21" "0" +ok_xmalloc "realloc large" 0 "" "r" "3500000" "0" +ok_xmalloc "strdup small" 0 "" "s" "21" "0" +ok_xmalloc "strdup large" 0 "" "s" "3500000" "0" +ok_xmalloc "strndup small" 0 "" "n" "21" "0" +ok_xmalloc "strndup large" 0 "" "n" "3500000" "0" +ok_xmalloc "calloc small" 0 "" "c" "24" "0" +ok_xmalloc "calloc large" 0 "" "c" "3500000" "0" +ok_xmalloc "asprintf small" 0 "" "a" "24" "0" +ok_xmalloc "asprintf large" 0 "" "a" "3500000" "0" +ok_xmalloc "vasprintf small" 0 "" "v" "24" "0" +ok_xmalloc "vasprintf large" 0 "" "v" "3500000" "0" + +# Now limit our memory to 3.5MB and then try the large ones again, all of +# which should fail. +# +# The exact memory limits used here are essentially black magic. They need to +# be large enough to allow the program to be loaded and do small allocations, +# but not so large that we can't reasonably expect to allocate that much +# memory normally. 3.5MB seems to work reasonably well on both Solaris and +# Linux. +# +# We assume that there are enough miscellaneous allocations that an allocation +# exactly as large as the limit will always fail. +ok_xmalloc "malloc fail" 1 \ + "failed to malloc 3500000 bytes at xmalloc.c line 38" \ + "m" "3500000" "3500000" +ok_xmalloc "realloc fail" 1 \ + "failed to realloc 3500000 bytes at xmalloc.c line 66" \ + "r" "3500000" "3500000" +ok_xmalloc "strdup fail" 1 \ + "failed to strdup 3500000 bytes at xmalloc.c line 97" \ + "s" "3500000" "3500000" +ok_xmalloc "strndup fail" 1 \ + "failed to strndup 3500000 bytes at xmalloc.c line 124" \ + "n" "3500000" "3500000" +ok_xmalloc "calloc fail" 1 \ + "failed to calloc 3500000 bytes at xmalloc.c line 148" \ + "c" "3500000" "3500000" +ok_xmalloc "asprintf fail" 1 \ + "failed to asprintf 3500000 bytes at xmalloc.c line 173" \ + "a" "3500000" "3500000" +ok_xmalloc "vasprintf fail" 1 \ + "failed to vasprintf 3500000 bytes at xmalloc.c line 193" \ + "v" "3500000" "3500000" + +# Check our custom error handler. +ok_xmalloc "malloc custom" 1 "malloc 3500000 xmalloc.c 38" \ + "M" "3500000" "3500000" +ok_xmalloc "realloc custom" 1 "realloc 3500000 xmalloc.c 66" \ + "R" "3500000" "3500000" +ok_xmalloc "strdup custom" 1 "strdup 3500000 xmalloc.c 97" \ + "S" "3500000" "3500000" +ok_xmalloc "strndup custom" 1 "strndup 3500000 xmalloc.c 124" \ + "N" "3500000" "3500000" +ok_xmalloc "calloc custom" 1 "calloc 3500000 xmalloc.c 148" \ + "C" "3500000" "3500000" +ok_xmalloc "asprintf custom" 1 "asprintf 3500000 xmalloc.c 173" \ + "A" "3500000" "3500000" +ok_xmalloc "vasprintf custom" 1 "vasprintf 3500000 xmalloc.c 193" \ + "V" "3500000" "3500000" + +# Check the smaller ones again just for grins. +ok_xmalloc "malloc retry" 0 "" "m" "21" "3500000" +ok_xmalloc "realloc retry" 0 "" "r" "32" "3500000" +ok_xmalloc "strdup retry" 0 "" "s" "64" "3500000" +ok_xmalloc "strndup retry" 0 "" "n" "20" "3500000" +ok_xmalloc "calloc retry" 0 "" "c" "24" "3500000" +ok_xmalloc "asprintf retry" 0 "" "a" "30" "3500000" +ok_xmalloc "vasprintf retry" 0 "" "v" "35" "3500000" diff --git a/tests/util/xmalloc-t.in b/tests/util/xmalloc-t.in deleted file mode 100644 index 5c18512..0000000 --- a/tests/util/xmalloc-t.in +++ /dev/null @@ -1,126 +0,0 @@ -#! /bin/sh -# -# Test suite for xmalloc and friends. -# -# Copyright 2004, 2005, 2006 -# by Internet Systems Consortium, Inc. ("ISC") -# Copyright 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003 by The Internet Software Consortium and Rich Salz -# -# This code is derived from software contributed to the Internet Software -# Consortium by Rich Salz. -# -# Permission to use, copy, modify, and distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH -# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, -# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE -# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -# PERFORMANCE OF THIS SOFTWARE. - -# The count starts at 1 and is updated each time ok is printed. printcount -# takes "ok" or "not ok". -count=1 -printcount () { - echo "$1 $count $2" - count=`expr $count + 1` -} - -# Run a program expected to succeed, and print ok if it does. -runsuccess () { - output=`$xmalloc "$1" "$2" "$3" 2>&1 >/dev/null` - status=$? - if test $status = 0 && test -z "$output" ; then - printcount "ok" - else - if test $status = 2 ; then - printcount "ok" "# skip - no data limit support" - else - printcount "not ok" - echo " $output" - fi - fi -} - -# Run a program expected to fail and make sure it fails with an exit status -# of 2 and the right failure message. Strip the colon and everything after -# it off the error message since it's system-specific. -runfailure () { - output=`$xmalloc "$1" "$2" "$3" 2>&1 >/dev/null` - status=$? - output=`echo "$output" | sed 's/:.*//' \ - | sed 's% [^ ]*/xmalloc.c% xmalloc.c%'` - if test $status = 1 && test x"$output" = x"$4" ; then - printcount "ok" - else - if test $status = 2 ; then - printcount "ok" "# skip - no data limit support" - else - printcount "not ok" - echo " saw: $output" - echo " not: $4" - fi - fi -} - -# Find where the helper program is. -xmalloc="@abs_top_builddir@/tests/util/xmalloc" - -# Total tests. -echo 36 - -# First run the tests expected to succeed. -runsuccess "m" "21" "0" -runsuccess "m" "128000" "0" -runsuccess "m" "0" "0" -runsuccess "r" "21" "0" -runsuccess "r" "128000" "0" -runsuccess "s" "21" "0" -runsuccess "s" "128000" "0" -runsuccess "n" "21" "0" -runsuccess "n" "128000" "0" -runsuccess "c" "24" "0" -runsuccess "c" "128000" "0" -runsuccess "a" "24" "0" -runsuccess "a" "128000" "0" -runsuccess "v" "24" "0" -runsuccess "v" "128000" "0" - -# Now limit our memory to 120KB and then try the large ones again, all of -# which should fail. -runfailure "m" "128000" "120000" \ - "failed to malloc 128000 bytes at xmalloc.c line 61" -runfailure "r" "128000" "120000" \ - "failed to realloc 128000 bytes at xmalloc.c line 90" -runfailure "s" "64000" "120000" \ - "failed to strdup 64000 bytes at xmalloc.c line 121" -runfailure "n" "64000" "120000" \ - "failed to strndup 64000 bytes at xmalloc.c line 148" -runfailure "c" "128000" "120000" \ - "failed to calloc 128000 bytes at xmalloc.c line 172" -runfailure "a" "64000" "120000" \ - "failed to asprintf 64000 bytes at xmalloc.c line 241" -runfailure "v" "64000" "120000" \ - "failed to vasprintf 64000 bytes at xmalloc.c line 217" - -# Check our custom error handler. -runfailure "M" "128000" "120000" "malloc 128000 xmalloc.c 61" -runfailure "R" "128000" "120000" "realloc 128000 xmalloc.c 90" -runfailure "S" "64000" "120000" "strdup 64000 xmalloc.c 121" -runfailure "N" "64000" "120000" "strndup 64000 xmalloc.c 148" -runfailure "C" "128000" "120000" "calloc 128000 xmalloc.c 172" -runfailure "A" "64000" "120000" "asprintf 64000 xmalloc.c 241" -runfailure "V" "64000" "120000" "vasprintf 64000 xmalloc.c 217" - -# Check the smaller ones again just for grins. -runsuccess "m" "21" "96000" -runsuccess "r" "32" "96000" -runsuccess "s" "64" "96000" -runsuccess "n" "20" "96000" -runsuccess "c" "24" "96000" -runsuccess "a" "30" "96000" -runsuccess "v" "35" "96000" diff --git a/tests/util/xmalloc.c b/tests/util/xmalloc.c index bd0ab62..3bd5588 100644 --- a/tests/util/xmalloc.c +++ b/tests/util/xmalloc.c @@ -1,27 +1,17 @@ /* * Test suite for xmalloc and family. * + * Copyright 2008 Board of Trustees, Leland Stanford Jr. University * Copyright 2004, 2005, 2006 * by Internet Systems Consortium, Inc. ("ISC") * Copyright 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, * 2003 by The Internet Software Consortium and Rich Salz * - * This code is derived from software contributed to the Internet Software - * Consortium by Rich Salz. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE - * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. + * See LICENSE for licensing terms. */ +#line 1 "xmalloc.c" + #include #include @@ -32,7 +22,8 @@ /* Linux requires sys/time.h be included before sys/resource.h. */ #include -#include +#include +#include /* @@ -81,20 +72,19 @@ test_realloc(size_t size) char *buffer; size_t i; - buffer = xmalloc(size / 2); + buffer = xmalloc(10); if (buffer == NULL) return 0; - if (size / 2 > 0) - memset(buffer, 1, size / 2); + memset(buffer, 1, 10); buffer = xrealloc(buffer, size); if (buffer == NULL) return 0; if (size > 0) - memset(buffer + size / 2, 2, size - size / 2); - for (i = 0; i < size / 2; i++) + memset(buffer + 10, 2, size - 10); + for (i = 0; i < 10; i++) if (buffer[i] != 1) return 0; - for (i = size / 2; i < size; i++) + for (i = 10; i < size; i++) if (buffer[i] != 2) return 0; free(buffer); @@ -257,6 +247,7 @@ main(int argc, char *argv[]) int willfail = 0; unsigned char code; struct rlimit rl; + void *tmp; if (argc < 3) die("Usage error. Type, size, and limit must be given."); @@ -269,6 +260,27 @@ main(int argc, char *argv[]) if (limit == 0 && errno != 0) sysdie("Invalid limit"); + /* If the code is capitalized, install our customized error handler. */ + code = argv[1][0]; + if (isupper(code)) { + xmalloc_error_handler = test_handler; + code = tolower(code); + } + + /* + * Decide if the allocation should fail. If it should, set willfail to 2, + * so that if it unexpectedly succeeds, we exit with a status indicating + * that the test should be skipped. + */ + max = size; + if (code == 's' || code == 'n' || code == 'a' || code == 'v') { + max += size; + if (limit > 0) + limit += size; + } + if (limit > 0 && max > limit) + willfail = 2; + /* * If a memory limit was given and we can set memory limits, set it. * Otherwise, exit 2, signalling to the driver that the test should be @@ -277,37 +289,28 @@ main(int argc, char *argv[]) * the shell to die). */ if (limit > 0) { -#if HAVE_SETRLIMIT && defined(RLIMIT_DATA) +#if HAVE_SETRLIMIT && defined(RLIMIT_AS) rl.rlim_cur = limit; rl.rlim_max = limit; - if (setrlimit(RLIMIT_DATA, &rl) < 0) { + if (setrlimit(RLIMIT_AS, &rl) < 0) { syswarn("Can't set data limit to %lu", (unsigned long) limit); exit(2); } + if (size < limit || code == 'r') { + tmp = malloc(code == 'r' ? 10 : size); + if (tmp == NULL) { + syswarn("Can't allocate initial memory of %lu", + (unsigned long) size); + exit(2); + } + free(tmp); + } #else warn("Data limits aren't supported."); exit(2); #endif } - /* If the code is capitalized, install our customized error handler. */ - code = argv[1][0]; - if (isupper(code)) { - xmalloc_error_handler = test_handler; - code = tolower(code); - } - - /* - * Decide if the allocation should fail. If it should, set willfail to 2, - * so that if it unexpectedly succeeds, we exit with a status indicating - * that the test should be skipped. - */ - max = size; - if (code == 's' || code == 'n' || code == 'a' || code == 'v') - max *= 2; - if (limit > 0 && max > limit) - willfail = 2; - switch (code) { case 'c': exit(test_calloc(size) ? willfail : 1); case 'm': exit(test_malloc(size) ? willfail : 1); -- 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/portable/strlcpy-t.c') 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