diff options
Diffstat (limited to 'tests')
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); | 
