summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/TESTS1
-rw-r--r--tests/client/basic-t.in6
-rw-r--r--tests/client/rekey-t.in100
-rw-r--r--tests/data/fake-keytabbin334 -> 334 bytes
-rw-r--r--tests/data/fake-keytab-foreignbin0 -> 453 bytes
-rw-r--r--tests/data/fake-keytab-mergebin666 -> 698 bytes
-rw-r--r--tests/data/fake-keytab-oldbin334 -> 785 bytes
-rw-r--r--tests/data/fake-keytab-partialbin0 -> 1149 bytes
-rw-r--r--tests/data/fake-keytab-partial-resultbin0 -> 1513 bytes
-rw-r--r--tests/data/fake-keytab-rekeybin0 -> 1149 bytes
-rw-r--r--tests/data/fake-keytab-unknownbin0 -> 334 bytes
-rw-r--r--tests/data/fake-srvtabbin50 -> 50 bytes
-rwxr-xr-xtests/docs/pod-spelling-t4
-rwxr-xr-xtests/docs/pod-t5
-rw-r--r--tests/portable/snprintf-t.c12
-rw-r--r--tests/runtests.c403
-rwxr-xr-xtests/server/backend-t18
-rwxr-xr-xtests/server/report-t10
-rw-r--r--tests/tap/basic.c196
-rw-r--r--tests/tap/basic.h41
-rw-r--r--tests/tap/kerberos.c31
-rw-r--r--tests/tap/kerberos.sh32
-rw-r--r--tests/tap/libtap.sh32
-rw-r--r--tests/tap/remctl.sh18
-rw-r--r--tests/util/messages-t.c4
-rw-r--r--tests/util/xmalloc.c5
26 files changed, 663 insertions, 255 deletions
diff --git a/tests/TESTS b/tests/TESTS
index 161941c..54b8190 100644
--- a/tests/TESTS
+++ b/tests/TESTS
@@ -1,6 +1,7 @@
client/basic
client/full
client/prompt
+client/rekey
docs/pod
docs/pod-spelling
portable/asprintf
diff --git a/tests/client/basic-t.in b/tests/client/basic-t.in
index 86e24d5..11f0bce 100644
--- a/tests/client/basic-t.in
+++ b/tests/client/basic-t.in
@@ -114,10 +114,10 @@ rm -f srvtab srvtab.bak
# Test keytab merging.
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
+ktutil_list keytab klist-seen
+ktutil_list data/fake-keytab-merge klist-good
ok '...and the merged keytab is correct' cmp klist-seen klist-good
-rm -f keytab klist-seen klist-good
+rm -f keytab klist-good klist-seen
# Test srvtab download into a merged keytab with an older version.
cp data/fake-keytab-old keytab
diff --git a/tests/client/rekey-t.in b/tests/client/rekey-t.in
new file mode 100644
index 0000000..390a362
--- /dev/null
+++ b/tests/client/rekey-t.in
@@ -0,0 +1,100 @@
+#! /bin/sh
+#
+# Test suite for the wallet-rekey command-line client.
+#
+# Written by Russ Allbery <rra@stanford.edu>
+# Copyright 2006, 2007, 2008, 2010
+# Board of Trustees, Leland Stanford Jr. University
+#
+# See LICENSE for licensing terms.
+
+# Load the test library.
+. "$SOURCE/tap/libtap.sh"
+. "$SOURCE/tap/kerberos.sh"
+. "$SOURCE/tap/remctl.sh"
+cd "$SOURCE"
+
+# 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
+# Stanford-specific; it just matches the files that are distributed with the
+# package.
+krb5conf=
+for p in /etc/krb5.conf /usr/local/etc/krb5.conf data/krb5.conf ; do
+ if [ -r "$p" ] ; then
+ krb5conf="$p"
+ sed -e '/^[ ]*test-k5.stanford.edu =/,/}/d' \
+ -e 's/\(default_realm.*=\) .*/\1 test-k5.stanford.edu/' \
+ -e 's/^[ ]*wallet_.*//' \
+ -e '/^[ ]*wallet[ ]*=[ ]*{/,/}/d' \
+ "$p" > ./krb5.conf
+ KRB5_CONFIG="./krb5.conf"
+ export KRB5_CONFIG
+ break
+ fi
+done
+if [ -z "$krb5conf" ] ; then
+ skip_all 'no krb5.conf found, put one in tests/data/krb5.conf'
+fi
+
+# Test setup.
+kerberos_setup
+if [ $? != 0 ] ; then
+ rm krb5.conf
+ skip_all 'Kerberos tests not configured'
+elif [ -z '@REMCTLD@' ] ; then
+ rm krb5.conf
+ skip_all 'No remctld found'
+else
+ plan 9
+fi
+remctld_start '@REMCTLD@' "$SOURCE/data/basic.conf"
+wallet="$BUILD/../client/wallet-rekey"
+
+# Rekeying should result in a merged keytab with both the old and new keys.
+cp data/fake-keytab-old keytab
+ok_program 'basic wallet-rekey' 0 '' \
+ "$wallet" -k "$principal" -p 14373 -s localhost -c fake-wallet keytab
+ktutil_list keytab klist-seen
+ktutil_list data/fake-keytab-rekey klist-good
+ok '...and the rekeyed keytab is correct' cmp klist-seen klist-good
+rm -f keytab klist-good klist-seen
+
+# Rekeying a keytab that contains no principals in the local domain should
+# produce an error message and do nothing.
+cp data/fake-keytab-foreign keytab
+ok_program 'foreign wallet-rekey' 1 'wallet: no rekeyable principals found' \
+ "$wallet" -k "$principal" -p 14373 -s localhost -c fake-wallet keytab
+ok '...and the keytab was untouched' cmp keytab data/fake-keytab-foreign
+rm -f keytab
+
+# Rekeying a keytab where we can't retrieve the principal should produce an
+# error message and abort when it's the first principal.
+cp data/fake-keytab-unknown keytab
+ok_program 'unknown wallet-rekey' 1 \
+'wallet: Unknown keytab service/real-keytab
+wallet: error rekeying for principal service/real-keytab
+wallet: aborting, keytab unchanged' \
+ "$wallet" -k "$principal" -p 14373 -s localhost -c fake-wallet keytab
+ok '...and the keytab was untouched' cmp keytab data/fake-keytab-unknown
+rm -f keytab
+
+# Rekeying a keytab where we can't retrieve a later principal should leave the
+# original keytab as keytab.old and store, in the new keytab, only the things
+# that it was able to rekey.
+cp data/fake-keytab-partial keytab
+ok_program 'partial wallet-rekey' 1 \
+'wallet: Unknown keytab service/real-keytab
+wallet: error rekeying for principal service/real-keytab
+wallet: partial failure to rekey keytab keytab, old keytab left in keytab.old'\
+ "$wallet" -k "$principal" -p 14373 -s localhost -c fake-wallet keytab
+ktutil_list keytab klist-seen
+ktutil_list data/fake-keytab-partial-result klist-good
+ok '...and the rekeyed keytab is correct' cmp klist-seen klist-good
+ok '...and the backup keytab is correct' \
+ cmp keytab.old data/fake-keytab-partial
+rm -f keytab keytab.old klist-seen klist-good
+
+# Clean up.
+rm -f autocreated krb5.conf
+remctld_stop
+kerberos_cleanup
diff --git a/tests/data/fake-keytab b/tests/data/fake-keytab
index 714d9b6..6a13fd6 100644
--- a/tests/data/fake-keytab
+++ b/tests/data/fake-keytab
Binary files differ
diff --git a/tests/data/fake-keytab-foreign b/tests/data/fake-keytab-foreign
new file mode 100644
index 0000000..efbc5ed
--- /dev/null
+++ b/tests/data/fake-keytab-foreign
Binary files differ
diff --git a/tests/data/fake-keytab-merge b/tests/data/fake-keytab-merge
index 31ddc49..4858eb4 100644
--- a/tests/data/fake-keytab-merge
+++ b/tests/data/fake-keytab-merge
Binary files differ
diff --git a/tests/data/fake-keytab-old b/tests/data/fake-keytab-old
index 6a13fd6..f7ee9c0 100644
--- a/tests/data/fake-keytab-old
+++ b/tests/data/fake-keytab-old
Binary files differ
diff --git a/tests/data/fake-keytab-partial b/tests/data/fake-keytab-partial
new file mode 100644
index 0000000..86587aa
--- /dev/null
+++ b/tests/data/fake-keytab-partial
Binary files differ
diff --git a/tests/data/fake-keytab-partial-result b/tests/data/fake-keytab-partial-result
new file mode 100644
index 0000000..a265ccc
--- /dev/null
+++ b/tests/data/fake-keytab-partial-result
Binary files differ
diff --git a/tests/data/fake-keytab-rekey b/tests/data/fake-keytab-rekey
new file mode 100644
index 0000000..4e7a507
--- /dev/null
+++ b/tests/data/fake-keytab-rekey
Binary files differ
diff --git a/tests/data/fake-keytab-unknown b/tests/data/fake-keytab-unknown
new file mode 100644
index 0000000..0827e74
--- /dev/null
+++ b/tests/data/fake-keytab-unknown
Binary files differ
diff --git a/tests/data/fake-srvtab b/tests/data/fake-srvtab
index f454af2..0b4af6b 100644
--- a/tests/data/fake-srvtab
+++ b/tests/data/fake-srvtab
Binary files differ
diff --git a/tests/docs/pod-spelling-t b/tests/docs/pod-spelling-t
index 6993e4c..eaa7dd6 100755
--- a/tests/docs/pod-spelling-t
+++ b/tests/docs/pod-spelling-t
@@ -47,8 +47,8 @@ my @pod = map {
my $pod = "$ENV{SOURCE}/../" . $_;
$pod =~ s,[^/.][^/]*/../,,g;
$pod;
-} qw(client/wallet.pod server/keytab-backend server/wallet-admin
- server/wallet-backend server/wallet-report);
+} qw(client/wallet.pod client/wallet-rekey.pod server/keytab-backend
+ server/wallet-admin server/wallet-backend server/wallet-report);
plan tests => scalar @pod;
# Finally, do the checks.
diff --git a/tests/docs/pod-t b/tests/docs/pod-t
index f92ba2c..e25ade2 100755
--- a/tests/docs/pod-t
+++ b/tests/docs/pod-t
@@ -12,8 +12,9 @@ use Test::More;
eval 'use Test::Pod 1.00';
plan skip_all => 'Test::Pod 1.00 required for testing POD' if $@;
-my @files = qw(client/wallet.pod server/keytab-backend server/wallet-admin
- server/wallet-backend server/wallet-report);
+my @files = qw(client/wallet.pod client/wallet-rekey.pod server/keytab-backend
+ server/wallet-admin server/wallet-backend
+ server/wallet-report);
my $total = scalar (@files);
plan tests => $total;
for my $file (@files) {
diff --git a/tests/portable/snprintf-t.c b/tests/portable/snprintf-t.c
index ca6ae61..fd4c228 100644
--- a/tests/portable/snprintf-t.c
+++ b/tests/portable/snprintf-t.c
@@ -2,7 +2,7 @@
* snprintf test suite.
*
* Written by Russ Allbery <rra@stanford.edu>
- * Copyright 2009 Board of Trustees, Leland Stanford Jr. University
+ * Copyright 2009, 2010 Board of Trustees, Leland Stanford Jr. University
* Copyright (c) 2004, 2005, 2006
* by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
@@ -17,6 +17,12 @@
#include <tests/tap/basic.h>
/*
+ * Disable the requirement that format strings be literals. We need variable
+ * formats for easy testing.
+ */
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+
+/*
* Intentionally don't add the printf attribute here since we pass a
* zero-length printf format during testing and don't want warnings.
*/
@@ -86,7 +92,7 @@ static unsigned long long ullong_nums[] = {
static void
-test_format(bool truncate, const char *expected, int count,
+test_format(bool trunc, const char *expected, int count,
const char *format, ...)
{
char buf[128];
@@ -94,7 +100,7 @@ test_format(bool truncate, const char *expected, int count,
va_list args;
va_start(args, format);
- result = test_vsnprintf(buf, truncate ? 32 : sizeof(buf), format, args);
+ result = test_vsnprintf(buf, trunc ? 32 : sizeof(buf), format, args);
va_end(args);
is_string(expected, buf, "format %s, wanted %s", format, expected);
is_int(count, result, "...and output length correct");
diff --git a/tests/runtests.c b/tests/runtests.c
index 1670012..ab77629 100644
--- a/tests/runtests.c
+++ b/tests/runtests.c
@@ -8,22 +8,41 @@
* Expects a list of executables located in the given file, one line per
* executable. For each one, runs it as part of a test suite, reporting
* results. Test output should start with a line containing the number of
- * tests (numbered from 1 to this number), and then each line should be in the
- * following format:
+ * tests (numbered from 1 to this number), optionally preceded by "1..",
+ * although that line may be given anywhere in the output. Each additional
+ * line should be in the following format:
*
* ok <number>
* not ok <number>
* ok <number> # skip
+ * not ok <number> # todo
*
- * where <number> is the number of the test. ok indicates success, not ok
- * indicates failure, and "# skip" indicates the test was skipped for some
- * reason (maybe because it doesn't apply to this platform). This is a subset
- * of TAP as documented in Test::Harness::TAP, which comes with Perl.
+ * where <number> is the number of the test. An optional comment is permitted
+ * after the number if preceded by whitespace. ok indicates success, not ok
+ * indicates failure. "# skip" and "# todo" are a special cases of a comment,
+ * and must start with exactly that formatting. They indicate the test was
+ * skipped for some reason (maybe because it doesn't apply to this platform)
+ * or is testing something known to currently fail. The text following either
+ * "# skip" or "# todo" and whitespace is the reason.
+ *
+ * As a special case, the first line of the output may be in the form:
+ *
+ * 1..0 # skip some reason
+ *
+ * which indicates that this entire test case should be skipped and gives a
+ * reason.
+ *
+ * Any other lines are ignored, although for compliance with the TAP protocol
+ * all lines other than the ones in the above format should be sent to
+ * standard error rather than standard output and start with #.
+ *
+ * This is a subset of TAP as documented in Test::Harness::TAP or
+ * TAP::Parser::Grammar, which comes with Perl.
*
* Any bug reports, bug fixes, and improvements are very much welcome and
* should be sent to the e-mail address below.
*
- * Copyright 2000, 2001, 2004, 2006, 2007, 2008, 2009
+ * Copyright 2000, 2001, 2004, 2006, 2007, 2008, 2009, 2010
* Russ Allbery <rra@stanford.edu>
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -88,6 +107,14 @@ enum test_status {
TEST_INVALID
};
+/* Indicates the state of our plan. */
+enum plan_status {
+ PLAN_INIT, /* Nothing seen yet. */
+ PLAN_FIRST, /* Plan seen before any tests. */
+ PLAN_PENDING, /* Test seen and no plan yet. */
+ PLAN_FINAL /* Plan seen after some tests. */
+};
+
/* Error exit statuses for test processes. */
#define CHILDERR_DUP 100 /* Couldn't redirect stderr or stdout. */
#define CHILDERR_EXEC 101 /* Couldn't exec child process. */
@@ -97,12 +124,14 @@ enum test_status {
struct testset {
char *file; /* The file name of the test. */
char *path; /* The path to the test program. */
- int count; /* Expected count of tests. */
- int current; /* The last seen test number. */
- int length; /* The length of the last status message. */
- int passed; /* Count of passing tests. */
- int failed; /* Count of failing lists. */
- int skipped; /* Count of skipped tests (passed). */
+ enum plan_status plan; /* The status of our plan. */
+ unsigned long count; /* Expected count of tests. */
+ unsigned long current; /* The last seen test number. */
+ unsigned int length; /* The length of the last status message. */
+ unsigned long passed; /* Count of passing tests. */
+ unsigned long failed; /* Count of failing lists. */
+ unsigned long skipped; /* Count of skipped tests (passed). */
+ unsigned long allocated; /* The size of the results table. */
enum test_status *results; /* Table of results by test number. */
int aborted; /* Whether the set as aborted. */
int reported; /* Whether the results were reported. */
@@ -131,8 +160,9 @@ Failed Set Fail/Total (%) Skip Stat Failing Tests\n\
-------------------------- -------------- ---- ---- ------------------------";
/* Include the file name and line number in malloc failures. */
-#define xmalloc(size) x_malloc((size), __FILE__, __LINE__)
-#define xstrdup(p) x_strdup((p), __FILE__, __LINE__)
+#define xmalloc(size) x_malloc((size), __FILE__, __LINE__)
+#define xrealloc(p, size) x_realloc((p), (size), __FILE__, __LINE__)
+#define xstrdup(p) x_strdup((p), __FILE__, __LINE__)
/*
@@ -164,7 +194,7 @@ x_malloc(size_t size, const char *file, int line)
void *p;
p = malloc(size);
- if (!p)
+ if (p == NULL)
sysdie("failed to malloc %lu bytes at %s line %d",
(unsigned long) size, file, line);
return p;
@@ -172,6 +202,20 @@ x_malloc(size_t size, const char *file, int line)
/*
+ * Reallocate memory, reporting a fatal error and exiting on failure.
+ */
+static void *
+x_realloc(void *p, size_t size, const char *file, int line)
+{
+ p = realloc(p, size);
+ if (p == NULL)
+ sysdie("failed to realloc %lu bytes at %s line %d",
+ (unsigned long) size, file, line);
+ return p;
+}
+
+
+/*
* Copy a string, reporting a fatal error and exiting on failure.
*/
static char *
@@ -182,7 +226,7 @@ x_strdup(const char *s, const char *file, int line)
len = strlen(s) + 1;
p = malloc(len);
- if (!p)
+ if (p == NULL)
sysdie("failed to strdup %lu bytes at %s line %d",
(unsigned long) len, file, line);
memcpy(p, s, len);
@@ -235,62 +279,6 @@ skip_whitespace(const char *p)
/*
- * Read the first line of test output, which should contain the range of
- * test numbers, and initialize the testset structure. Assume it was zeroed
- * before being passed in. Return true if initialization succeeds, false
- * otherwise.
- */
-static int
-test_init(const char *line, struct testset *ts)
-{
- int i;
-
- /*
- * Prefer a simple number of tests, but if the count is given as a range
- * such as 1..10, accept that too for compatibility with Perl's
- * Test::Harness.
- */
- line = skip_whitespace(line);
- if (strncmp(line, "1..", 3) == 0)
- line += 3;
-
- /*
- * Get the count, check it for validity, and initialize the struct. If we
- * have something of the form "1..0 # skip foo", the whole file was
- * skipped; record that.
- */
- i = strtol(line, (char **) &line, 10);
- if (i == 0) {
- line = skip_whitespace(line);
- if (*line == '#') {
- line = skip_whitespace(line + 1);
- if (strncasecmp(line, "skip", 4) == 0) {
- line = skip_whitespace(line + 4);
- if (*line != '\0') {
- ts->reason = xstrdup(line);
- ts->reason[strlen(ts->reason) - 1] = '\0';
- }
- ts->all_skipped = 1;
- ts->aborted = 1;
- return 0;
- }
- }
- }
- if (i <= 0) {
- puts("ABORTED (invalid test count)");
- ts->aborted = 1;
- ts->reported = 1;
- return 0;
- }
- ts->count = i;
- ts->results = xmalloc(ts->count * sizeof(enum test_status));
- for (i = 0; i < ts->count; i++)
- ts->results[i] = TEST_INVALID;
- return 1;
-}
-
-
-/*
* Start a program, connecting its stdout to a pipe on our end and its stderr
* to /dev/null, and storing the file descriptor to read from in the two
* argument. Returns the PID of the new process. Errors are fatal.
@@ -340,7 +328,7 @@ test_start(const char *path, int *fd)
static void
test_backspace(struct testset *ts)
{
- int i;
+ unsigned int i;
if (!isatty(STDOUT_FILENO))
return;
@@ -355,6 +343,87 @@ test_backspace(struct testset *ts)
/*
+ * Read the plan line of test output, which should contain the range of test
+ * numbers. We may initialize the testset structure here if we haven't yet
+ * seen a test. Return true if initialization succeeded and the test should
+ * continue, false otherwise.
+ */
+static int
+test_plan(const char *line, struct testset *ts)
+{
+ unsigned long i;
+ long n;
+
+ /*
+ * Accept a plan without the leading 1.. for compatibility with older
+ * versions of runtests. This will only be allowed if we've not yet seen
+ * a test result.
+ */
+ line = skip_whitespace(line);
+ if (strncmp(line, "1..", 3) == 0)
+ line += 3;
+
+ /*
+ * Get the count, check it for validity, and initialize the struct. If we
+ * have something of the form "1..0 # skip foo", the whole file was
+ * skipped; record that. If we do skip the whole file, zero out all of
+ * our statistics, since they're no longer relevant.
+ */
+ n = strtol(line, (char **) &line, 10);
+ if (n == 0) {
+ line = skip_whitespace(line);
+ if (*line == '#') {
+ line = skip_whitespace(line + 1);
+ if (strncasecmp(line, "skip", 4) == 0) {
+ line = skip_whitespace(line + 4);
+ if (*line != '\0') {
+ ts->reason = xstrdup(line);
+ ts->reason[strlen(ts->reason) - 1] = '\0';
+ }
+ ts->all_skipped = 1;
+ ts->aborted = 1;
+ ts->count = 0;
+ ts->passed = 0;
+ ts->skipped = 0;
+ ts->failed = 0;
+ return 0;
+ }
+ }
+ }
+ if (n <= 0) {
+ puts("ABORTED (invalid test count)");
+ ts->aborted = 1;
+ ts->reported = 1;
+ return 0;
+ }
+ if (ts->plan == PLAN_INIT && ts->allocated == 0) {
+ ts->count = n;
+ ts->allocated = n;
+ ts->plan = PLAN_FIRST;
+ ts->results = xmalloc(ts->count * sizeof(enum test_status));
+ for (i = 0; i < ts->count; i++)
+ ts->results[i] = TEST_INVALID;
+ } else if (ts->plan == PLAN_PENDING) {
+ if ((unsigned long) n < ts->count) {
+ printf("ABORTED (invalid test number %lu)\n", ts->count);
+ ts->aborted = 1;
+ ts->reported = 1;
+ return 0;
+ }
+ ts->count = n;
+ if ((unsigned long) n > ts->allocated) {
+ ts->results = xrealloc(ts->results, n * sizeof(enum test_status));
+ for (i = ts->allocated; i < ts->count; i++)
+ ts->results[i] = TEST_INVALID;
+ ts->allocated = n;
+ }
+ ts->plan = PLAN_FINAL;
+ }
+ return 1;
+}
+
+
+/*
* Given a single line of output from a test, parse it and return the success
* status of that test. Anything printed to stdout not matching the form
* /^(not )?ok \d+/ is ignored. Sets ts->current to the test number that just
@@ -366,20 +435,21 @@ test_checkline(const char *line, struct testset *ts)
enum test_status status = TEST_PASS;
const char *bail;
char *end;
- int current;
+ long number;
+ unsigned long i, current;
/* Before anything, check for a test abort. */
bail = strstr(line, "Bail out!");
if (bail != NULL) {
bail = skip_whitespace(bail + strlen("Bail out!"));
if (*bail != '\0') {
- int length;
+ size_t length;
length = strlen(bail);
if (bail[length - 1] == '\n')
length--;
test_backspace(ts);
- printf("ABORTED (%.*s)\n", length, bail);
+ printf("ABORTED (%.*s)\n", (int) length, bail);
ts->reported = 1;
}
ts->aborted = 1;
@@ -393,6 +463,26 @@ test_checkline(const char *line, struct testset *ts)
if (line[strlen(line) - 1] != '\n')
return;
+ /* If the line begins with a hash mark, ignore it. */
+ if (line[0] == '#')
+ return;
+
+ /* If we haven't yet seen a plan, look for one. */
+ if (ts->plan == PLAN_INIT && isdigit((unsigned char)(*line))) {
+ if (!test_plan(line, ts))
+ return;
+ } else if (strncmp(line, "1..", 3) == 0) {
+ if (ts->plan == PLAN_PENDING) {
+ if (!test_plan(line, ts))
+ return;
+ } else {
+ puts("ABORTED (multiple plans)");
+ ts->aborted = 1;
+ ts->reported = 1;
+ return;
+ }
+ }
+
/* Parse the line, ignoring something we can't parse. */
if (strncmp(line, "not ", 4) == 0) {
status = TEST_FAIL;
@@ -402,17 +492,36 @@ test_checkline(const char *line, struct testset *ts)
return;
line = skip_whitespace(line + 2);
errno = 0;
- current = strtol(line, &end, 10);
+ number = strtol(line, &end, 10);
if (errno != 0 || end == line)
- current = ts->current + 1;
- if (current <= 0 || current > ts->count) {
+ number = ts->current + 1;
+ current = number;
+ if (number <= 0 || (current > ts->count && ts->plan == PLAN_FIRST)) {
test_backspace(ts);
- printf("ABORTED (invalid test number %d)\n", current);
+ printf("ABORTED (invalid test number %lu)\n", current);
ts->aborted = 1;
ts->reported = 1;
return;
}
+ /* We have a valid test result. Tweak the results array if needed. */
+ if (ts->plan == PLAN_INIT || ts->plan == PLAN_PENDING) {
+ ts->plan = PLAN_PENDING;
+ if (current > ts->count)
+ ts->count = current;
+ if (current > ts->allocated) {
+ unsigned long n;
+
+ n = (ts->allocated == 0) ? 32 : ts->allocated * 2;
+ if (n < current)
+ n = current;
+ ts->results = xrealloc(ts->results, n * sizeof(enum test_status));
+ for (i = ts->allocated; i < n; i++)
+ ts->results[i] = TEST_INVALID;
+ ts->allocated = n;
+ }
+ }
+
/*
* Handle directives. We should probably do something more interesting
* with unexpected passes of todo tests.
@@ -431,7 +540,7 @@ test_checkline(const char *line, struct testset *ts)
/* Make sure that the test number is in range and not a duplicate. */
if (ts->results[current - 1] != TEST_INVALID) {
test_backspace(ts);
- printf("ABORTED (duplicate test number %d)\n", current);
+ printf("ABORTED (duplicate test number %lu)\n", current);
ts->aborted = 1;
ts->reported = 1;
return;
@@ -442,13 +551,13 @@ test_checkline(const char *line, struct testset *ts)
case TEST_PASS: ts->passed++; break;
case TEST_FAIL: ts->failed++; break;
case TEST_SKIP: ts->skipped++; break;
- default: break;
+ case TEST_INVALID: break;
}
ts->current = current;
ts->results[current - 1] = status;
test_backspace(ts);
if (isatty(STDOUT_FILENO)) {
- ts->length = printf("%d/%d", current, ts->count);
+ ts->length = printf("%lu/%lu", current, ts->count);
fflush(stdout);
}
}
@@ -461,12 +570,13 @@ test_checkline(const char *line, struct testset *ts)
* chars plus the space needed would go over the limit (use a limit of 0 to
* disable this.
*/
-static int
-test_print_range(int first, int last, int chars, int limit)
+static unsigned int
+test_print_range(unsigned long first, unsigned long last, unsigned int chars,
+ unsigned int limit)
{
- int needed = 0;
- int out = 0;
- int n;
+ unsigned int needed = 0;
+ unsigned int out = 0;
+ unsigned long n;
if (chars > 0) {
needed += 2;
@@ -484,8 +594,8 @@ test_print_range(int first, int last, int chars, int limit)
out += printf("...");
} else {
if (last > first)
- out += printf("%d-", first);
- out += printf("%d", last);
+ out += printf("%lu-", first);
+ out += printf("%lu", last);
}
return out;
}
@@ -500,16 +610,16 @@ test_print_range(int first, int last, int chars, int limit)
static void
test_summarize(struct testset *ts, int status)
{
- int i;
- int missing = 0;
- int failed = 0;
- int first = 0;
- int last = 0;
+ unsigned long i;
+ unsigned long missing = 0;
+ unsigned long failed = 0;
+ unsigned long first = 0;
+ unsigned long last = 0;
if (ts->aborted) {
fputs("ABORTED", stdout);
if (ts->count > 0)
- printf(" (passed %d/%d)", ts->passed, ts->count - ts->skipped);
+ printf(" (passed %lu/%lu)", ts->passed, ts->count - ts->skipped);
} else {
for (i = 0; i < ts->count; i++) {
if (ts->results[i] == TEST_INVALID) {
@@ -553,9 +663,9 @@ test_summarize(struct testset *ts, int status)
fputs(!status ? "ok" : "dubious", stdout);
if (ts->skipped > 0) {
if (ts->skipped == 1)
- printf(" (skipped %d test)", ts->skipped);
+ printf(" (skipped %lu test)", ts->skipped);
else
- printf(" (skipped %d tests)", ts->skipped);
+ printf(" (skipped %lu tests)", ts->skipped);
}
}
}
@@ -570,8 +680,9 @@ test_summarize(struct testset *ts, int status)
/*
* Given a test set, analyze the results, classify the exit status, handle a
- * few special error messages, and then pass it along to test_summarize()
- * for the regular output.
+ * few special error messages, and then pass it along to test_summarize() for
+ * the regular output. Returns true if the test set ran successfully and all
+ * tests passed or were skipped, false otherwise.
*/
static int
test_analyze(struct testset *ts)
@@ -606,6 +717,10 @@ test_analyze(struct testset *ts)
} else if (WIFSIGNALED(ts->status)) {
test_summarize(ts, -WTERMSIG(ts->status));
return 0;
+ } else if (ts->plan != PLAN_FIRST && ts->plan != PLAN_FINAL) {
+ puts("ABORTED (no valid test plan)");
+ ts->aborted = 1;
+ return 0;
} else {
test_summarize(ts, 0);
return (ts->failed == 0);
@@ -622,14 +737,12 @@ static int
test_run(struct testset *ts)
{
pid_t testpid, child;
- int outfd, i, status;
+ int outfd, status;
+ unsigned long i;
FILE *output;
char buffer[BUFSIZ];
- /*
- * Initialize the test and our data structures, flagging this set in error
- * if the initialization fails.
- */
+ /* Run the test program. */
testpid = test_start(ts->path, &outfd);
output = fdopen(outfd, "r");
if (!output) {
@@ -637,15 +750,11 @@ test_run(struct testset *ts)
fflush(stdout);
sysdie("fdopen failed");
}
- if (!fgets(buffer, sizeof(buffer), output))
- ts->aborted = 1;
- if (!ts->aborted && !test_init(buffer, ts))
- ts->aborted = 1;
/* Pass each line of output to test_checkline(). */
while (!ts->aborted && fgets(buffer, sizeof(buffer), output))
test_checkline(buffer, ts);
- if (ferror(output))
+ if (ferror(output) || ts->plan == PLAN_INIT)
ts->aborted = 1;
test_backspace(ts);
@@ -686,7 +795,8 @@ static void
test_fail_summary(const struct testlist *fails)
{
struct testset *ts;
- int i, chars, total, first, last;
+ unsigned int chars;
+ unsigned long i, first, last, total;
puts(header);
@@ -695,7 +805,7 @@ test_fail_summary(const struct testlist *fails)
for (; fails; fails = fails->next) {
ts = fails->ts;
total = ts->count - ts->skipped;
- printf("%-26.26s %4d/%-4d %3.0f%% %4d ", ts->file, ts->failed,
+ printf("%-26.26s %4lu/%-4lu %3.0f%% %4lu ", ts->file, ts->failed,
total, total ? (ts->failed * 100.0) / total : 0,
ts->skipped);
if (WIFEXITED(ts->status))
@@ -711,19 +821,25 @@ test_fail_summary(const struct testlist *fails)
last = 0;
for (i = 0; i < ts->count; i++) {
if (ts->results[i] == TEST_FAIL) {
- if (first && i == last)
+ if (first != 0 && i == last)
last = i + 1;
else {
- if (first)
+ if (first != 0)
chars += test_print_range(first, last, chars, 20);
first = i + 1;
last = i + 1;
}
}
}
- if (first)
+ if (first != 0)
test_print_range(first, last, chars, 20);
putchar('\n');
+ free(ts->file);
+ free(ts->path);
+ free(ts->results);
+ if (ts->reason != NULL)
+ free(ts->reason);
+ free(ts);
}
}
@@ -746,7 +862,7 @@ find_test(const char *name, struct testset *ts, const char *source,
{
char *path;
const char *bases[] = { ".", build, source, NULL };
- int i;
+ unsigned int i;
for (i = 0; bases[i] != NULL; i++) {
path = xmalloc(strlen(bases[i]) + strlen(name) + 4);
@@ -778,20 +894,21 @@ static int
test_batch(const char *testlist, const char *source, const char *build)
{
FILE *tests;
- size_t length, i;
- size_t longest = 0;
+ unsigned int length, i;
+ unsigned int longest = 0;
char buffer[BUFSIZ];
- int line;
+ unsigned int line;
struct testset ts, *tmp;
struct timeval start, end;
struct rusage stats;
- struct testlist *failhead = 0;
- struct testlist *failtail = 0;
- int total = 0;
- int passed = 0;
- int skipped = 0;
- int failed = 0;
- int aborted = 0;
+ struct testlist *failhead = NULL;
+ struct testlist *failtail = NULL;
+ struct testlist *next;
+ unsigned long total = 0;
+ unsigned long passed = 0;
+ unsigned long skipped = 0;
+ unsigned long failed = 0;
+ unsigned long aborted = 0;
/*
* Open our file of tests to run and scan it, checking for lines that
@@ -805,7 +922,7 @@ test_batch(const char *testlist, const char *source, const char *build)
line++;
length = strlen(buffer) - 1;
if (buffer[length] != '\n') {
- fprintf(stderr, "%s:%d: line too long\n", testlist, line);
+ fprintf(stderr, "%s:%u: line too long\n", testlist, line);
exit(1);
}
if (length > longest)
@@ -834,7 +951,7 @@ test_batch(const char *testlist, const char *source, const char *build)
line++;
length = strlen(buffer) - 1;
if (buffer[length] != '\n') {
- fprintf(stderr, "%s:%d: line too long\n", testlist, line);
+ fprintf(stderr, "%s:%u: line too long\n", testlist, line);
exit(1);
}
buffer[length] = '\0';
@@ -844,12 +961,14 @@ test_batch(const char *testlist, const char *source, const char *build)
if (isatty(STDOUT_FILENO))
fflush(stdout);
memset(&ts, 0, sizeof(ts));
+ ts.plan = PLAN_INIT;
ts.file = xstrdup(buffer);
find_test(buffer, &ts, source, build);
ts.reason = NULL;
if (test_run(&ts)) {
free(ts.file);
free(ts.path);
+ free(ts.results);
if (ts.reason != NULL)
free(ts.reason);
} else {
@@ -858,13 +977,13 @@ test_batch(const char *testlist, const char *source, const char *build)
if (!failhead) {
failhead = xmalloc(sizeof(struct testset));
failhead->ts = tmp;
- failhead->next = 0;
+ failhead->next = NULL;
failtail = failhead;
} else {
failtail->next = xmalloc(sizeof(struct testset));
failtail = failtail->next;
failtail->ts = tmp;
- failtail->next = 0;
+ failtail->next = NULL;
}
}
aborted += ts.aborted;
@@ -880,29 +999,35 @@ test_batch(const char *testlist, const char *source, const char *build)
getrusage(RUSAGE_CHILDREN, &stats);
/* Print out our final results. */
- if (failhead)
+ if (failhead != NULL) {
test_fail_summary(failhead);
+ while (failhead != NULL) {
+ next = failhead->next;
+ free(failhead);
+ failhead = next;
+ }
+ }
putchar('\n');
if (aborted != 0) {
if (aborted == 1)
- printf("Aborted %d test set", aborted);
+ printf("Aborted %lu test set", aborted);
else
- printf("Aborted %d test sets", aborted);
- printf(", passed %d/%d tests", passed, total);
+ printf("Aborted %lu test sets", aborted);
+ printf(", passed %lu/%lu tests", passed, total);
}
else if (failed == 0)
fputs("All tests successful", stdout);
else
- printf("Failed %d/%d tests, %.2f%% okay", failed, total,
+ printf("Failed %lu/%lu tests, %.2f%% okay", failed, total,
(total - failed) * 100.0 / total);
if (skipped != 0) {
if (skipped == 1)
- printf(", %d test skipped", skipped);
+ printf(", %lu test skipped", skipped);
else
- printf(", %d tests skipped", skipped);
+ printf(", %lu tests skipped", skipped);
}
puts(".");
- printf("Files=%d, Tests=%d", line, total);
+ printf("Files=%u, Tests=%lu", line, total);
printf(", %.2f seconds", tv_diff(&end, &start));
printf(" (%.2f usr + %.2f sys = %.2f CPU)\n",
tv_seconds(&stats.ru_utime), tv_seconds(&stats.ru_stime),
diff --git a/tests/server/backend-t b/tests/server/backend-t
index b58d02c..a618391 100755
--- a/tests/server/backend-t
+++ b/tests/server/backend-t
@@ -289,11 +289,19 @@ for my $command (sort keys %acl_commands) {
my @args = @base;
$args[$arg] = 'foo;bar';
($out, $err) = run_backend ('acl', $command, @args);
- is ($err, "invalid characters in argument: foo;bar\n",
- "Invalid arguments for acl $command $arg");
- is ($OUTPUT, "error for admin (1.2.3.4): invalid characters in"
- . " argument: foo;bar\n", ' and syslog correct');
- is ($out, "$new\n", ' and nothing ran');
+ if (($command eq 'add' or $command eq 'remove') and $arg == 2) {
+ is ($err, '', 'Add/remove allows any characters');
+ is ($OUTPUT, "command acl $command @args[0..2] from admin"
+ . " (1.2.3.4) succeeded\n", ' and success logged');
+ is ($out, "$new\nacl_$command @args[0..2]\n",
+ ' and calls the right method');
+ } else {
+ is ($err, "invalid characters in argument: foo;bar\n",
+ "Invalid arguments for acl $command $arg");
+ is ($OUTPUT, "error for admin (1.2.3.4): invalid characters in"
+ . " argument: foo;bar\n", ' and syslog correct');
+ is ($out, "$new\n", ' and nothing ran');
+ }
}
}
for my $command (sort keys %flag_commands) {
diff --git a/tests/server/report-t b/tests/server/report-t
index 394a869..0771946 100755
--- a/tests/server/report-t
+++ b/tests/server/report-t
@@ -8,7 +8,7 @@
# See LICENSE for licensing terms.
use strict;
-use Test::More tests => 44;
+use Test::More tests => 48;
# Create a dummy class for Wallet::Report that prints what method was called
# with its arguments and returns data for testing.
@@ -35,6 +35,7 @@ sub acls {
shift;
print "acls @_\n";
return if ($error or $empty);
+ return ([ qw/d1 d2 d3/ ], [ qw/o1 o2/ ]) if (@_ && $_[0] eq 'duplicate');
return ([ 1, 'ADMIN' ], [ 2, 'group/admins' ], [ 4, 'group/users' ]);
}
@@ -119,6 +120,10 @@ is ($err, '', 'List succeeds for ACLs');
is ($out, "new\nacls \n"
. "ADMIN (ACL ID: 1)\ngroup/admins (ACL ID: 2)\ngroup/users (ACL ID: 4)\n",
' and returns the right output');
+($out, $err) = run_report ('acls', 'duplicate');
+is ($err, '', 'Duplicate report succeeds for ACLs');
+is ($out, "new\nacls duplicate\nd1 d2 d3\no1 o2\n",
+ ' and returns the right output');
($out, $err) = run_report ('acls', 'entry', 'foo', 'foo');
is ($err, '', 'List succeeds for ACLs');
is ($out, "new\nacls entry foo foo\n"
@@ -168,6 +173,9 @@ $Wallet::Report::empty = 1;
($out, $err) = run_report ('acls');
is ($err, '', 'acls runs with an empty list and no errors');
is ($out, "new\nacls \n", ' and calls the right methods');
+($out, $err) = run_report ('acls', 'duplicate');
+is ($err, '', 'acls duplicate runs with an empty list and no errors');
+is ($out, "new\nacls duplicate\n", ' and calls the right methods');
($out, $err) = run_report ('audit', 'objects', 'name');
is ($err, '', 'audit runs with an empty list and no errors');
is ($out, "new\naudit objects name\n", ' and calls the right methods');
diff --git a/tests/tap/basic.c b/tests/tap/basic.c
index 5ca9ff4..829f91a 100644
--- a/tests/tap/basic.c
+++ b/tests/tap/basic.c
@@ -2,12 +2,13 @@
* Some utility routines for writing tests.
*
* Herein are a variety of utility routines for writing tests. All routines
- * of the form ok*() take a test number and some number of appropriate
+ * of the form ok() or is*() take a test number and some number of appropriate
* arguments, check to be sure the results match the expected output using the
* arguments, and print out something appropriate for that test number. Other
- * utility routines help in constructing more complex tests.
+ * utility routines help in constructing more complex tests, skipping tests,
+ * or setting up the TAP output format.
*
- * Copyright 2009 Russ Allbery <rra@stanford.edu>
+ * Copyright 2009, 2010 Russ Allbery <rra@stanford.edu>
* Copyright 2006, 2007, 2008
* Board of Trustees, Leland Stanford Jr. University
* Copyright (c) 2004, 2005, 2006
@@ -34,7 +35,7 @@
* The test count. Always contains the number that will be used for the next
* test status.
*/
-int testnum = 1;
+unsigned long testnum = 1;
/*
* Status information stored so that we can give a test summary at the end of
@@ -44,10 +45,14 @@ int testnum = 1;
* We also store the PID of the process that called plan() and only summarize
* results when that process exits, so as to not misreport results in forked
* processes.
+ *
+ * If _lazy is true, we're doing lazy planning and will print out the plan
+ * based on the last test number at the end of testing.
*/
-static int _planned = 0;
-static int _failed = 0;
+static unsigned long _planned = 0;
+static unsigned long _failed = 0;
static pid_t _process = 0;
+static int _lazy = 0;
/*
@@ -57,22 +62,28 @@ static pid_t _process = 0;
static void
finish(void)
{
- int highest = testnum - 1;
-
- if (_process != 0 && getpid() == _process && _planned > 0) {
+ unsigned long highest = testnum - 1;
+
+ if (_planned == 0 && !_lazy)
+ return;
+ if (_process != 0 && getpid() == _process) {
+ if (_lazy) {
+ printf("1..%lu\n", highest);
+ _planned = highest;
+ }
if (_planned > highest)
- printf("# Looks like you planned %d test%s but only ran %d\n",
+ printf("# Looks like you planned %lu test%s but only ran %lu\n",
_planned, (_planned > 1 ? "s" : ""), highest);
else if (_planned < highest)
- printf("# Looks like you planned %d test%s but ran %d extra\n",
+ printf("# Looks like you planned %lu test%s but ran %lu extra\n",
_planned, (_planned > 1 ? "s" : ""), highest - _planned);
else if (_failed > 0)
- printf("# Looks like you failed %d test%s of %d\n", _failed,
+ printf("# Looks like you failed %lu test%s of %lu\n", _failed,
(_failed > 1 ? "s" : ""), _planned);
else if (_planned > 1)
- printf("# All %d tests successful or skipped\n", _planned);
+ printf("# All %lu tests successful or skipped\n", _planned);
else
- printf("# %d test successful or skipped\n", _planned);
+ printf("# %lu test successful or skipped\n", _planned);
}
}
@@ -82,12 +93,12 @@ finish(void)
* the number of tests in the test suite.
*/
void
-plan(int count)
+plan(unsigned long count)
{
if (setvbuf(stdout, NULL, _IOLBF, BUFSIZ) != 0)
fprintf(stderr, "# cannot set stdout to line buffered: %s\n",
strerror(errno));
- printf("1..%d\n", count);
+ printf("1..%lu\n", count);
testnum = 1;
_planned = count;
_process = getpid();
@@ -96,6 +107,23 @@ plan(int count)
/*
+ * Initialize things for lazy planning, where we'll automatically print out a
+ * plan at the end of the program. Turns on line buffering on stdout as well.
+ */
+void
+plan_lazy(void)
+{
+ if (setvbuf(stdout, NULL, _IOLBF, BUFSIZ) != 0)
+ fprintf(stderr, "# cannot set stdout to line buffered: %s\n",
+ strerror(errno));
+ testnum = 1;
+ _process = getpid();
+ _lazy = 1;
+ atexit(finish);
+}
+
+
+/*
* Skip the entire test suite and exits. Should be called instead of plan(),
* not after it, since it prints out a special plan line.
*/
@@ -134,7 +162,7 @@ print_desc(const char *format, va_list args)
void
ok(int success, const char *format, ...)
{
- printf("%sok %d", success ? "" : "not ", testnum++);
+ printf("%sok %lu", success ? "" : "not ", testnum++);
if (!success)
_failed++;
if (format != NULL) {
@@ -149,12 +177,27 @@ ok(int success, const char *format, ...)
/*
+ * Same as ok(), but takes the format arguments as a va_list.
+ */
+void
+okv(int success, const char *format, va_list args)
+{
+ printf("%sok %lu", success ? "" : "not ", testnum++);
+ if (!success)
+ _failed++;
+ if (format != NULL)
+ print_desc(format, args);
+ putchar('\n');
+}
+
+
+/*
* Skip a test.
*/
void
skip(const char *reason, ...)
{
- printf("ok %d # skip", testnum++);
+ printf("ok %lu # skip", testnum++);
if (reason != NULL) {
va_list args;
@@ -171,12 +214,12 @@ skip(const char *reason, ...)
* Report the same status on the next count tests.
*/
void
-ok_block(int count, int status, const char *format, ...)
+ok_block(unsigned long count, int status, const char *format, ...)
{
- int i;
+ unsigned long i;
for (i = 0; i < count; i++) {
- printf("%sok %d", status ? "" : "not ", testnum++);
+ printf("%sok %lu", status ? "" : "not ", testnum++);
if (!status)
_failed++;
if (format != NULL) {
@@ -195,12 +238,12 @@ ok_block(int count, int status, const char *format, ...)
* Skip the next count tests.
*/
void
-skip_block(int count, const char *reason, ...)
+skip_block(unsigned long count, const char *reason, ...)
{
- int i;
+ unsigned long i;
for (i = 0; i < count; i++) {
- printf("ok %d # skip", testnum++);
+ printf("ok %lu # skip", testnum++);
if (reason != NULL) {
va_list args;
@@ -219,13 +262,13 @@ skip_block(int count, const char *reason, ...)
* if those two numbers match.
*/
void
-is_int(int wanted, int seen, const char *format, ...)
+is_int(long wanted, long seen, const char *format, ...)
{
if (wanted == seen)
- printf("ok %d", testnum++);
+ printf("ok %lu", testnum++);
else {
- printf("# wanted: %d\n# seen: %d\n", wanted, seen);
- printf("not ok %d", testnum++);
+ printf("# wanted: %ld\n# seen: %ld\n", wanted, seen);
+ printf("not ok %lu", testnum++);
_failed++;
}
if (format != NULL) {
@@ -251,10 +294,10 @@ is_string(const char *wanted, const char *seen, const char *format, ...)
if (seen == NULL)
seen = "(null)";
if (strcmp(wanted, seen) == 0)
- printf("ok %d", testnum++);
+ printf("ok %lu", testnum++);
else {
printf("# wanted: %s\n# seen: %s\n", wanted, seen);
- printf("not ok %d", testnum++);
+ printf("not ok %lu", testnum++);
_failed++;
}
if (format != NULL) {
@@ -276,10 +319,10 @@ void
is_double(double wanted, double seen, const char *format, ...)
{
if (wanted == seen)
- printf("ok %d", testnum++);
+ printf("ok %lu", testnum++);
else {
printf("# wanted: %g\n# seen: %g\n", wanted, seen);
- printf("not ok %d", testnum++);
+ printf("not ok %lu", testnum++);
_failed++;
}
if (format != NULL) {
@@ -301,11 +344,11 @@ void
is_hex(unsigned long wanted, unsigned long seen, const char *format, ...)
{
if (wanted == seen)
- printf("ok %d", testnum++);
+ printf("ok %lu", testnum++);
else {
printf("# wanted: %lx\n# seen: %lx\n", (unsigned long) wanted,
(unsigned long) seen);
- printf("not ok %d", testnum++);
+ printf("not ok %lu", testnum++);
_failed++;
}
if (format != NULL) {
@@ -354,3 +397,88 @@ sysbail(const char *format, ...)
printf(": %s\n", strerror(oerrno));
exit(1);
}
+
+
+/*
+ * Report a diagnostic to stderr.
+ */
+void
+diag(const char *format, ...)
+{
+ va_list args;
+
+ fflush(stdout);
+ printf("# ");
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+ printf("\n");
+}
+
+
+/*
+ * Report a diagnostic to stderr, appending strerror(errno).
+ */
+void
+sysdiag(const char *format, ...)
+{
+ va_list args;
+ int oerrno = errno;
+
+ fflush(stdout);
+ printf("# ");
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+ printf(": %s\n", strerror(oerrno));
+}
+
+
+/*
+ * Locate a test file. Given the partial path to a file, look under BUILD and
+ * then SOURCE for the file and return the full path to the file. Returns
+ * NULL if the file doesn't exist. A non-NULL return should be freed with
+ * test_file_path_free().
+ *
+ * This function uses sprintf because it attempts to be independent of all
+ * other portability layers. The use immediately after a memory allocation
+ * should be safe without using snprintf or strlcpy/strlcat.
+ */
+char *
+test_file_path(const char *file)
+{
+ char *base;
+ char *path = NULL;
+ size_t length;
+ const char *envs[] = { "BUILD", "SOURCE", NULL };
+ int i;
+
+ for (i = 0; envs[i] != NULL; i++) {
+ base = getenv(envs[i]);
+ if (base == NULL)
+ continue;
+ length = strlen(base) + 1 + strlen(file) + 1;
+ path = malloc(length);
+ if (path == NULL)
+ sysbail("cannot allocate memory");
+ sprintf(path, "%s/%s", base, file);
+ if (access(path, R_OK) == 0)
+ break;
+ free(path);
+ path = NULL;
+ }
+ return path;
+}
+
+
+/*
+ * Free a path returned from test_file_path(). This function exists primarily
+ * for Windows, where memory must be freed from the same library domain that
+ * it was allocated from.
+ */
+void
+test_file_path_free(char *path)
+{
+ if (path != NULL)
+ free(path);
+}
diff --git a/tests/tap/basic.h b/tests/tap/basic.h
index efe94ba..9602db4 100644
--- a/tests/tap/basic.h
+++ b/tests/tap/basic.h
@@ -1,6 +1,7 @@
/*
* Basic utility routines for the TAP protocol.
*
+ * Copyright 2009, 2010 Russ Allbery <rra@stanford.edu>
* Copyright 2006, 2007, 2008
* Board of Trustees, Leland Stanford Jr. University
* Copyright (c) 2004, 2005, 2006
@@ -14,6 +15,7 @@
#ifndef TAP_BASIC_H
#define TAP_BASIC_H 1
+#include <stdarg.h> /* va_list */
#include <sys/types.h> /* pid_t */
/*
@@ -56,29 +58,40 @@ BEGIN_DECLS
* The test count. Always contains the number that will be used for the next
* test status.
*/
-extern int testnum;
+extern unsigned long testnum;
/* Print out the number of tests and set standard output to line buffered. */
-void plan(int count);
+void plan(unsigned long count);
+
+/*
+ * Prepare for lazy planning, in which the plan will be printed automatically
+ * at the end of the test program.
+ */
+void plan_lazy(void);
/* Skip the entire test suite. Call instead of plan. */
void skip_all(const char *format, ...)
__attribute__((__noreturn__, __format__(printf, 1, 2)));
-/* Basic reporting functions. */
+/*
+ * Basic reporting functions. The okv() function is the same as ok() but
+ * takes the test description as a va_list to make it easier to reuse the
+ * reporting infrastructure when writing new tests.
+ */
void ok(int success, const char *format, ...)
__attribute__((__format__(printf, 2, 3)));
+void okv(int success, const char *format, va_list args);
void skip(const char *reason, ...)
__attribute__((__format__(printf, 1, 2)));
/* Report the same status on, or skip, the next count tests. */
-void ok_block(int count, int success, const char *format, ...)
+void ok_block(unsigned long count, int success, const char *format, ...)
__attribute__((__format__(printf, 3, 4)));
-void skip_block(int count, const char *reason, ...)
+void skip_block(unsigned long count, const char *reason, ...)
__attribute__((__format__(printf, 2, 3)));
/* Check an expected value against a seen value. */
-void is_int(int wanted, int seen, const char *format, ...)
+void is_int(long wanted, long seen, const char *format, ...)
__attribute__((__format__(printf, 3, 4)));
void is_double(double wanted, double seen, const char *format, ...)
__attribute__((__format__(printf, 3, 4)));
@@ -93,6 +106,20 @@ void bail(const char *format, ...)
void sysbail(const char *format, ...)
__attribute__((__noreturn__, __nonnull__, __format__(printf, 1, 2)));
+/* Report a diagnostic to stderr prefixed with #. */
+void diag(const char *format, ...)
+ __attribute__((__nonnull__, __format__(printf, 1, 2)));
+void sysdiag(const char *format, ...)
+ __attribute__((__nonnull__, __format__(printf, 1, 2)));
+
+/*
+ * Find a test file under BUILD or SOURCE, returning the full path. The
+ * returned path should be freed with test_file_path_free().
+ */
+char *test_file_path(const char *file)
+ __attribute__((__malloc__, __nonnull__));
+void test_file_path_free(char *path);
+
END_DECLS
-#endif /* LIBTEST_H */
+#endif /* TAP_BASIC_H */
diff --git a/tests/tap/kerberos.c b/tests/tap/kerberos.c
index 700212e..a17d980 100644
--- a/tests/tap/kerberos.c
+++ b/tests/tap/kerberos.c
@@ -23,33 +23,6 @@
/*
- * Given the partial path to a file, look under BUILD and then SOURCE for the
- * file and return the full path to the file in newly-allocated memory.
- * Returns NULL if the file doesn't exist.
- */
-static char *
-find_file(const char *file)
-{
- char *base;
- char *path = NULL;
- const char *envs[] = { "BUILD", "SOURCE", NULL };
- int i;
-
- for (i = 0; envs[i] != NULL; i++) {
- base = getenv(envs[i]);
- if (base == NULL)
- continue;
- path = concatpath(base, file);
- if (access(path, R_OK) == 0)
- break;
- free(path);
- path = NULL;
- }
- return path;
-}
-
-
-/*
* Obtain Kerberos tickets for the principal specified in test.principal using
* the keytab specified in test.keytab, both of which are presumed to be in
* tests/data in either the build or the source tree.
@@ -78,7 +51,7 @@ kerberos_setup(void)
krb5_creds creds;
/* Read the principal name and find the keytab file. */
- path = find_file("data/test.principal");
+ path = test_file_path("data/test.principal");
if (path == NULL)
return NULL;
file = fopen(path, "r");
@@ -95,7 +68,7 @@ kerberos_setup(void)
bail("no newline in %s", path);
free(path);
principal[strlen(principal) - 1] = '\0';
- path = find_file("data/test.keytab");
+ path = test_file_path("data/test.keytab");
if (path == NULL)
return NULL;
diff --git a/tests/tap/kerberos.sh b/tests/tap/kerberos.sh
index da07e66..904cae5 100644
--- a/tests/tap/kerberos.sh
+++ b/tests/tap/kerberos.sh
@@ -1,7 +1,7 @@
# Shell function library to initialize Kerberos credentials
#
# Written by Russ Allbery <rra@stanford.edu>
-# Copyright 2009 Board of Trustees, Leland Stanford Jr. University
+# Copyright 2009, 2010 Board of Trustees, Leland Stanford Jr. University
#
# See LICENSE for licensing terms.
@@ -10,18 +10,9 @@
# configured. Sets the global principal variable to the principal to use.
kerberos_setup () {
local keytab
- keytab=''
- for f in "$BUILD/data/test.keytab" "$SOURCE/data/test.keytab" ; do
- if [ -r "$f" ] ; then
- keytab="$f"
- fi
- done
- principal=''
- for f in "$BUILD/data/test.principal" "$SOURCE/data/test.principal" ; do
- if [ -r "$f" ] ; then
- principal=`cat "$BUILD/data/test.principal"`
- fi
- done
+ keytab=`test_file_path data/test.keytab`
+ principal=`test_file_path data/test.principal`
+ principal=`cat "$principal" 2>/dev/null`
if [ -z "$keytab" ] || [ -z "$principal" ] ; then
return 1
fi
@@ -46,3 +37,18 @@ kerberos_setup () {
kerberos_cleanup () {
rm -f "$BUILD/data/test.cache"
}
+
+# List the contents of a keytab with enctypes and keys. This adjusts for the
+# difference between MIT Kerberos (which uses klist) and Heimdal (which uses
+# ktutil). Be careful to try klist first, since the ktutil on MIT Kerberos
+# may just hang. Takes the keytab to list and the file into which to save the
+# output, and strips off the header containing the file name.
+ktutil_list () {
+ if klist -keK "$1" > ktutil-tmp 2>/dev/null ; then
+ :
+ else
+ ktutil -k "$1" list --keys > ktutil-tmp < /dev/null 2>/dev/null
+ fi
+ sed -e '/Keytab name:/d' -e "/^[^ ]*:/d" ktutil-tmp > "$2"
+ rm -f ktutil-tmp
+}
diff --git a/tests/tap/libtap.sh b/tests/tap/libtap.sh
index 1846840..a9b46d4 100644
--- a/tests/tap/libtap.sh
+++ b/tests/tap/libtap.sh
@@ -1,7 +1,7 @@
# Shell function library for test cases.
#
# Written by Russ Allbery <rra@stanford.edu>
-# Copyright 2009 Russ Allbery <rra@stanford.edu>
+# Copyright 2009, 2010 Russ Allbery <rra@stanford.edu>
# Copyright 2006, 2007, 2008 Board of Trustees, Leland Stanford Jr. University
#
# See LICENSE for licensing terms.
@@ -15,10 +15,22 @@ plan () {
trap finish 0
}
+# Prepare for lazy planning.
+plan_lazy () {
+ count=1
+ planned=0
+ failed=0
+ trap finish 0
+}
+
# Report the test status on exit.
finish () {
local highest looks
highest=`expr "$count" - 1`
+ if [ "$planned" = 0 ] ; then
+ echo "1..$highest"
+ planned="$highest"
+ fi
looks='# Looks like you'
if [ "$planned" -gt 0 ] ; then
if [ "$planned" -gt "$highest" ] ; then
@@ -146,3 +158,21 @@ bail () {
echo 'Bail out!' "$@"
exit 1
}
+
+# Output a diagnostic on standard error, preceded by the required # mark.
+diag () {
+ echo '#' "$@"
+}
+
+# Search for the given file first in $BUILD and then in $SOURCE and echo the
+# path where the file was found, or the empty string if the file wasn't
+# found.
+test_file_path () {
+ if [ -f "$BUILD/$1" ] ; then
+ echo "$BUILD/$1"
+ elif [ -f "$SOURCE/$1" ] ; then
+ echo "$SOURCE/$1"
+ else
+ echo ''
+ fi
+}
diff --git a/tests/tap/remctl.sh b/tests/tap/remctl.sh
index b9667ef..9e01bcf 100644
--- a/tests/tap/remctl.sh
+++ b/tests/tap/remctl.sh
@@ -10,18 +10,12 @@
remctld_start () {
local keytab principal
rm -f "$BUILD/data/remctld.pid"
- keytab=''
- for f in "$BUILD/data/test.keytab" "$SOURCE/data/test.keytab" ; do
- if [ -r "$f" ] ; then
- keytab="$f"
- fi
- done
- principal=''
- for f in "$BUILD/data/test.principal" "$SOURCE/data/test.principal" ; do
- if [ -r "$f" ] ; then
- principal=`cat "$BUILD/data/test.principal"`
- fi
- done
+ keytab=`test_file_path data/test.keytab`
+ principal=`test_file_path data/test.principal`
+ principal=`cat "$principal" 2>/dev/null`
+ if [ -z "$keytab" ] || [ -z "$principal" ] ; then
+ return 1
+ fi
if [ -n "$VALGRIND" ] ; then
( "$VALGRIND" --log-file=valgrind.%p --leak-check=full "$1" -m \
-p 14373 -s "$principal" -P "$BUILD/data/remctld.pid" -f "$2" -d \
diff --git a/tests/util/messages-t.c b/tests/util/messages-t.c
index fb82a42..a58f82c 100644
--- a/tests/util/messages-t.c
+++ b/tests/util/messages-t.c
@@ -146,8 +146,8 @@ test_strerror(int status, const char *output, int error,
char *full_output, *name;
full_output = concat(output, ": ", strerror(error), "\n", (char *) NULL);
- xasprintf(&name, "strerror %d", testnum / 3 + 1);
- is_function_output(function, status, full_output, name);
+ xasprintf(&name, "strerror %lu", testnum / 3 + 1);
+ is_function_output(function, status, full_output, "%s", name);
free(full_output);
free(name);
}
diff --git a/tests/util/xmalloc.c b/tests/util/xmalloc.c
index 3bd5588..b6f4564 100644
--- a/tests/util/xmalloc.c
+++ b/tests/util/xmalloc.c
@@ -246,8 +246,6 @@ main(int argc, char *argv[])
size_t limit = 0;
int willfail = 0;
unsigned char code;
- struct rlimit rl;
- void *tmp;
if (argc < 3)
die("Usage error. Type, size, and limit must be given.");
@@ -290,6 +288,9 @@ main(int argc, char *argv[])
*/
if (limit > 0) {
#if HAVE_SETRLIMIT && defined(RLIMIT_AS)
+ struct rlimit rl;
+ void *tmp;
+
rl.rlim_cur = limit;
rl.rlim_max = limit;
if (setrlimit(RLIMIT_AS, &rl) < 0) {