aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorRuss Allbery <rra@stanford.edu>2010-02-09 18:40:22 -0800
committerRuss Allbery <rra@stanford.edu>2010-02-09 18:40:22 -0800
commitc02942ddc12408f0e5b9d828cddf240519d1fe93 (patch)
tree62f80e0ba359c1a13cee7daee228e3e00011a723 /tests
parentd05f66dbff10b525d37f60ee01d5b9f94bf5192e (diff)
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.
Diffstat (limited to 'tests')
-rw-r--r--tests/TESTS3
-rw-r--r--tests/client/basic-t.in220
-rw-r--r--tests/libtest.c203
-rw-r--r--tests/libtest.h69
-rw-r--r--tests/libtest.sh80
-rw-r--r--tests/portable/asprintf-t.c34
-rw-r--r--tests/portable/mkstemp-t.c73
-rw-r--r--tests/portable/mkstemp.c2
-rw-r--r--tests/portable/setenv-t.c46
-rw-r--r--tests/portable/setenv.c2
-rw-r--r--tests/portable/snprintf-t.c123
-rw-r--r--tests/portable/strlcat-t.c84
-rw-r--r--tests/portable/strlcpy-t.c73
-rw-r--r--tests/runtests.c327
-rw-r--r--tests/tap/basic.c356
-rw-r--r--tests/tap/basic.h98
-rw-r--r--tests/tap/kerberos.c164
-rw-r--r--tests/tap/kerberos.h32
-rw-r--r--tests/tap/kerberos.sh48
-rw-r--r--tests/tap/libtap.sh148
-rw-r--r--tests/tap/messages.c80
-rw-r--r--tests/tap/messages.h35
-rw-r--r--tests/tap/process.c100
-rw-r--r--tests/tap/process.h37
-rw-r--r--tests/tap/remctl.sh46
-rw-r--r--tests/util/concat-t.c60
-rw-r--r--tests/util/messages-krb5-t.c99
-rw-r--r--tests/util/messages-t.c201
-rwxr-xr-xtests/util/xmalloc-t127
-rw-r--r--tests/util/xmalloc-t.in126
-rw-r--r--tests/util/xmalloc.c85
31 files changed, 2089 insertions, 1092 deletions
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 <<EOF
wallet_principal = $principal
}
EOF
-runsuccess "" "$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
+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 <config.h>
-#include <portable/system.h>
-
-#include <sys/time.h>
-#include <sys/wait.h>
-
-#include <tests/libtest.h>
-#include <util/util.h>
-
-/* 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 <config.h>
-#include <portable/macros.h>
-
-/*
- * 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 <rra@stanford.edu>
-# 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 <rra@stanford.edu>
- * 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 <config.h>
#include <portable/system.h>
-#include <tests/libtest.h>
+#include <tests/tap/basic.h>
-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 <rra@stanford.edu>
+ * Copyright 2002, 2004, 2008, 2009
+ * Board of Trustees, Leland Stanford Jr. University
+ *
+ * See LICENSE for licensing terms.
+ */
+
+#include <config.h>
+#include <portable/system.h>
+
+#include <errno.h>
+#include <sys/stat.h>
+
+#include <tests/tap/basic.h>
+
+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 <portable/mkstemp.c>
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 <rra@stanford.edu>
+ * 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 <config.h>
+#include <portable/system.h>
+
+#include <errno.h>
+
+#include <tests/tap/basic.h>
+
+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 <portable/setenv.c>
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 <rra@stanford.edu>
+ * 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 <config.h>
#include <portable/system.h>
-#include <tests/libtest.h>
+#include <tests/tap/basic.h>
+/*
+ * 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 <rra@stanford.edu>
+ * 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 <config.h>
#include <portable/system.h>
-#include <tests/libtest.h>
+#include <tests/tap/basic.h>
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 <rra@stanford.edu>
+ * 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 <config.h>
#include <portable/system.h>
-#include <tests/libtest.h>
+#include <tests/tap/basic.h>
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 <number> is the number of the test. ok indicates success, not ok
* indicates failure, and "# skip" indicates the test was skipped for some
- * reason (maybe because it doesn't apply to this platform).
+ * 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 <rra@stanford.edu>
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -44,16 +45,19 @@
* DEALINGS IN THE SOFTWARE.
*/
-#include <config.h>
-#include <portable/system.h>
-
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
+#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
+#include <unistd.h>
/* sys/time.h must be included before sys/resource.h on some platforms. */
#include <sys/resource.h>
@@ -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.
@@ -219,6 +222,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
* before being passed in. Return true if initialization succeeds, false
@@ -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. */
@@ -666,12 +729,53 @@ 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 <test-list>\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 <rra@stanford.edu>
+ * 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 <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <tap/basic.h>
+
+/*
+ * 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 <sys/types.h> /* 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 <config.h>
+#include <portable/krb5.h>
+#include <portable/system.h>
+
+#include <tests/tap/basic.h>
+#include <tests/tap/kerberos.h>
+#include <util/concat.h>
+#include <util/xmalloc.h>
+
+
+/*
+ * 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 <config.h>
+#include <portable/macros.h>
+
+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 <rra@stanford.edu>
+# 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
+ status=$?
+ if [ $status != 0 ] ; then
+ kinit -t "$keytab" "$principal" >/dev/null </dev/null
+ status=$?
+ fi
+ if [ $status != 0 ] ; then
+ kinit -k -K "$keytab" "$principal" >/dev/null </dev/null
+ status=$?
+ fi
+ if [ $status != 0 ] ; then
+ bail "Can't get Kerberos tickets"
+ fi
+ return 0
+}
+
+# Clean up at the end of a test. Currently only removes the ticket cache.
+kerberos_cleanup () {
+ rm -f "$BUILD/data/test.cache"
+}
diff --git a/tests/tap/libtap.sh b/tests/tap/libtap.sh
new file mode 100644
index 0000000..1846840
--- /dev/null
+++ b/tests/tap/libtap.sh
@@ -0,0 +1,148 @@
+# Shell function library for test cases.
+#
+# Written by Russ Allbery <rra@stanford.edu>
+# Copyright 2009 Russ Allbery <rra@stanford.edu>
+# 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 <config.h>
+#include <portable/system.h>
+
+#include <tests/tap/messages.h>
+#include <util/concat.h>
+#include <util/macros.h>
+#include <util/messages.h>
+#include <util/xmalloc.h>
+
+/* 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 <config.h>
+#include <portable/macros.h>
+
+/* 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 <config.h>
+#include <portable/system.h>
+
+#include <sys/wait.h>
+
+#include <tests/tap/basic.h>
+#include <tests/tap/process.h>
+#include <util/xmalloc.h>
+
+
+/*
+ * 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 <config.h>
+#include <portable/macros.h>
+
+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 <rra@stanford.edu>
+# 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 <rra@stanford.edu>
+ * 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 <config.h>
#include <portable/system.h>
-#include <tests/libtest.h>
-#include <util/util.h>
+#include <tests/tap/basic.h>
+#include <util/concat.h>
#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 <rra@stanford.edu>
+ * Copyright 2010 Board of Trustees, Leland Stanford Jr. University
+ *
+ * See LICENSE for licensing terms.
+ */
+
+#include <config.h>
+#include <portable/krb5.h>
+#include <portable/system.h>
+
+#include <tests/tap/basic.h>
+#include <tests/tap/process.h>
+#include <util/messages-krb5.h>
+#include <util/messages.h>
+#include <util/xmalloc.h>
+
+
+/*
+ * 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 <rra@stanford.edu>
+ * 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 <config.h>
@@ -30,67 +19,11 @@
#include <sys/stat.h>
#include <sys/wait.h>
-#include <tests/libtest.h>
-#include <util/util.h>
-
-#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 <tests/tap/basic.h>
+#include <tests/tap/process.h>
+#include <util/concat.h>
+#include <util/messages.h>
+#include <util/xmalloc.h>
/*
@@ -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 <rra@stanford.edu>
+# 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 <config.h>
#include <portable/system.h>
@@ -32,7 +22,8 @@
/* Linux requires sys/time.h be included before sys/resource.h. */
#include <sys/resource.h>
-#include <util/util.h>
+#include <util/messages.h>
+#include <util/xmalloc.h>
/*
@@ -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);