diff options
author | Russ Allbery <rra@stanford.edu> | 2010-02-21 17:45:55 -0800 |
---|---|---|
committer | Russ Allbery <rra@stanford.edu> | 2010-02-21 17:45:55 -0800 |
commit | 60210334fa3dbd5dd168199063c6ee850d750d0c (patch) | |
tree | 31e832ba6788076075d38e20ffd27ebf09430407 /tests | |
parent | e571a8eb96f42de5a114cf11ff1c3d63e5a8d301 (diff) |
Imported Upstream version 0.10
Diffstat (limited to 'tests')
50 files changed, 2475 insertions, 1487 deletions
diff --git a/tests/TESTS b/tests/TESTS index c94cce0..161941c 100644 --- a/tests/TESTS +++ b/tests/TESTS @@ -1,16 +1,18 @@ client/basic client/full -client/pod client/prompt -kasetkey/basic +docs/pod +docs/pod-spelling portable/asprintf +portable/mkstemp +portable/setenv portable/snprintf portable/strlcat portable/strlcpy server/admin server/backend 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 f18c28e..1ae3a70 100644 --- a/tests/client/basic-t.in +++ b/tests/client/basic-t.in @@ -1,56 +1,23 @@ #! /bin/sh -# $Id$ # # Test suite for the wallet command-line client. # # Written by Russ Allbery <rra@stanford.edu> -# Copyright 2006, 2007, 2008 Board of Trustees, Leland Stanford Jr. University +# Copyright 2006, 2007, 2008, 2010 +# Board of Trustees, Leland Stanford Jr. University # # See LICENSE for licensing terms. # Load the test library. -. "@abs_top_srcdir@/tests/libtest.sh" - -# Print the number of tests. -total=35 -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 - -# We need a modified krb5.conf file for the srvtab test to work, since we need -# to add a v4_realm setting for the test-k5.stanford.edu realm that the keytab -# is for. Despite all the Stanford hard-coding, this test isn't -# Stanford-specific. It just matches the data files shipped with the package. +. "$SOURCE/tap/libtap.sh" +. "$SOURCE/tap/kerberos.sh" +. "$SOURCE/tap/remctl.sh" +cd "$SOURCE" + +# We need a modified krb5.conf file to test wallet configuration settings in +# krb5.conf. Despite the hard-coding of test-k5.stanford.edu, this test isn't +# Stanford-specific; it just matches the files that are distributed with the +# package. krb5conf= for p in /etc/krb5.conf /usr/local/etc/krb5.conf data/krb5.conf ; do if [ -r "$p" ] ; then @@ -64,7 +31,7 @@ for p in /etc/krb5.conf /usr/local/etc/krb5.conf data/krb5.conf ; do [realms] test-k5.stanford.edu = { - v4_realm = TEST.STANFORD.EDU + v4_realm = test-k5.stanford.edu } EOF KRB5_CONFIG="./krb5.conf" @@ -73,44 +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 36 +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 sync-kaserver \ - autocreated +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. @@ -124,138 +86,86 @@ 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 -if [ ! -f sync-kaserver ] ; then - printcount "ok" -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 -if [ -f sync-kaserver ] ; then - printcount "ok" -else - printcount "not ok" -fi -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 [ -f sync-kaserver ] ; then - printcount "ok" - rm sync-kaserver -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 [ -f sync-kaserver ] ; then - printcount "ok" - rm sync-kaserver -else - printcount "not ok" -fi -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 +printf 'This is store input\000with a nul character' > store-input +echo 'file fake-nul' > store-correct +cat store-input >> store-correct +ok_program 'store from a file with a nul' 0 '' \ + "$wallet" -f store-input store file fake-nul +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/client/full-t.in b/tests/client/full-t.in index f4ef1d3..ce2789d 100644 --- a/tests/client/full-t.in +++ b/tests/client/full-t.in @@ -1,24 +1,23 @@ #!/usr/bin/perl -w -# $Id$ # -# tests/client/full-t -- End-to-end tests for the wallet client. +# End-to-end tests for the wallet client. # # Written by Russ Allbery <rra@stanford.edu> -# Copyright 2008 Board of Trustees, Leland Stanford Jr. University +# Copyright 2008, 2010 Board of Trustees, Leland Stanford Jr. University # # See LICENSE for licensing terms. # Point to our server configuration. This must be done before Wallet::Config # is loaded, and it's pulled in as a prerequisite for Wallet::Admin. -BEGIN { $ENV{WALLET_CONFIG} = '@abs_top_srcdir@/tests/data/wallet.conf' } +BEGIN { $ENV{WALLET_CONFIG} = "$ENV{SOURCE}/data/wallet.conf" } -BEGIN { our $total = 53 } +BEGIN { our $total = 59 } use Test::More tests => $total; -use lib '@abs_top_srcdir@/perl'; +use lib "$ENV{SOURCE}/../perl"; use Wallet::Admin; -use lib '@abs_top_srcdir@/perl/t/lib'; +use lib "$ENV{SOURCE}/../perl/t/lib"; use Util; # Make a call to the wallet client. Takes the principal used by the server @@ -34,9 +33,9 @@ sub wallet { or die "cannot create wallet.out: $!\n"; open (STDERR, '>', 'wallet.err') or die "cannot create wallet.err: $!\n"; - exec ('@abs_top_builddir@/client/wallet', '-k', $principal, '-p', + exec ("$ENV{BUILD}/../client/wallet", '-k', $principal, '-p', '14373', '-s', 'localhost', @command) - or die "cannot run @abs_top_builddir@/client/wallet: $!\n"; + or die "cannot run $ENV{BUILD}/client/wallet: $!\n"; } else { waitpid ($pid, 0); } @@ -52,21 +51,24 @@ sub wallet { return ($output, $error, $status); } +# cd to the correct directory. +chdir "$ENV{SOURCE}" or die "Cannot chdir to $ENV{SOURCE}: $!\n"; + SKIP: { skip 'no keytab configuration', $total - unless -f '@abs_top_builddir@/tests/data/test.keytab'; + unless -f "$ENV{BUILD}/data/test.keytab"; my $remctld = '@REMCTLD@'; skip 'remctld not found', $total unless $remctld; # Spawn remctld and get local tickets. Don't destroy the user's Kerberos # ticket cache. unlink ('krb5cc_test', 'test-pid'); - my $principal = contents ('@abs_top_builddir@/tests/data/test.principal'); + my $principal = contents ("$ENV{BUILD}/data/test.principal"); remctld_spawn ($remctld, $principal, - '@abs_top_builddir@/tests/data/test.keytab', - '@abs_top_builddir@/tests/data/full.conf'); + "$ENV{BUILD}/data/test.keytab", + "$ENV{SOURCE}/data/full.conf"); $ENV{KRB5CCNAME} = 'krb5cc_test'; - getcreds ('@abs_top_builddir@/tests/data/test.keytab', $principal); + getcreds ("$ENV{BUILD}/data/test.keytab", $principal); # Use Wallet::Admin to set up the database. db_setup; @@ -166,6 +168,22 @@ SKIP: { is ($out, '-q', ' with the right output'); is ($err, '', ' and no error'); + # Store data containing nul characters. + my $data = "Some data\000with a nul"; + open (IN, '>', 'tmp-file') or BAIL_OUT ("cannot create tmp-file: $!"); + print IN $data; + close IN; + ($out, $err, $status) = wallet ($principal, '-f', 'tmp-file', 'store', + 'file', 'auto'); + unlink ('tmp-file'); + is ($status, 0, 'Storing data with a nul succeeds'); + is ($out, '', ' with no output'); + is ($err, '', ' and no error'); + ($out, $err, $status) = wallet ($principal, 'get', 'file', 'auto'); + is ($status, 0, 'Object get succeeds'); + is ($out, $data, ' with the right output'); + is ($err, '', ' and no error'); + # All done. remctld_stop; $admin->destroy; diff --git a/tests/client/pod-t.in b/tests/client/pod-t.in deleted file mode 100644 index 98c34c7..0000000 --- a/tests/client/pod-t.in +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/perl -# $Id$ -# -# Test POD formatting for client documentation. -# -# Written by Russ Allbery <rra@stanford.edu> -# Copyright 2008 Board of Trustees, Leland Stanford Jr. University -# -# See LICENSE for licensing terms. - -use Test::More; - -my @files = qw(wallet.pod); -my $total = scalar (@files); -plan tests => $total; - -eval 'use Test::Pod 1.00'; -SKIP: { - skip $total, 'Test::Pod 1.00 required for testing POD' if $@; - for my $file (@files) { - pod_file_ok ("@abs_top_srcdir@/client/$file", "client/$file"); - } -} diff --git a/tests/client/prompt-t.in b/tests/client/prompt-t.in index 2d6097d..1d8b079 100644 --- a/tests/client/prompt-t.in +++ b/tests/client/prompt-t.in @@ -1,29 +1,27 @@ #!/usr/bin/perl -w -# $Id$ # -# tests/client/prompt-t -- Password prompting tests for the wallet client. +# Password prompting tests for the wallet client. # # Written by Russ Allbery <rra@stanford.edu> -# Copyright 2008 Board of Trustees, Leland Stanford Jr. University +# Copyright 2008, 2010 Board of Trustees, Leland Stanford Jr. University # # See LICENSE for licensing terms. BEGIN { our $total = 5 } use Test::More tests => $total; -use lib '@abs_top_srcdir@/perl'; +use lib "$ENV{SOURCE}/..//perl"; use Wallet::Admin; -use lib '@abs_top_srcdir@/perl/t/lib'; +use lib "$ENV{SOURCE}/../perl/t/lib"; use Util; # cd to the correct directory. -chdir '@abs_top_srcdir@/tests' - or die "Cannot chdir to @abs_top_srcdir@/tests: $!\n"; +chdir "$ENV{SOURCE}" or die "Cannot chdir to $ENV{SOURCE}: $!\n"; SKIP: { skip 'no password configuration', $total - unless -f '@abs_top_builddir@/tests/data/test.password'; + unless -f "$ENV{BUILD}/data/test.password"; my $remctld = '@REMCTLD@'; skip 'remctld not found', $total unless $remctld; eval { require Expect }; @@ -36,22 +34,21 @@ SKIP: { # Spawn remctld and set up with a different ticket cache. unlink ('krb5cc_test', 'test-pid'); - my $principal = contents ('@abs_top_builddir@/tests/data/test.principal'); - remctld_spawn ($remctld, $principal, - '@abs_top_builddir@/tests/data/test.keytab', - '@abs_top_builddir@/tests/data/basic.conf'); + my $principal = contents ("$ENV{BUILD}/data/test.principal"); + remctld_spawn ($remctld, $principal, "$ENV{BUILD}/data/test.keytab", + "$ENV{SOURCE}/data/basic.conf"); $ENV{KRB5CCNAME} = 'krb5cc_test'; # Read in the principal and password. - open (PASS, '<', '@abs_top_builddir@/tests/data/test.password') - or die "Cannot open @abs_top_builddir@/tests/data/test.password: $!\n"; + open (PASS, '<', "$ENV{BUILD}/data/test.password") + or die "Cannot open $ENV{BUILD}/data/test.password: $!\n"; my $user = <PASS>; my $password = <PASS>; close PASS; chomp ($user, $password); # Spawn wallet and check an invalid password. - my $wallet = Expect->spawn ('@abs_top_builddir@/client/wallet', '-k', + my $wallet = Expect->spawn ("$ENV{BUILD}/../client/wallet", '-k', $principal, '-p', 14373, '-s', 'localhost', '-c', 'fake-wallet', '-u', $user, 'get', 'keytab', 'service/fake-output'); @@ -62,7 +59,7 @@ SKIP: { $wallet->soft_close; # Now check a valid password. - $wallet = Expect->spawn ('@abs_top_builddir@/client/wallet', '-k', + $wallet = Expect->spawn ("$ENV{BUILD}/../client/wallet", '-k', $principal, '-p', 14373, '-s', 'localhost', '-c', 'fake-wallet', '-u', $user, 'get', 'keytab', 'service/fake-output'); diff --git a/tests/data/basic.conf b/tests/data/basic.conf index 7ad998f..5f3c2a3 100644 --- a/tests/data/basic.conf +++ b/tests/data/basic.conf @@ -1,4 +1,4 @@ # remctl configuration for wallet client tests. -# $Id$ +fake-wallet store data/cmd-fake stdin=last ANYUSER fake-wallet ALL data/cmd-fake ANYUSER diff --git a/tests/data/cmd-fake b/tests/data/cmd-fake index 3ffd9cc..add72fc 100755 --- a/tests/data/cmd-fake +++ b/tests/data/cmd-fake @@ -1,11 +1,10 @@ #!/bin/sh -# $Id$ # # This is a fake wallet backend that returns bogus data for verification by # the client test suite. It doesn't test any of the wallet server code. # # Written by Russ Allbery <rra@stanford.edu> -# Copyright 2007, 2008 Board of Trustees, Leland Stanford Jr. University +# Copyright 2007, 2008, 2010 Board of Trustees, Leland Stanford Jr. University # See LICENSE for licensing terms. command="$1" @@ -18,55 +17,6 @@ if [ "$type" != "keytab" ] && [ "$type" != "file" ] ; then fi case "$command" in -getattr) - if [ -n "$3" ] ; then - echo "Too many arguments" >&2 - exit 1 - fi - if [ "$type" != "keytab" ] || [ "$2" != sync ] ; then - echo "Unknown attribute $2" >&2 - exit 1 - fi - case "$1" in - service/fake-srvtab) - if [ -f sync-kaserver ] ; then - echo "kaserver" - fi - ;; - *) - echo "Looking at sync attribute of wrong keytab" >&2 - exit 1 - ;; - esac - ;; -setattr) - if [ -n "$4" ] ; then - echo "Too many arguments" >&2 - exit 1 - fi - if [ "$type" != "keytab" ] || [ "$2" != sync ] ; then - echo "Unknown attribute $2" >&2 - exit 1 - fi - case "$1" in - service/fake-srvtab) - if [ "$3" = "kaserver" ] ; then - touch sync-kaserver - else - if [ "$3" = "" ] ; then - rm sync-kaserver - else - echo "Invalid attribute value $3" >&2 - exit 1 - fi - fi - ;; - *) - echo "Looking at sync attribute of wrong keytab" >&2 - exit 1 - ;; - esac - ;; check) if [ -n "$2" ] ; then echo "Too many arguments" >&2 @@ -132,10 +82,15 @@ get) ;; store) if [ -n "$3" ] ; then - echo "Too many arguments" >&2 + echo 'Too many arguments' >&2 + exit 1 + fi + if [ -n "$2" ] ; then + echo 'stdin remctld configuration not supported' >&2 exit 1 fi - printf "$type $1\n$2" > store-output + printf "$type $1\n" > store-output + cat >> store-output ;; show) if [ -n "$2" ] ; then diff --git a/tests/data/cmd-wrapper.in b/tests/data/cmd-wrapper index e119002..79b1943 100644..100755 --- a/tests/data/cmd-wrapper.in +++ b/tests/data/cmd-wrapper @@ -1,10 +1,8 @@ #!/bin/sh -# $Id$ # # Wrapper around the standard wallet-backend script that sets the Perl INC # path and the WALLET_CONFIG environment variable appropriately. -WALLET_CONFIG='@abs_top_srcdir@/tests/data/wallet.conf' +WALLET_CONFIG="$SOURCE/data/wallet.conf" export WALLET_CONFIG -exec perl -I'@abs_top_srcdir@/perl' '@abs_top_srcdir@/server/wallet-backend' \ - -q "$@" +exec perl -I"$SOURCE/../perl" "$SOURCE/../server/wallet-backend" -q "$@" diff --git a/tests/data/fake-kadmin b/tests/data/fake-kadmin index 81dc999..61906a4 100755 --- a/tests/data/fake-kadmin +++ b/tests/data/fake-kadmin @@ -1,5 +1,4 @@ #!/usr/bin/perl -w -# $Id$ # # fake-kadmin -- Fake kadmin.local used to test the keytab backend. # diff --git a/tests/data/fake-srvtab b/tests/data/fake-srvtab Binary files differindex 3c0ec65..f454af2 100644 --- a/tests/data/fake-srvtab +++ b/tests/data/fake-srvtab diff --git a/tests/data/full.conf b/tests/data/full.conf new file mode 100644 index 0000000..941a9ac --- /dev/null +++ b/tests/data/full.conf @@ -0,0 +1,4 @@ +# remctl configuration for full wallet client tests. + +wallet store data/cmd-wrapper stdin=4 ANYUSER +wallet ALL data/cmd-wrapper ANYUSER diff --git a/tests/data/full.conf.in b/tests/data/full.conf.in deleted file mode 100644 index b97a4bc..0000000 --- a/tests/data/full.conf.in +++ /dev/null @@ -1,4 +0,0 @@ -# remctl configuration for full wallet client tests. -# $Id$ - -wallet ALL @abs_top_builddir@/tests/data/cmd-wrapper ANYUSER diff --git a/tests/data/wallet.conf b/tests/data/wallet.conf index b864e5e..0a232dd 100644 --- a/tests/data/wallet.conf +++ b/tests/data/wallet.conf @@ -1,5 +1,4 @@ # wallet.conf -- Test wallet server configuration. -*- perl -*- -# $Id$ # Always test with SQLite. $DB_DRIVER = 'SQLite'; diff --git a/tests/docs/pod-spelling-t b/tests/docs/pod-spelling-t new file mode 100755 index 0000000..6993e4c --- /dev/null +++ b/tests/docs/pod-spelling-t @@ -0,0 +1,80 @@ +#!/usr/bin/perl +# +# Check for spelling errors in POD documentation +# +# Checks all POD files in the tree for spelling problems using Pod::Spell and +# either aspell or ispell. aspell is preferred. This test is disabled unless +# RRA_MAINTAINER_TESTS is set, since spelling dictionaries vary too much +# between environments. +# +# Copyright 2008, 2009 Russ Allbery <rra@stanford.edu> +# +# See LICENSE for licensing terms. + +use strict; +use Test::More; + +# Skip all spelling tests unless the maintainer environment variable is set. +plan skip_all => 'spelling tests only run for maintainer' + unless $ENV{RRA_MAINTAINER_TESTS}; + +# Load required Perl modules. +eval 'use Test::Pod 1.00'; +plan skip_all => 'Test::Pod 1.00 required for testing POD' if $@; +eval 'use Pod::Spell'; +plan skip_all => 'Pod::Spell required to test POD spelling' if $@; + +# Locate a spell-checker. hunspell is not currently supported due to its lack +# of support for contractions (at least in the version in Debian). +my @spell; +my %options = (aspell => [ qw(-d en_US --home-dir=./ list) ], + ispell => [ qw(-d american -l -p /dev/null) ]); +SEARCH: for my $program (qw/aspell ispell/) { + for my $dir (split ':', $ENV{PATH}) { + if (-x "$dir/$program") { + @spell = ("$dir/$program", @{ $options{$program} }); + } + last SEARCH if @spell; + } +} +plan skip_all => 'aspell or ispell required to test POD spelling' + unless @spell; + +# Prerequisites are satisfied, so we're going to do some testing. Figure out +# what POD files we have and from that develop our plan. +$| = 1; +my @pod = map { + my $pod = "$ENV{SOURCE}/../" . $_; + $pod =~ s,[^/.][^/]*/../,,g; + $pod; +} qw(client/wallet.pod server/keytab-backend server/wallet-admin + server/wallet-backend server/wallet-report); +plan tests => scalar @pod; + +# Finally, do the checks. +for my $pod (@pod) { + my $child = open (CHILD, '-|'); + if (not defined $child) { + BAIL_OUT ("cannot fork: $!"); + } elsif ($child == 0) { + my $pid = open (SPELL, '|-', @spell) + or BAIL_OUT ("cannot run @spell: $!"); + open (POD, '<', $pod) or BAIL_OUT ("cannot open $pod: $!"); + my $parser = Pod::Spell->new; + $parser->parse_from_filehandle (\*POD, \*SPELL); + close POD; + close SPELL; + exit ($? >> 8); + } else { + my @words = <CHILD>; + close CHILD; + SKIP: { + skip "@spell failed for $pod", 1 unless $? == 0; + for (@words) { + s/^\s+//; + s/\s+$//; + } + is ("@words", '', $pod); + } + } +} diff --git a/tests/docs/pod-t b/tests/docs/pod-t new file mode 100755 index 0000000..f92ba2c --- /dev/null +++ b/tests/docs/pod-t @@ -0,0 +1,21 @@ +#!/usr/bin/perl -w +# +# Test POD formatting for documentation. +# +# Written by Russ Allbery <rra@stanford.edu> +# Copyright 2008, 2010 Board of Trustees, Leland Stanford Jr. University +# +# See LICENSE for licensing terms. + +use strict; +use Test::More; +eval 'use Test::Pod 1.00'; +plan skip_all => 'Test::Pod 1.00 required for testing POD' if $@; + +my @files = qw(client/wallet.pod server/keytab-backend server/wallet-admin + server/wallet-backend server/wallet-report); +my $total = scalar (@files); +plan tests => $total; +for my $file (@files) { + pod_file_ok ("$ENV{SOURCE}/../$file", $file); +} diff --git a/tests/kasetkey/basic-t.in b/tests/kasetkey/basic-t.in deleted file mode 100644 index afc6747..0000000 --- a/tests/kasetkey/basic-t.in +++ /dev/null @@ -1,128 +0,0 @@ -#!/usr/bin/perl -w -# $Id$ -# -# Tests for basic kasetkey functionality. -# -# We only test creation (with a random key), deletion, enable, disable, and -# examine. That's enough to verify that kasetkey is basically working, and -# since AFS kaservers are becoming scarce, it's probably not worth the effort -# to do anything more comprehensive. -# -# We do test creation of a principal with a known key given a srvtab from -# inside the wallet server test suite already. -# -# Written by Russ Allbery <rra@stanford.edu> -# Copyright 2008 Board of Trustees, Leland Stanford Jr. University -# -# See LICENSE for licensing terms. - -BEGIN { our $total = 27 } -use Test::More tests => $total; - -use lib '@abs_top_builddir@/perl/blib/lib'; -use lib '@abs_top_srcdir@/perl/t/lib'; -use Util; - -# Global variables used for the kasetkey configuration. -our $ADMIN; -our $SRVTAB; - -# Make a call to the kasetkey client and returns the standard output, the -# standard error, and the exit status as a list. -sub kasetkey { - my @command = @_; - my $pid = fork; - if (not defined $pid) { - die "cannot fork: $!\n"; - } elsif ($pid == 0) { - open (STDOUT, '>', 'kasetkey.out') - or die "cannot create kasetkey.out: $!\n"; - open (STDERR, '>', 'kasetkey.err') - or die "cannot create kasetkey.err: $!\n"; - exec ('@abs_top_builddir@/kasetkey/kasetkey', '-a', $ADMIN, - '-k', $SRVTAB, @command) - or die "cannot run @abs_top_builddir@/kasetkey/kasetky: $!\n"; - } else { - waitpid ($pid, 0); - } - my $status = ($? >> 8); - local $/; - open (OUT, '<', 'kasetkey.out') or die "cannot open kasetkey.out: $!\n"; - my $output = <OUT>; - close OUT; - open (ERR, '<', 'kasetkey.err') or die "cannot open kasetkey.err: $!\n"; - my $error = <ERR>; - close ERR; - unlink ('kasetkey.out', 'kasetkey.err'); - return ($output, $error, $status); -} - -SKIP: { - skip 'no AFS kaserver configuration', $total - unless -f '@abs_top_builddir@/tests/data/test.srvtab'; - skip 'no AFS kaserver support', $total, - unless -x '@abs_top_builddir@/kasetkey/kasetkey'; - - # Set up the configuration. - $ADMIN = contents ('@abs_top_builddir@/tests/data/test.admin'); - $SRVTAB = '@abs_top_builddir@/tests/data/test.srvtab'; - my $realm = $ADMIN; - $realm =~ s/^[^\@]+\@//; - my $principal = "wallet.one\@$realm"; - - # Now we can start manipulating principals. Test examine and create. - my ($out, $err, $status) = kasetkey ('-e', $principal); - is ($status, 1, 'Examining a non-existent principal fails'); - is ($out, '', ' with no output'); - is ($err, "no such entry in the database\n", ' and the right error'); - ($out, $err, $status) = kasetkey ('-s', $principal, '-r'); - is ($status, 0, 'Creating a principal succeeds'); - is ($out, '', ' with no output'); - is ($err, '', ' and no error'); - ($out, $err, $status) = kasetkey ('-e', $principal); - is ($status, 0, 'Examining a principal succeeds'); - $out =~ s/: (Sun|Mon|Tue|Wed|Thu|Fri|Sat).*/: DATE/g; - my $shortadmin = $ADMIN; - $shortadmin =~ s/\@.*//; - my $enabled = <<"EOE"; -status: enabled -account expiration: never -password last changed: DATE -modification time: DATE -modified by: $shortadmin -EOE - is ($out, $enabled, ' with the right output'); - is ($err, '', ' and no error'); - - # Test enable and disable. - ($out, $err, $status) = kasetkey ('-s', $principal, '-n'); - is ($status, 0, 'Disabling a principal succeeds'); - is ($out, '', ' with no output'); - is ($err, '', ' and no error'); - ($out, $err, $status) = kasetkey ('-e', $principal); - is ($status, 0, ' and examining it still succeeds'); - $out =~ s/: (Sun|Mon|Tue|Wed|Thu|Fri|Sat).*/: DATE/g; - my $disabled = $enabled; - $disabled =~ s/enabled/disabled/; - is ($out, $disabled, ' with the right output'); - is ($err, '', ' and no error'); - ($out, $err, $status) = kasetkey ('-s', $principal, '-t'); - is ($status, 0, 'Enabling a principal succeeds'); - is ($out, '', ' with no output'); - is ($err, '', ' and no error'); - ($out, $err, $status) = kasetkey ('-e', $principal); - is ($status, 0, ' and examining it still succeeds'); - $out =~ s/: (Sun|Mon|Tue|Wed|Thu|Fri|Sat).*/: DATE/g; - is ($out, $enabled, ' with the right output'); - is ($err, '', ' and no error'); - - # Test deletion. - ($out, $err, $status) = kasetkey ('-D', $principal); - is ($status, 0, 'Deleting the principal succeeds'); - is ($out, '', ' with no output'); - is ($err, '', ' and no error'); - ($out, $err, $status) = kasetkey ('-e', $principal); - is ($status, 1, ' and now examining it fails'); - is ($out, '', ' with no output'); - is ($err, "no such entry in the database\n", ' and the right error'); -} diff --git a/tests/libtest.c b/tests/libtest.c deleted file mode 100644 index 76d5207..0000000 --- a/tests/libtest.c +++ /dev/null @@ -1,204 +0,0 @@ -/* $Id$ - * - * 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 ac2b083..0000000 --- a/tests/libtest.h +++ /dev/null @@ -1,70 +0,0 @@ -/* $Id$ - * - * 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 ed46d0e..0000000 --- a/tests/libtest.sh +++ /dev/null @@ -1,82 +0,0 @@ -# $Id$ -# -# 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 d42e740..04fbd1b 100644 --- a/tests/portable/asprintf-t.c +++ b/tests/portable/asprintf-t.c @@ -1,9 +1,9 @@ -/* $Id$ - * +/* * 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. */ @@ -11,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 @@ -33,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 c33e0e7..ca6ae61 100644 --- a/tests/portable/snprintf-t.c +++ b/tests/portable/snprintf-t.c @@ -1,33 +1,25 @@ -/* $Id$ - * +/* * 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); @@ -94,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]; @@ -104,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"); } @@ -125,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 c860803..e02c277 100644 --- a/tests/portable/strlcat-t.c +++ b/tests/portable/strlcat-t.c @@ -1,32 +1,20 @@ -/* $Id$ - * +/* * 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); @@ -36,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 8fb1f9c..ba224ba 100644 --- a/tests/portable/strlcpy-t.c +++ b/tests/portable/strlcpy-t.c @@ -1,32 +1,20 @@ -/* $Id$ - * +/* * 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); @@ -36,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 abad3b6..1670012 100644 --- a/tests/runtests.c +++ b/tests/runtests.c @@ -1,5 +1,4 @@ -/* $Id$ - * +/* * Run a set of tests, reporting results. * * Usage: @@ -18,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 @@ -45,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> @@ -64,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, @@ -79,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. */ @@ -90,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. */ @@ -104,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\ @@ -116,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. @@ -220,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 @@ -235,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; @@ -330,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. @@ -344,37 +398,40 @@ test_checkline(const char *line, struct testset *ts) status = TEST_FAIL; line += 4; } - if (strncmp(line, "ok ", 3) != 0) - return; - line += 3; - current = atoi(line); - if (current == 0) + if (strncmp(line, "ok", 2) != 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; @@ -450,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) { @@ -521,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)); @@ -562,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"); @@ -581,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)) @@ -595,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. */ @@ -667,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; @@ -742,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) { @@ -758,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; @@ -770,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) @@ -801,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/server/admin-t.in b/tests/server/admin-t index be40880..5bde104 100644..100755 --- a/tests/server/admin-t.in +++ b/tests/server/admin-t @@ -1,23 +1,21 @@ #!/usr/bin/perl -w -# $Id$ # # Tests for the wallet-admin dispatch code. # # Written by Russ Allbery <rra@stanford.edu> -# Copyright 2008 Board of Trustees, Leland Stanford Jr. University +# Copyright 2008, 2009, 2010 Board of Trustees, Leland Stanford Jr. University # # See LICENSE for licensing terms. use strict; -use Test::More tests => 54; +use Test::More tests => 36; # Create a dummy class for Wallet::Admin that prints what method was called # with its arguments and returns data for testing. package Wallet::Admin; -use vars qw($empty $error); +use vars qw($error); $error = 0; -$empty = 0; sub error { if ($error) { @@ -45,19 +43,6 @@ sub initialize { return 1; } -sub list_objects { - print "list_objects\n"; - return if ($error or $empty); - return ([ keytab => 'host/windlord.stanford.edu' ], - [ file => 'unix-wallet-password' ]); -} - -sub list_acls { - print "list_acls\n"; - return if ($error or $empty); - return ([ 1, 'ADMIN' ], [ 2, 'group/admins' ], [ 4, 'group/users' ]); -} - sub register_object { shift; print "register_object @_\n"; @@ -76,7 +61,7 @@ sub register_verifier { # Wallet::Admin package has already been loaded. package main; $INC{'Wallet/Admin.pm'} = 'FAKE'; -eval { do '@abs_top_srcdir@/server/wallet-admin' }; +eval { do "$ENV{SOURCE}/../server/wallet-admin" }; # Run the wallet admin client. This fun hack takes advantage of the fact that # the wallet admin client is written in Perl so that we can substitute our own @@ -99,10 +84,9 @@ is ($err, "unknown command foo\n", 'Unknown command'); is ($out, "new\n", ' and nothing ran'); # Check too few and too many arguments for every command. -my %commands = (destroy => [0, 0], - initialize => [1, 1], - list => [1, 1], - register => [3, 3]); +my %commands = (destroy => [0, 0], + initialize => [1, 1], + register => [3, 3]); for my $command (sort keys %commands) { my ($min, $max) = @{ $commands{$command} }; if ($min > 0) { @@ -111,10 +95,12 @@ for my $command (sort keys %commands) { "Too few arguments for $command"); is ($out, "new\n", ' and nothing ran'); } - ($out, $err) = run_admin ($command, ('foo') x ($max + 1)); - is ($err, "too many arguments to $command\n", - "Too many arguments for $command"); - is ($out, "new\n", ' and nothing ran'); + if ($max >= 0) { + ($out, $err) = run_admin ($command, ('foo') x ($max + 1)); + is ($err, "too many arguments to $command\n", + "Too many arguments for $command"); + is ($out, "new\n", ' and nothing ran'); + } } # Test destroy. @@ -150,22 +136,6 @@ is ($out, "new\n", ' and nothing was run'); is ($err, '', 'Initialize succeeds with a principal'); is ($out, "new\ninitialize rra\@stanford.edu\n", ' and runs the right code'); -# Test list. -($out, $err) = run_admin ('list', 'foo'); -is ($err, "only objects or acls are supported for list\n", - 'List requires a known object'); -is ($out, "new\n", ' and nothing was run'); -($out, $err) = run_admin ('list', 'objects'); -is ($err, '', 'List succeeds for objects'); -is ($out, "new\nlist_objects\n" - . "keytab host/windlord.stanford.edu\nfile unix-wallet-password\n", - ' and returns the right output'); -($out, $err) = run_admin ('list', 'acls'); -is ($err, '', 'List succeeds for ACLs'); -is ($out, "new\nlist_acls\n" - . "ADMIN (ACL ID: 1)\ngroup/admins (ACL ID: 2)\ngroup/users (ACL ID: 4)\n", - ' and returns the right output'); - # Test register. ($out, $err) = run_admin ('register', 'foo', 'foo', 'Foo::Bar'); is ($err, "only object or verifier is supported for register\n", @@ -191,12 +161,6 @@ is ($out, "new\n" is ($err, "some error\n", 'Error handling succeeds for initialize'); is ($out, "new\ninitialize rra\@stanford.edu\n", ' and calls the right methods'); -($out, $err) = run_admin ('list', 'objects'); -is ($err, "some error\n", 'Error handling succeeds for list objects'); -is ($out, "new\nlist_objects\n", ' and calls the right methods'); -($out, $err) = run_admin ('list', 'acls'); -is ($err, "some error\n", 'Error handling succeeds for list acls'); -is ($out, "new\nlist_acls\n", ' and calls the right methods'); ($out, $err) = run_admin ('register', 'object', 'foo', 'Foo::Object'); is ($err, "some error\n", 'Error handling succeeds for register object'); is ($out, "new\nregister_object foo Foo::Object\n", @@ -205,13 +169,3 @@ is ($out, "new\nregister_object foo Foo::Object\n", is ($err, "some error\n", 'Error handling succeeds for register verifier'); is ($out, "new\nregister_verifier foo Foo::Verifier\n", ' and calls the right methods'); - -# Test empty lists. -$Wallet::Admin::error = 0; -$Wallet::Admin::empty = 1; -($out, $err) = run_admin ('list', 'objects'); -is ($err, '', 'list objects runs with an empty list with no errors'); -is ($out, "new\nlist_objects\n", ' and calls the right methods'); -($out, $err) = run_admin ('list', 'acls'); -is ($err, '', 'list acls runs with an empty list and no errors'); -is ($out, "new\nlist_acls\n", ' and calls the right methods'); diff --git a/tests/server/backend-t.in b/tests/server/backend-t index e1518d8..b58d02c 100644..100755 --- a/tests/server/backend-t.in +++ b/tests/server/backend-t @@ -1,15 +1,15 @@ #!/usr/bin/perl -w -# $Id$ # # Tests for the wallet-backend dispatch code. # # Written by Russ Allbery <rra@stanford.edu> -# Copyright 2006, 2007, 2008 Board of Trustees, Leland Stanford Jr. University +# Copyright 2006, 2007, 2008, 2009, 2010 +# Board of Trustees, Leland Stanford Jr. University # # See LICENSE for licensing terms. use strict; -use Test::More tests => 1263; +use Test::More tests => 1269; # Create a dummy class for Wallet::Server that prints what method was called # with its arguments and returns data for testing. @@ -163,7 +163,8 @@ package main; $INC{'Wallet/Server.pm'} = 'FAKE'; my $OUTPUT; our $SYSLOG = \$OUTPUT; -eval { do '@abs_top_srcdir@/server/wallet-backend' }; +my $INPUT = ''; +eval { do "$ENV{SOURCE}/../server/wallet-backend" }; # Run the wallet backend. This fun hack takes advantage of the fact that the # wallet backend is written in Perl so that we can substitute our own @@ -173,6 +174,8 @@ sub run_backend { my $result = ''; open (OUTPUT, '>', \$result) or die "cannot create output string: $!\n"; select OUTPUT; + close STDIN; + open (STDIN, '<', \$INPUT) or die "cannot change stdin: $!\n"; local $| = 1; eval { command (@args) }; my $error = $@; @@ -224,7 +227,7 @@ my %commands = (autocreate => [2, 2], setacl => [4, 4], setattr => [4, 9], show => [2, 2], - store => [3, 3]); + store => [2, 3]); my %acl_commands = (add => [3, 3], create => [1, 1], destroy => [1, 1], @@ -326,6 +329,7 @@ for my $command (qw/autocreate create destroy setacl setattr store/) { $method ||= $command; my @extra = ('foo') x ($commands{$command}[0] - 2); my $extra = @extra ? join (' ', '', @extra) : ''; + $extra = ' ' if $command eq 'store'; ($out, $err) = run_backend ($command, 'type', 'name', @extra); my $ran; if ($command eq 'store') { @@ -339,7 +343,11 @@ for my $command (qw/autocreate create destroy setacl setattr store/) { is ($out, "$new\n$method type name$extra\n", ' and ran the right method'); ($out, $err) = run_backend ($command, 'error', 'name', @extra); - $ran = "$command error name" . (@extra ? " @extra" : ''); + if ($command eq 'store') { + $ran = "$command error name"; + } else { + $ran = "$command error name" . (@extra ? " @extra" : ''); + } is ($err, "error count $error\n", "Command $command ran with errors"); is ($OUTPUT, "command $ran from admin (1.2.3.4) failed: error count" . " $error\n", ' and syslog correct'); @@ -409,7 +417,7 @@ for my $command (qw/check expires get getacl getattr history owner show/) { ' and ran the right method with output'); } ($out, $err) = run_backend ($command, 'error', 'name', @extra); - my $ran = "$command error name" . (@extra ? " @extra" : ''); + $ran = "$command error name" . (@extra ? " @extra" : ''); is ($err, "error count $error\n", "Command $command ran with errors"); is ($OUTPUT, "command $ran from admin (1.2.3.4) failed: error count" . " $error\n", ' and syslog correct'); @@ -464,6 +472,22 @@ for my $command (sort keys %flag_commands) { $error++; } +# Special check for store allowing nul characters on standard input. +$INPUT = "Some data\000with a nul character"; +($out, $err) = run_backend ('store', 'type', 'name'); +is ($err, '', 'store with nul data ran with no errors'); +is ($OUTPUT, "command store type name from admin (1.2.3.4) succeeded\n", + ' and success logged'); +is ($out, "$new\nstore type name $INPUT\n", + ' and ran the right method'); +$INPUT = ''; +($out, $err) = run_backend ('store', 'type', 'name'); +is ($err, '', 'store with empty stdin data ran with no errors'); +is ($OUTPUT, "command store type name from admin (1.2.3.4) succeeded\n", + ' and success logged'); +is ($out, "$new\nstore type name \n", + ' and ran the right method'); + # Almost done. All that remains is to test the robustness of the bad # character checks against every possible character and test permitting the # empty argument. diff --git a/tests/server/keytab-t.in b/tests/server/keytab-t index f74267d..2a0ceed 100644..100755 --- a/tests/server/keytab-t.in +++ b/tests/server/keytab-t @@ -1,10 +1,9 @@ #!/usr/bin/perl -w -# $Id: backend-t.in 3547 2007-09-14 23:18:48Z rra $ # # Tests for the keytab-backend dispatch code. # # Written by Russ Allbery <rra@stanford.edu> -# Copyright 2006, 2007 Board of Trustees, Leland Stanford Jr. University +# Copyright 2006, 2007, 2010 Board of Trustees, Leland Stanford Jr. University # # See LICENSE for licensing terms. @@ -16,9 +15,9 @@ use Test::More tests => 63; # Load the keytab-backend code and override various settings. my $OUTPUT; $SYSLOG = \$OUTPUT; -eval { do '@abs_top_srcdir@/server/keytab-backend' }; -$CONFIG = '@abs_top_srcdir@/tests/data/allow-extract'; -$KADMIN = '@abs_top_srcdir@/tests/data/fake-kadmin'; +eval { do "$ENV{SOURCE}/../server/keytab-backend" }; +$CONFIG = "$ENV{SOURCE}/data/allow-extract"; +$KADMIN = "$ENV{SOURCE}/data/fake-kadmin"; $TMP = '.'; # Run the keytab backend. diff --git a/tests/server/pod-t.in b/tests/server/pod-t.in deleted file mode 100644 index fd939a5..0000000 --- a/tests/server/pod-t.in +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/perl -# $Id$ -# -# tests/server/pod-t -- Test POD formatting for client documentation. -# -# Written by Russ Allbery <rra@stanford.edu> -# Copyright 2008 Board of Trustees, Leland Stanford Jr. University -# -# See LICENSE for licensing terms. - -use Test::More; - -my @files = qw(keytab-backend wallet-admin wallet-backend); -my $total = scalar (@files); -plan tests => $total; - -eval 'use Test::Pod 1.00'; -SKIP: { - skip $total, 'Test::Pod 1.00 required for testing POD' if $@; - for my $file (@files) { - pod_file_ok ("@abs_top_srcdir@/server/$file", "server/$file"); - } -} diff --git a/tests/server/report-t b/tests/server/report-t new file mode 100755 index 0000000..285ee5a --- /dev/null +++ b/tests/server/report-t @@ -0,0 +1,151 @@ +#!/usr/bin/perl -w +# +# Tests for the wallet-report dispatch code. +# +# Written by Russ Allbery <rra@stanford.edu> +# Copyright 2008, 2009, 2010 Board of Trustees, Leland Stanford Jr. University +# +# See LICENSE for licensing terms. + +use strict; +use Test::More tests => 32; + +# Create a dummy class for Wallet::Report that prints what method was called +# with its arguments and returns data for testing. +package Wallet::Report; + +use vars qw($empty $error); +$error = 0; +$empty = 0; + +sub error { + if ($error) { + return "some error"; + } else { + return; + } +} + +sub new { + print "new\n"; + return bless ({}, 'Wallet::Report'); +} + +sub acls { + shift; + print "acls @_\n"; + return if ($error or $empty); + return ([ 1, 'ADMIN' ], [ 2, 'group/admins' ], [ 4, 'group/users' ]); +} + +sub objects { + shift; + print "objects @_\n"; + return if ($error or $empty); + return ([ keytab => 'host/windlord.stanford.edu' ], + [ file => 'unix-wallet-password' ]); +} + +sub owners { + shift; + print "owners @_\n"; + return if ($error or $empty); + return ([ krb5 => 'admin@EXAMPLE.COM' ]); +} + +# Back to the main package and the actual test suite. Lie about whether the +# Wallet::Report package has already been loaded. +package main; +$INC{'Wallet/Report.pm'} = 'FAKE'; +eval { do "$ENV{SOURCE}/../server/wallet-report" }; + +# Run the wallet report client. This fun hack takes advantage of the fact +# that the wallet report client is written in Perl so that we can substitute +# our own Wallet::Report class. +sub run_report { + my (@args) = @_; + my $result = ''; + open (OUTPUT, '>', \$result) or die "cannot create output string: $!\n"; + select OUTPUT; + local $| = 1; + eval { command (@args) }; + my $error = $@; + select STDOUT; + return ($result, $error); +} + +# Now for the actual tests. First check for unknown commands. +my ($out, $err) = run_report ('foo'); +is ($err, "unknown command foo\n", 'Unknown command'); +is ($out, "new\n", ' and nothing ran'); + +# Check too few and too many arguments for every command. +my %commands = (acls => [0, 3], + objects => [0, 2], + owners => [2, 2]); +for my $command (sort keys %commands) { + my ($min, $max) = @{ $commands{$command} }; + if ($min > 0) { + ($out, $err) = run_report ($command, ('foo') x ($min - 1)); + is ($err, "too few arguments to $command\n", + "Too few arguments for $command"); + is ($out, "new\n", ' and nothing ran'); + } + if ($max >= 0) { + ($out, $err) = run_report ($command, ('foo') x ($max + 1)); + is ($err, "too many arguments to $command\n", + "Too many arguments for $command"); + is ($out, "new\n", ' and nothing ran'); + } +} + +# Test the report methods. +($out, $err) = run_report ('acls'); +is ($err, '', 'List succeeds for ACLs'); +is ($out, "new\nacls \n" + . "ADMIN (ACL ID: 1)\ngroup/admins (ACL ID: 2)\ngroup/users (ACL ID: 4)\n", + ' and returns the right output'); +($out, $err) = run_report ('acls', 'entry', 'foo', 'foo'); +is ($err, '', 'List succeeds for ACLs'); +is ($out, "new\nacls entry foo foo\n" + . "ADMIN (ACL ID: 1)\ngroup/admins (ACL ID: 2)\ngroup/users (ACL ID: 4)\n", + ' and returns the right output'); +($out, $err) = run_report ('objects'); +is ($err, '', 'List succeeds for objects'); +is ($out, "new\nobjects \n" + . "keytab host/windlord.stanford.edu\nfile unix-wallet-password\n", + ' and returns the right output'); +($out, $err) = run_report ('objects', 'type', 'foo'); +is ($err, '', 'List succeeds for objects type foo'); +is ($out, "new\nobjects type foo\n" + . "keytab host/windlord.stanford.edu\nfile unix-wallet-password\n", + ' and returns the right output'); +($out, $err) = run_report ('owners', '%', '%'); +is ($err, '', 'Report succeeds for owners'); +is ($out, "new\nowners % %\nkrb5 admin\@EXAMPLE.COM\n", + ' and returns the right output'); + +# Test error handling. +$Wallet::Report::error = 1; +($out, $err) = run_report ('acls'); +is ($err, "some error\n", 'Error handling succeeds for list acls'); +is ($out, "new\nacls \n", ' and calls the right methods'); +($out, $err) = run_report ('objects'); +is ($err, "some error\n", 'Error handling succeeds for list objects'); +is ($out, "new\nobjects \n", ' and calls the right methods'); +($out, $err) = run_report ('owners', 'foo', 'bar'); +is ($err, "some error\n", 'Error handling succeeds for report owners'); +is ($out, "new\nowners foo bar\n", ' and calls the right methods'); + +# Test empty lists. +$Wallet::Report::error = 0; +$Wallet::Report::empty = 1; +($out, $err) = run_report ('acls'); +is ($err, '', 'list acls runs with an empty list and no errors'); +is ($out, "new\nacls \n", ' and calls the right methods'); +($out, $err) = run_report ('objects'); +is ($err, '', 'list objects runs with an empty list with no errors'); +is ($out, "new\nobjects \n", ' and calls the right methods'); +($out, $err) = run_report ('owners', 'foo', 'bar'); +is ($err, '', 'report owners runs with an empty list and no errors'); +is ($out, "new\nowners foo bar\n", ' and calls the right methods'); 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 2428d71..ca7de2c 100644 --- a/tests/util/concat-t.c +++ b/tests/util/concat-t.c @@ -1,59 +1,46 @@ -/* $Id$ - * +/* * 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 - * - * 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. + * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + * 2002, 2003 by The Internet Software Consortium and Rich Salz * - * 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 434ef56..fb82a42 100644 --- a/tests/util/messages-t.c +++ b/tests/util/messages-t.c @@ -1,26 +1,14 @@ -/* $Id$ - * +/* * 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 - * - * 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. + * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + * 2002, 2003 by The Internet Software Consortium and Rich Salz * - * 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> @@ -31,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> /* @@ -204,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. + * Given the intended status, intended message sans the appended strerror + * output, errno, and the function to run, check the output. */ static void -test_error(int n, int status, const char *output, test_function_t function) +test_strerror(int status, const char *output, int error, + test_function_type 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); -} + char *full_output, *name; - -/* - * Given the test number, intended status, intended message sans the appended - * strerror output, errno, and the function to run, print ok or not ok. - */ -static void -test_strerror(int n, int status, const char *output, int error, - test_function_t function) -{ - char *full_output; - - 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); } @@ -251,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 f721822..0000000 --- a/tests/util/xmalloc-t.in +++ /dev/null @@ -1,127 +0,0 @@ -#! /bin/sh -# $Id$ -# -# 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 699d0c4..3bd5588 100644 --- a/tests/util/xmalloc.c +++ b/tests/util/xmalloc.c @@ -1,28 +1,17 @@ -/* $Id$ - * +/* * 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> @@ -33,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> /* @@ -82,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); @@ -258,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."); @@ -270,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 @@ -278,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); |