diff options
| author | Russ Allbery <rra@stanford.edu> | 2013-02-27 14:25:37 -0800 | 
|---|---|---|
| committer | Russ Allbery <rra@stanford.edu> | 2013-02-27 16:52:32 -0800 | 
| commit | 234e3805c524a7432caed8be328df6e2fbfe9afb (patch) | |
| tree | d40473e103f8cfacec000959c2ae3ea83e8e86b2 /tests | |
| parent | 7a572127a7305a17bf84c26e66e65ab37f66b77d (diff) | |
Update to rra-c-util 4.8 and C TAP Harness 1.12
Update to rra-c-util 4.8:
* Look for krb5-config in /usr/kerberos/bin after the user's PATH.
* Kerberos library probing fixes without transitive shared libraries.
* Fix Autoconf warnings when probing for AIX's bundled Kerberos.
* Avoid using krb5-config if --with-{krb5,gssapi}-{include,lib} given.
* Correctly remove -I/usr/include from Kerberos and GSS-API flags.
* Build on systems where krb5/krb5.h exists but krb5.h does not.
* Pass --deps to krb5-config unless --enable-reduced-depends was used.
* Do not use krb5-config results unless gssapi is supported.
* Fix probing for Heimdal's libroken to work with older versions.
* Update warning flags for GCC 4.6.1.
* Update utility library and test suite for newer GCC warnings.
* Fix broken GCC attribute markers causing compilation problems.
* Suppress warnings on compilers that support gcc's __attribute__.
* Add notices to all files copied over from rra-c-util.
* Fix warnings when reporting memory allocation failure in messages.c.
* Fix message utility library compiler warnings on 64-bit systems.
* Include strings.h for additional POSIX functions where found.
* Use an atexit handler to clean up after Kerberos tests.
* Kerberos test configuration now goes in tests/config.
* The principal of the test keytab is determined automatically.
* Simplify the test suite calls for Kerberos and remctl tests.
* Check for a missing ssize_t.
* Improve the xstrndup utility function.
* Checked asprintf variants are now void functions and cannot fail.
* Fix use of long long in portable/mkstemp.c.
* Fix test suite portability to Solaris.
* Substantial improvements to the POD syntax and spelling checks.
Update to C TAP Harness 1.12:
* Fix compliation of runtests with more aggressive warnings.
* Add a more complete usage message and a -h command-line flag.
* Flush stderr before printing output from tests.
* Better handle running shell tests without BUILD and SOURCE set.
* Fix runtests to honor -s even if BUILD and -b aren't given.
* runtests now frees all allocated resources on exit.
* Only use feature-test macros when requested or built with gcc -ansi.
* Drop is_double from the C TAP library to avoid requiring -lm.
* Avoid using local in the shell libtap.sh library.
* Suppress warnings on compilers that support gcc's __attribute__.
Change-Id: I394294d5486ac1ce265c7713bec71a148aaaf1ce
Reviewed-on: https://gerrit.stanford.edu/841
Reviewed-by: Russ Allbery <rra@stanford.edu>
Tested-by: Russ Allbery <rra@stanford.edu>
Diffstat (limited to 'tests')
34 files changed, 2701 insertions, 628 deletions
| diff --git a/tests/client/full-t.in b/tests/client/full-t.in index ce2789d..680e78f 100644 --- a/tests/client/full-t.in +++ b/tests/client/full-t.in @@ -56,19 +56,19 @@ chdir "$ENV{SOURCE}" or die "Cannot chdir to $ENV{SOURCE}: $!\n";  SKIP: {      skip 'no keytab configuration', $total -        unless -f "$ENV{BUILD}/data/test.keytab"; +        unless -f "$ENV{BUILD}/config/keytab";      my $remctld = '@REMCTLD@';      skip 'remctld not found', $total unless $remctld;      # Spawn remctld and get local tickets.  Don't destroy the user's Kerberos      # ticket cache.      unlink ('krb5cc_test', 'test-pid'); -    my $principal = contents ("$ENV{BUILD}/data/test.principal"); +    my $principal = contents ("$ENV{BUILD}/config/principal");      remctld_spawn ($remctld, $principal, -                   "$ENV{BUILD}/data/test.keytab", +                   "$ENV{BUILD}/config/keytab",                     "$ENV{SOURCE}/data/full.conf");      $ENV{KRB5CCNAME} = 'krb5cc_test'; -    getcreds ("$ENV{BUILD}/data/test.keytab", $principal); +    getcreds ("$ENV{BUILD}/config/keytab", $principal);      # Use Wallet::Admin to set up the database.      db_setup; diff --git a/tests/client/prompt-t.in b/tests/client/prompt-t.in index 1d8b079..682cd70 100644 --- a/tests/client/prompt-t.in +++ b/tests/client/prompt-t.in @@ -21,11 +21,11 @@ chdir "$ENV{SOURCE}" or die "Cannot chdir to $ENV{SOURCE}: $!\n";  SKIP: {      skip 'no password configuration', $total -        unless -f "$ENV{BUILD}/data/test.password"; +        unless -f "$ENV{BUILD}/config/password";      my $remctld = '@REMCTLD@';      skip 'remctld not found', $total unless $remctld;      eval { require Expect }; -    skip 'Exepct module not found', $total if $@; +    skip 'Expect module not found', $total if $@;      # Disable sending of wallet's output to our standard output.  Do this      # twice to avoid Perl warnings. @@ -34,14 +34,14 @@ SKIP: {      # Spawn remctld and set up with a different ticket cache.      unlink ('krb5cc_test', 'test-pid'); -    my $principal = contents ("$ENV{BUILD}/data/test.principal"); -    remctld_spawn ($remctld, $principal, "$ENV{BUILD}/data/test.keytab", +    my $principal = contents ("$ENV{BUILD}/config/principal"); +    remctld_spawn ($remctld, $principal, "$ENV{BUILD}/config/keytab",                     "$ENV{SOURCE}/data/basic.conf");      $ENV{KRB5CCNAME} = 'krb5cc_test';      # Read in the principal and password. -    open (PASS, '<', "$ENV{BUILD}/data/test.password") -        or die "Cannot open $ENV{BUILD}/data/test.password: $!\n"; +    open (PASS, '<', "$ENV{BUILD}/config/password") +        or die "Cannot open $ENV{BUILD}/config/password: $!\n";      my $user = <PASS>;      my $password = <PASS>;      close PASS; diff --git a/tests/config/README b/tests/config/README new file mode 100644 index 0000000..2992a11 --- /dev/null +++ b/tests/config/README @@ -0,0 +1,24 @@ +This directory contains configuration required to run the complete wallet +test suite.  If there is no configuration in this directory, some of the +tests will be skipped.  To enable the full test suite, create the +following files: + +keytab + +    A valid Kerberos keytab for a principal, preferrably in your local +    realm.  This will be used to test network interactions that require +    Kerberos authentication. + +principal + +    The name of the Kerberos principal whose keys are stored in keytab. + +password + +    This file should contain two lines.  The first line is the +    fully-qualified principal (including the realm) of a Kerberos +    principal to use for testing authentication.  The second line is +    the password for that principal.  The realm of the principal must +    be configured in your system krb5.conf file or in DNS configuration +    picked up by your Kerberos libraries and must be in the same realm as +    the keytab above or have valid cross-realm trust to it. diff --git a/tests/data/perl.conf b/tests/data/perl.conf new file mode 100644 index 0000000..eaf7443 --- /dev/null +++ b/tests/data/perl.conf @@ -0,0 +1,6 @@ +# Configuration for Perl tests.  -*- perl -*- + +# No special configuration yet. + +# File must end with this line. +1; diff --git a/tests/docs/pod-spelling-t b/tests/docs/pod-spelling-t index eaa7dd6..e1a95cd 100755 --- a/tests/docs/pod-spelling-t +++ b/tests/docs/pod-spelling-t @@ -1,80 +1,52 @@  #!/usr/bin/perl  # -# Check for spelling errors in POD documentation +# Checks all POD files in the tree for spelling errors using Test::Spelling. +# This test is disabled unless RRA_MAINTAINER_TESTS is set, since spelling +# dictionaries vary too much between environments.  # -# Checks all POD files in the tree for spelling problems using Pod::Spell and -# either aspell or ispell.  aspell is preferred.  This test is disabled unless -# RRA_MAINTAINER_TESTS is set, since spelling dictionaries vary too much -# between environments. +# The canonical version of this file is maintained in the rra-c-util package, +# which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.  # -# Copyright 2008, 2009 Russ Allbery <rra@stanford.edu> +# Written by Russ Allbery <rra@stanford.edu> +# Copyright 2012, 2013 +#     The Board of Trustees of the Leland Stanford Junior University  # -# See LICENSE for licensing terms. +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +use 5.006;  use strict; -use Test::More; +use warnings; -# Skip all spelling tests unless the maintainer environment variable is set. -plan skip_all => 'spelling tests only run for maintainer' -    unless $ENV{RRA_MAINTAINER_TESTS}; +use lib "$ENV{SOURCE}/tap/perl"; + +use Test::More; +use Test::RRA qw(skip_unless_maintainer use_prereq); +use Test::RRA::Automake qw(automake_setup perl_dirs); -# Load required Perl modules. -eval 'use Test::Pod 1.00'; -plan skip_all => 'Test::Pod 1.00 required for testing POD' if $@; -eval 'use Pod::Spell'; -plan skip_all => 'Pod::Spell required to test POD spelling' if $@; +# Only run this test for the maintainer. +skip_unless_maintainer('Spelling tests'); -# Locate a spell-checker.  hunspell is not currently supported due to its lack -# of support for contractions (at least in the version in Debian). -my @spell; -my %options = (aspell => [ qw(-d en_US --home-dir=./ list) ], -               ispell => [ qw(-d american -l -p /dev/null) ]); -SEARCH: for my $program (qw/aspell ispell/) { -    for my $dir (split ':', $ENV{PATH}) { -        if (-x "$dir/$program") { -            @spell = ("$dir/$program", @{ $options{$program} }); -        } -        last SEARCH if @spell; -    } -} -plan skip_all => 'aspell or ispell required to test POD spelling' -    unless @spell; +# Load prerequisite modules. +use_prereq('Test::Spelling'); -# Prerequisites are satisfied, so we're going to do some testing.  Figure out -# what POD files we have and from that develop our plan. -$| = 1; -my @pod = map { -    my $pod = "$ENV{SOURCE}/../" . $_; -    $pod =~ s,[^/.][^/]*/../,,g; -    $pod; -} qw(client/wallet.pod client/wallet-rekey.pod server/keytab-backend -     server/wallet-admin server/wallet-backend server/wallet-report); -plan tests => scalar @pod; +# Set up Automake testing. +automake_setup(); -# Finally, do the checks. -for my $pod (@pod) { -    my $child = open (CHILD, '-|'); -    if (not defined $child) { -        BAIL_OUT ("cannot fork: $!"); -    } elsif ($child == 0) { -        my $pid = open (SPELL, '|-', @spell) -            or BAIL_OUT ("cannot run @spell: $!"); -        open (POD, '<', $pod) or BAIL_OUT ("cannot open $pod: $!"); -        my $parser = Pod::Spell->new; -        $parser->parse_from_filehandle (\*POD, \*SPELL); -        close POD; -        close SPELL; -        exit ($? >> 8); -    } else { -        my @words = <CHILD>; -        close CHILD; -      SKIP: { -            skip "@spell failed for $pod", 1 unless $? == 0; -            for (@words) { -                s/^\s+//; -                s/\s+$//; -            } -            is ("@words", '', $pod); -        } -    } -} +# Run the tests. +all_pod_files_spelling_ok(perl_dirs()); diff --git a/tests/docs/pod-t b/tests/docs/pod-t index e25ade2..2743287 100755 --- a/tests/docs/pod-t +++ b/tests/docs/pod-t @@ -1,22 +1,48 @@  #!/usr/bin/perl -w  # -# Test POD formatting for documentation. +# Check all POD documents in the tree, except for any embedded Perl module +# distribution, for POD formatting errors. +# +# The canonical version of this file is maintained in the rra-c-util package, +# which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.  #  # Written by Russ Allbery <rra@stanford.edu> -# Copyright 2008, 2010 Board of Trustees, Leland Stanford Jr. University +# Copyright 2012, 2013 +#     The Board of Trustees of the Leland Stanford Junior University +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions:  # -# See LICENSE for licensing terms. +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +use 5.006;  use strict; +use warnings; + +use lib "$ENV{SOURCE}/tap/perl"; +  use Test::More; -eval 'use Test::Pod 1.00'; -plan skip_all => 'Test::Pod 1.00 required for testing POD' if $@; +use Test::RRA qw(use_prereq); +use Test::RRA::Automake qw(automake_setup perl_dirs); + +# Load prerequisite modules. +use_prereq('Test::Pod'); + +# Set up Automake testing. +automake_setup(); -my @files = qw(client/wallet.pod client/wallet-rekey.pod server/keytab-backend -               server/wallet-admin server/wallet-backend -               server/wallet-report); -my $total = scalar (@files); -plan tests => $total; -for my $file (@files) { -    pod_file_ok ("$ENV{SOURCE}/../$file", $file); -} +# Run the tests. +all_pod_files_ok(perl_dirs()); diff --git a/tests/portable/asprintf-t.c b/tests/portable/asprintf-t.c index 04fbd1b..4513a85 100644 --- a/tests/portable/asprintf-t.c +++ b/tests/portable/asprintf-t.c @@ -1,11 +1,18 @@  /*   * asprintf and vasprintf test suite.   * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>. + *   * Written by Russ Allbery <rra@stanford.edu> - * Copyright 2006, 2008, 2009 - *     Board of Trustees, Leland Stanford Jr. University   * - * See LICENSE for licensing terms. + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work.   */  #include <config.h> diff --git a/tests/portable/mkstemp-t.c b/tests/portable/mkstemp-t.c index 54701f7..98c708e 100644 --- a/tests/portable/mkstemp-t.c +++ b/tests/portable/mkstemp-t.c @@ -1,11 +1,18 @@  /*   * mkstemp test suite.   * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>. + *   * Written by Russ Allbery <rra@stanford.edu> - * Copyright 2002, 2004, 2008, 2009 - *     Board of Trustees, Leland Stanford Jr. University   * - * See LICENSE for licensing terms. + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work.   */  #include <config.h> diff --git a/tests/portable/setenv-t.c b/tests/portable/setenv-t.c index 5bc59ce..a1aecb5 100644 --- a/tests/portable/setenv-t.c +++ b/tests/portable/setenv-t.c @@ -1,14 +1,18 @@  /*   * setenv test suite.   * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>. + *   * 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. + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work.   */  #include <config.h> diff --git a/tests/portable/snprintf-t.c b/tests/portable/snprintf-t.c index 4b64f5b..927de96 100644 --- a/tests/portable/snprintf-t.c +++ b/tests/portable/snprintf-t.c @@ -1,14 +1,20 @@  /*   * snprintf test suite.   * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>. + *   * 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 (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, - *     2002, 2003 by The Internet Software Consortium and Rich Salz + * Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006 + *     Russ Allbery <rra@stanford.edu> + * Copyright 2009, 2010 + *     The Board of Trustees of the Leland Stanford Junior University + * Copyright 1995 Patrick Powell + * Copyright 2001 Hrvoje Niksic   * - * See LICENSE for licensing terms. + * This code is based on code written by Patrick Powell (papowell@astart.com) + * It may be used for any purpose as long as this notice remains intact + * on all source code distributions   */  #include <config.h> diff --git a/tests/portable/strlcat-t.c b/tests/portable/strlcat-t.c index e02c277..54d0d40 100644 --- a/tests/portable/strlcat-t.c +++ b/tests/portable/strlcat-t.c @@ -1,14 +1,18 @@  /*   * strlcat test suite.   * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>. + *   * 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. + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work.   */  #include <config.h> diff --git a/tests/portable/strlcpy-t.c b/tests/portable/strlcpy-t.c index ba224ba..26aa8f2 100644 --- a/tests/portable/strlcpy-t.c +++ b/tests/portable/strlcpy-t.c @@ -1,14 +1,18 @@  /*   * strlcpy test suite.   * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>. + *   * 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. + * The authors hereby relinquish any claim to any copyright that they may have + * in this work, whether granted under contract or by operation of law or + * international treaty, and hereby commit to the public, at large, that they + * shall not, at any time in the future, seek to enforce any copyright in this + * work against any person or entity, or prevent any person or entity from + * copying, publishing, distributing or creating derivative works of this + * work.   */  #include <config.h> diff --git a/tests/runtests.c b/tests/runtests.c index ab77629..4249875 100644 --- a/tests/runtests.c +++ b/tests/runtests.c @@ -3,14 +3,15 @@   *   * Usage:   * - *      runtests <test-list> + *      runtests [-b <build-dir>] [-s <source-dir>] <test-list> + *      runtests -o [-b <build-dir>] [-s <source-dir>] <test>   * - * Expects a list of executables located in the given file, one line per - * executable.  For each one, runs it as part of a test suite, reporting - * results.  Test output should start with a line containing the number of - * tests (numbered from 1 to this number), optionally preceded by "1..", - * although that line may be given anywhere in the output.  Each additional - * line should be in the following format: + * In the first case, expects a list of executables located in the given file, + * one line per executable.  For each one, runs it as part of a test suite, + * reporting results.  Test output should start with a line containing the + * number of tests (numbered from 1 to this number), optionally preceded by + * "1..", although that line may be given anywhere in the output.  Each + * additional line should be in the following format:   *   *      ok <number>   *      not ok <number> @@ -39,10 +40,21 @@   * This is a subset of TAP as documented in Test::Harness::TAP or   * TAP::Parser::Grammar, which comes with Perl.   * + * If the -o option is given, instead run a single test and display all of its + * output.  This is intended for use with failing tests so that the person + * running the test suite can get more details about what failed. + * + * If built with the C preprocessor symbols SOURCE and BUILD defined, C TAP + * Harness will export those values in the environment so that tests can find + * the source and build directory and will look for tests under both + * directories.  These paths can also be set with the -b and -s command-line + * options, which will override anything set at build time. + *   * Any bug reports, bug fixes, and improvements are very much welcome and - * should be sent to the e-mail address below. + * should be sent to the e-mail address below.  This program is part of C TAP + * Harness <http://www.eyrie.org/~eagle/software/c-tap-harness/>.   * - * Copyright 2000, 2001, 2004, 2006, 2007, 2008, 2009, 2010 + * Copyright 2000, 2001, 2004, 2006, 2007, 2008, 2009, 2010, 2011   *     Russ Allbery <rra@stanford.edu>   *   * Permission is hereby granted, free of charge, to any person obtaining a @@ -64,6 +76,13 @@   * DEALINGS IN THE SOFTWARE.  */ +/* Required for fdopen(), getopt(), and putenv(). */ +#if defined(__STRICT_ANSI__) || defined(PEDANTIC) +# ifndef _XOPEN_SOURCE +#  define _XOPEN_SOURCE 500 +# endif +#endif +  #include <ctype.h>  #include <errno.h>  #include <fcntl.h> @@ -71,6 +90,7 @@  #include <stdio.h>  #include <stdlib.h>  #include <string.h> +#include <strings.h>  #include <sys/stat.h>  #include <sys/time.h>  #include <sys/types.h> @@ -133,10 +153,10 @@ struct testset {      unsigned long skipped;      /* Count of skipped tests (passed). */      unsigned long allocated;    /* The size of the results table. */      enum test_status *results;  /* Table of results by test number. */ -    int aborted;                /* Whether the set as aborted. */ +    unsigned int aborted;       /* Whether the set as aborted. */      int reported;               /* Whether the results were reported. */      int status;                 /* The exit status of the test. */ -    int all_skipped;            /* Whether all tests were skipped. */ +    unsigned int all_skipped;   /* Whether all tests were skipped. */      char *reason;               /* Why all tests were skipped. */  }; @@ -147,6 +167,23 @@ struct testlist {  };  /* + * Usage message.  Should be used as a printf format with two arguments: the + * path to runtests, given twice. + */ +static const char usage_message[] = "\ +Usage: %s [-b <build-dir>] [-s <source-dir>] <test-list>\n\ +       %s -o [-b <build-dir>] [-s <source-dir>] <test>\n\ +\n\ +Options:\n\ +    -b <build-dir>      Set the build directory to <build-dir>\n\ +    -o                  Run a single test rather than a list of tests\n\ +    -s <source-dir>     Set the source directory to <source-dir>\n\ +\n\ +runtests normally runs each test listed in a file whose path is given as\n\ +its command-line argument.  With the -o option, it instead runs a single\n\ +test and shows its complete output.\n"; + +/*   * Header used for test output.  %s is replaced by the file name of the list   * of tests.   */ @@ -367,7 +404,9 @@ test_plan(const char *line, struct testset *ts)       * Get the count, check it for validity, and initialize the struct.  If we       * have something of the form "1..0 # skip foo", the whole file was       * skipped; record that.  If we do skip the whole file, zero out all of -     * our statistics, since they're no longer relevant. +     * our statistics, since they're no longer relevant.  strtol is called +     * with a second argument to advance the line pointer past the count to +     * make it simpler to detect the # skip case.       */      n = strtol(line, (char **) &line, 10);      if (n == 0) { @@ -437,6 +476,7 @@ test_checkline(const char *line, struct testset *ts)      char *end;      long number;      unsigned long i, current; +    int outlen;      /* Before anything, check for a test abort. */      bail = strstr(line, "Bail out!"); @@ -557,7 +597,8 @@ test_checkline(const char *line, struct testset *ts)      ts->results[current - 1] = status;      test_backspace(ts);      if (isatty(STDOUT_FILENO)) { -        ts->length = printf("%lu/%lu", current, ts->count); +        outlen = printf("%lu/%lu", current, ts->count); +        ts->length = (outlen >= 0) ? outlen : 0;          fflush(stdout);      }  } @@ -565,23 +606,20 @@ test_checkline(const char *line, struct testset *ts)  /*   * Print out a range of test numbers, returning the number of characters it - * took up.  Add a comma and a space before the range if chars indicates that + * took up.  Takes the first number, the last number, the number of characters + * already printed on the line, and the limit of number of characters the line + * can hold.  Add a comma and a space before the range if chars indicates that   * something has already been printed on the line, and print ... instead if   * chars plus the space needed would go over the limit (use a limit of 0 to - * disable this. + * disable this).   */  static unsigned int  test_print_range(unsigned long first, unsigned long last, unsigned int chars,                   unsigned int limit)  {      unsigned int needed = 0; -    unsigned int out = 0;      unsigned long n; -    if (chars > 0) { -        needed += 2; -        if (!limit || chars <= limit) out += printf(", "); -    }      for (n = first; n > 0; n /= 10)          needed++;      if (last > first) { @@ -589,15 +627,26 @@ test_print_range(unsigned long first, unsigned long last, unsigned int chars,              needed++;          needed++;      } -    if (limit && chars + needed > limit) { -        if (chars <= limit) -            out += printf("..."); +    if (chars > 0) +        needed += 2; +    if (limit > 0 && chars + needed > limit) { +        needed = 0; +        if (chars <= limit) { +            if (chars > 0) { +                printf(", "); +                needed += 2; +            } +            printf("..."); +            needed += 3; +        }      } else { +        if (chars > 0) +            printf(", ");          if (last > first) -            out += printf("%lu-", first); -        out += printf("%lu", last); +            printf("%lu-", first); +        printf("%lu", last);      } -    return out; +    return needed;  } @@ -825,14 +874,14 @@ test_fail_summary(const struct testlist *fails)                      last = i + 1;                  else {                      if (first != 0) -                        chars += test_print_range(first, last, chars, 20); +                        chars += test_print_range(first, last, chars, 19);                      first = i + 1;                      last = i + 1;                  }              }          }          if (first != 0) -            test_print_range(first, last, chars, 20); +            test_print_range(first, last, chars, 19);          putchar('\n');          free(ts->file);          free(ts->path); @@ -861,10 +910,17 @@ find_test(const char *name, struct testset *ts, const char *source,            const char *build)  {      char *path; -    const char *bases[] = { ".", build, source, NULL }; +    const char *bases[4];      unsigned int i; -    for (i = 0; bases[i] != NULL; i++) { +    bases[0] = "."; +    bases[1] = build; +    bases[2] = source; +    bases[3] = NULL; + +    for (i = 0; i < 3; i++) { +        if (bases[i] == NULL) +            continue;          path = xmalloc(strlen(bases[i]) + strlen(name) + 4);          sprintf(path, "%s/%s-t", bases[i], name);          if (access(path, X_OK) != 0) @@ -993,6 +1049,7 @@ test_batch(const char *testlist, const char *source, const char *build)          failed += ts.failed;      }      total -= skipped; +    fclose(tests);      /* Stop the timer and get our child resource statistics. */      gettimeofday(&end, NULL); @@ -1060,17 +1117,23 @@ int  main(int argc, char *argv[])  {      int option; +    int status = 0;      int single = 0; -    char *setting; +    char *source_env = NULL; +    char *build_env = NULL;      const char *list;      const char *source = SOURCE;      const char *build = BUILD; -    while ((option = getopt(argc, argv, "b:os:")) != EOF) { +    while ((option = getopt(argc, argv, "b:hos:")) != EOF) {          switch (option) {          case 'b':              build = optarg;              break; +        case 'h': +            printf(usage_message, argv[0], argv[0]); +            exit(0); +            break;          case 'o':              single = 1;              break; @@ -1081,36 +1144,46 @@ main(int argc, char *argv[])              exit(1);          }      } -    argc -= optind; -    argv += optind; -    if (argc != 1) { -        fprintf(stderr, "Usage: runtests <test-list>\n"); +    if (argc - optind != 1) { +        fprintf(stderr, usage_message, argv[0], argv[0]);          exit(1);      } +    argc -= optind; +    argv += optind;      if (source != NULL) { -        setting = xmalloc(strlen("SOURCE=") + strlen(source) + 1); -        sprintf(setting, "SOURCE=%s", source); -        if (putenv(setting) != 0) +        source_env = xmalloc(strlen("SOURCE=") + strlen(source) + 1); +        sprintf(source_env, "SOURCE=%s", source); +        if (putenv(source_env) != 0)              sysdie("cannot set SOURCE in the environment");      }      if (build != NULL) { -        setting = xmalloc(strlen("BUILD=") + strlen(build) + 1); -        sprintf(setting, "BUILD=%s", build); -        if (putenv(setting) != 0) +        build_env = xmalloc(strlen("BUILD=") + strlen(build) + 1); +        sprintf(build_env, "BUILD=%s", build); +        if (putenv(build_env) != 0)              sysdie("cannot set BUILD in the environment");      } -    if (single) { +    if (single)          test_single(argv[0], source, build); -        exit(0); -    } else { +    else {          list = strrchr(argv[0], '/');          if (list == NULL)              list = argv[0];          else              list++;          printf(banner, list); -        exit(test_batch(argv[0], source, build) ? 0 : 1); +        status = test_batch(argv[0], source, build) ? 0 : 1; +    } + +    /* For valgrind cleanliness. */ +    if (source_env != NULL) { +        putenv((char *) "SOURCE="); +        free(source_env); +    } +    if (build_env != NULL) { +        putenv((char *) "BUILD="); +        free(build_env);      } +    exit(status);  } diff --git a/tests/tap/basic.c b/tests/tap/basic.c index 829f91a..e8196fc 100644 --- a/tests/tap/basic.c +++ b/tests/tap/basic.c @@ -1,22 +1,38 @@  /*   * Some utility routines for writing tests.   * - * Herein are a variety of utility routines for writing tests.  All routines - * of the form ok() or is*() take a test number and some number of appropriate - * arguments, check to be sure the results match the expected output using the - * arguments, and print out something appropriate for that test number.  Other - * utility routines help in constructing more complex tests, skipping tests, - * or setting up the TAP output format. + * Here are a variety of utility routines for writing tests compatible with + * the TAP protocol.  All routines of the form ok() or is*() take a test + * number and some number of appropriate arguments, check to be sure the + * results match the expected output using the arguments, and print out + * something appropriate for that test number.  Other utility routines help in + * constructing more complex tests, skipping tests, reporting errors, setting + * up the TAP output format, or finding things in the test environment.   * - * Copyright 2009, 2010 Russ Allbery <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 + * This file is part of C TAP Harness.  The current version plus supporting + * documentation is at <http://www.eyrie.org/~eagle/software/c-tap-harness/>.   * - * See LICENSE for licensing terms. + * Copyright 2009, 2010, 2011, 2012 Russ Allbery <rra@stanford.edu> + * Copyright 2001, 2002, 2004, 2005, 2006, 2007, 2008, 2011, 2012 + *     The Board of Trustees of the Leland Stanford Junior University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE.   */  #include <errno.h> @@ -24,12 +40,21 @@  #include <stdio.h>  #include <stdlib.h>  #include <string.h> +#ifdef _WIN32 +# include <direct.h> +#else +# include <sys/stat.h> +#endif  #include <sys/types.h> -#include <sys/time.h> -#include <sys/wait.h>  #include <unistd.h> -#include <tap/basic.h> +#include <tests/tap/basic.h> + +/* Windows provides mkdir and rmdir under different names. */ +#ifdef _WIN32 +# define mkdir(p, m) _mkdir(p) +# define rmdir(p)    _rmdir(p) +#endif  /*   * The test count.  Always contains the number that will be used for the next @@ -57,7 +82,9 @@ static int _lazy = 0;  /*   * Our exit handler.  Called on completion of the test to report a summary of - * results provided we're still in the original process. + * results provided we're still in the original process.  This also handles + * printing out the plan if we used plan_lazy(), although that's suppressed if + * we never ran a test (due to an early bail, for example).   */  static void  finish(void) @@ -66,8 +93,9 @@ finish(void)      if (_planned == 0 && !_lazy)          return; +    fflush(stderr);      if (_process != 0 && getpid() == _process) { -        if (_lazy) { +        if (_lazy && highest > 0) {              printf("1..%lu\n", highest);              _planned = highest;          } @@ -98,6 +126,7 @@ plan(unsigned long count)      if (setvbuf(stdout, NULL, _IOLBF, BUFSIZ) != 0)          fprintf(stderr, "# cannot set stdout to line buffered: %s\n",                  strerror(errno)); +    fflush(stderr);      printf("1..%lu\n", count);      testnum = 1;      _planned = count; @@ -130,6 +159,7 @@ plan_lazy(void)  void  skip_all(const char *format, ...)  { +    fflush(stderr);      printf("1..0 # skip");      if (format != NULL) {          va_list args; @@ -162,6 +192,7 @@ print_desc(const char *format, va_list args)  void  ok(int success, const char *format, ...)  { +    fflush(stderr);      printf("%sok %lu", success ? "" : "not ", testnum++);      if (!success)          _failed++; @@ -182,6 +213,7 @@ ok(int success, const char *format, ...)  void  okv(int success, const char *format, va_list args)  { +    fflush(stderr);      printf("%sok %lu", success ? "" : "not ", testnum++);      if (!success)          _failed++; @@ -197,6 +229,7 @@ okv(int success, const char *format, va_list args)  void  skip(const char *reason, ...)  { +    fflush(stderr);      printf("ok %lu # skip", testnum++);      if (reason != NULL) {          va_list args; @@ -218,6 +251,7 @@ ok_block(unsigned long count, int status, const char *format, ...)  {      unsigned long i; +    fflush(stderr);      for (i = 0; i < count; i++) {          printf("%sok %lu", status ? "" : "not ", testnum++);          if (!status) @@ -242,6 +276,7 @@ skip_block(unsigned long count, const char *reason, ...)  {      unsigned long i; +    fflush(stderr);      for (i = 0; i < count; i++) {          printf("ok %lu # skip", testnum++);          if (reason != NULL) { @@ -264,6 +299,7 @@ skip_block(unsigned long count, const char *reason, ...)  void  is_int(long wanted, long seen, const char *format, ...)  { +    fflush(stderr);      if (wanted == seen)          printf("ok %lu", testnum++);      else { @@ -293,6 +329,7 @@ is_string(const char *wanted, const char *seen, const char *format, ...)          wanted = "(null)";      if (seen == NULL)          seen = "(null)"; +    fflush(stderr);      if (strcmp(wanted, seen) == 0)          printf("ok %lu", testnum++);      else { @@ -312,37 +349,13 @@ is_string(const char *wanted, const char *seen, const char *format, ...)  /* - * Takes an expected double and a seen double and assumes the test passes if - * those two numbers match. - */ -void -is_double(double wanted, double seen, const char *format, ...) -{ -    if (wanted == seen) -        printf("ok %lu", testnum++); -    else { -        printf("# wanted: %g\n#   seen: %g\n", wanted, seen); -        printf("not ok %lu", testnum++); -        _failed++; -    } -    if (format != NULL) { -        va_list args; - -        va_start(args, format); -        print_desc(format, args); -        va_end(args); -    } -    putchar('\n'); -} - - -/*   * Takes an expected unsigned long and a seen unsigned long and assumes the   * test passes if the two numbers match.  Otherwise, reports them in hex.   */  void  is_hex(unsigned long wanted, unsigned long seen, const char *format, ...)  { +    fflush(stderr);      if (wanted == seen)          printf("ok %lu", testnum++);      else { @@ -370,6 +383,7 @@ bail(const char *format, ...)  {      va_list args; +    fflush(stderr);      fflush(stdout);      printf("Bail out! ");      va_start(args, format); @@ -389,6 +403,7 @@ sysbail(const char *format, ...)      va_list args;      int oerrno = errno; +    fflush(stderr);      fflush(stdout);      printf("Bail out! ");      va_start(args, format); @@ -407,6 +422,7 @@ diag(const char *format, ...)  {      va_list args; +    fflush(stderr);      fflush(stdout);      printf("# ");      va_start(args, format); @@ -425,6 +441,7 @@ sysdiag(const char *format, ...)      va_list args;      int oerrno = errno; +    fflush(stderr);      fflush(stdout);      printf("# ");      va_start(args, format); @@ -435,6 +452,92 @@ sysdiag(const char *format, ...)  /* + * Allocate cleared memory, reporting a fatal error with bail on failure. + */ +void * +bcalloc(size_t n, size_t size) +{ +    void *p; + +    p = calloc(n, size); +    if (p == NULL) +        sysbail("failed to calloc %lu", (unsigned long)(n * size)); +    return p; +} + + +/* + * Allocate memory, reporting a fatal error with bail on failure. + */ +void * +bmalloc(size_t size) +{ +    void *p; + +    p = malloc(size); +    if (p == NULL) +        sysbail("failed to malloc %lu", (unsigned long) size); +    return p; +} + + +/* + * Reallocate memory, reporting a fatal error with bail on failure. + */ +void * +brealloc(void *p, size_t size) +{ +    p = realloc(p, size); +    if (p == NULL) +        sysbail("failed to realloc %lu bytes", (unsigned long) size); +    return p; +} + + +/* + * Copy a string, reporting a fatal error with bail on failure. + */ +char * +bstrdup(const char *s) +{ +    char *p; +    size_t len; + +    len = strlen(s) + 1; +    p = malloc(len); +    if (p == NULL) +        sysbail("failed to strdup %lu bytes", (unsigned long) len); +    memcpy(p, s, len); +    return p; +} + + +/* + * Copy up to n characters of a string, reporting a fatal error with bail on + * failure.  Don't use the system strndup function, since it may not exist and + * the TAP library doesn't assume any portability support. + */ +char * +bstrndup(const char *s, size_t n) +{ +    const char *p; +    char *copy; +    size_t length; + +    /* Don't assume that the source string is nul-terminated. */ +    for (p = s; (size_t) (p - s) < n && *p != '\0'; p++) +        ; +    length = p - s; +    copy = malloc(length + 1); +    if (p == NULL) +        sysbail("failed to strndup %lu bytes", (unsigned long) length); +    memcpy(copy, s, length); +    copy[length] = '\0'; +    return copy; +} + + +/*   * Locate a test file.  Given the partial path to a file, look under BUILD and   * then SOURCE for the file and return the full path to the file.  Returns   * NULL if the file doesn't exist.  A non-NULL return should be freed with @@ -458,9 +561,7 @@ test_file_path(const char *file)          if (base == NULL)              continue;          length = strlen(base) + 1 + strlen(file) + 1; -        path = malloc(length); -        if (path == NULL) -            sysbail("cannot allocate memory"); +        path = bmalloc(length);          sprintf(path, "%s/%s", base, file);          if (access(path, R_OK) == 0)              break; @@ -482,3 +583,47 @@ test_file_path_free(char *path)      if (path != NULL)          free(path);  } + + +/* + * Create a temporary directory, tmp, under BUILD if set and the current + * directory if it does not.  Returns the path to the temporary directory in + * newly allocated memory, and calls bail on any failure.  The return value + * should be freed with test_tmpdir_free. + * + * This function uses sprintf because it attempts to be independent of all + * other portability layers.  The use immediately after a memory allocation + * should be safe without using snprintf or strlcpy/strlcat. + */ +char * +test_tmpdir(void) +{ +    const char *build; +    char *path = NULL; +    size_t length; + +    build = getenv("BUILD"); +    if (build == NULL) +        build = "."; +    length = strlen(build) + strlen("/tmp") + 1; +    path = bmalloc(length); +    sprintf(path, "%s/tmp", build); +    if (access(path, X_OK) < 0) +        if (mkdir(path, 0777) < 0) +            sysbail("error creating temporary directory %s", path); +    return path; +} + + +/* + * Free a path returned from test_tmpdir() and attempt to remove the + * directory.  If we can't delete the directory, don't worry; something else + * that hasn't yet cleaned up may still be using it. + */ +void +test_tmpdir_free(char *path) +{ +    rmdir(path); +    if (path != NULL) +        free(path); +} diff --git a/tests/tap/basic.h b/tests/tap/basic.h index 9602db4..fa4adaf 100644 --- a/tests/tap/basic.h +++ b/tests/tap/basic.h @@ -1,47 +1,38 @@  /*   * Basic utility routines for the TAP protocol.   * - * Copyright 2009, 2010 Russ Allbery <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 + * This file is part of C TAP Harness.  The current version plus supporting + * documentation is at <http://www.eyrie.org/~eagle/software/c-tap-harness/>.   * - * See LICENSE for licensing terms. + * Copyright 2009, 2010, 2011, 2012 Russ Allbery <rra@stanford.edu> + * Copyright 2001, 2002, 2004, 2005, 2006, 2007, 2008, 2011, 2012 + *     The Board of Trustees of the Leland Stanford Junior University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE.   */  #ifndef TAP_BASIC_H  #define TAP_BASIC_H 1 +#include <tests/tap/macros.h>  #include <stdarg.h>             /* va_list */ -#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 +#include <sys/types.h>          /* size_t */  /*   * Used for iterating through arrays.  ARRAY_SIZE returns the number of @@ -93,8 +84,6 @@ void skip_block(unsigned long count, const char *reason, ...)  /* Check an expected value against a seen value. */  void is_int(long wanted, long seen, const char *format, ...)      __attribute__((__format__(printf, 3, 4))); -void is_double(double wanted, double seen, const char *format, ...) -    __attribute__((__format__(printf, 3, 4)));  void is_string(const char *wanted, const char *seen, const char *format, ...)      __attribute__((__format__(printf, 3, 4)));  void is_hex(unsigned long wanted, unsigned long seen, const char *format, ...) @@ -112,6 +101,18 @@ void diag(const char *format, ...)  void sysdiag(const char *format, ...)      __attribute__((__nonnull__, __format__(printf, 1, 2))); +/* Allocate memory, reporting a fatal error with bail on failure. */ +void *bcalloc(size_t, size_t) +    __attribute__((__alloc_size__(1, 2), __malloc__)); +void *bmalloc(size_t) +    __attribute__((__alloc_size__(1), __malloc__)); +void *brealloc(void *, size_t) +    __attribute__((__alloc_size__(2), __malloc__)); +char *bstrdup(const char *) +    __attribute__((__malloc__, __nonnull__)); +char *bstrndup(const char *, size_t) +    __attribute__((__malloc__, __nonnull__)); +  /*   * Find a test file under BUILD or SOURCE, returning the full path.  The   * returned path should be freed with test_file_path_free(). @@ -120,6 +121,14 @@ char *test_file_path(const char *file)      __attribute__((__malloc__, __nonnull__));  void test_file_path_free(char *path); +/* + * Create a temporary directory relative to BUILD and return the path.  The + * returned path should be freed with test_tmpdir_free. + */ +char *test_tmpdir(void) +    __attribute__((__malloc__)); +void test_tmpdir_free(char *path); +  END_DECLS  #endif /* TAP_BASIC_H */ diff --git a/tests/tap/kerberos.c b/tests/tap/kerberos.c index a17d980..474cf4f 100644 --- a/tests/tap/kerberos.c +++ b/tests/tap/kerberos.c @@ -1,47 +1,90 @@  /*   * Utility functions for tests that use Kerberos.   * - * Currently only provides kerberos_setup(), which assumes a particular set of - * data files in either the SOURCE or BUILD directories and, using those, - * obtains Kerberos credentials, sets up a ticket cache, and sets the - * environment variable pointing to the Kerberos keytab to use for testing. + * The core function is kerberos_setup, which loads Kerberos test + * configuration and returns a struct of information.  It also supports + * obtaining initial tickets from the configured keytab and setting up + * KRB5CCNAME and KRB5_KTNAME if a Kerberos keytab is present.  Also included + * are utility functions for setting up a krb5.conf file and reporting + * Kerberos errors or warnings during testing.   * - * Copyright 2006, 2007, 2009, 2010 - *     Board of Trustees, Leland Stanford Jr. University + * Some of the functionality here is only available if the Kerberos libraries + * are available.   * - * See LICENSE for licensing terms. + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>. + * + * Written by Russ Allbery <rra@stanford.edu> + * Copyright 2006, 2007, 2009, 2010, 2011, 2012 + *     The Board of Trustees of the Leland Stanford Junior University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE.   */  #include <config.h> -#include <portable/krb5.h> +#ifdef HAVE_KERBEROS +# include <portable/krb5.h> +#endif  #include <portable/system.h> +#include <sys/stat.h> +  #include <tests/tap/basic.h>  #include <tests/tap/kerberos.h> -#include <util/concat.h> -#include <util/xmalloc.h> +#include <tests/tap/process.h> +#include <tests/tap/string.h> + +/* + * Disable the requirement that format strings be literals, since it's easier + * to handle the possible patterns for kinit commands as an array. + */ +#pragma GCC diagnostic ignored "-Wformat-nonliteral"  /* - * Obtain Kerberos tickets for the principal specified in test.principal using - * the keytab specified in test.keytab, both of which are presumed to be in - * tests/data in either the build or the source tree. - * - * Returns the contents of test.principal in newly allocated memory or NULL if - * Kerberos tests are apparently not configured.  If Kerberos tests are - * configured but something else fails, calls bail(). + * These variables hold the allocated configuration struct, the environment to + * point to a different Kerberos ticket cache, keytab, and configuration file, + * and the temporary directories used.  We store them so that we can free them + * on exit for cleaner valgrind output, making it easier to find real memory + * leaks in the tested programs. + */ +static struct kerberos_config *config = NULL; +static char *krb5ccname = NULL; +static char *krb5_ktname = NULL; +static char *krb5_config = NULL; +static char *tmpdir_ticket = NULL; +static char *tmpdir_conf = NULL; + + +/* + * Obtain Kerberos tickets and fill in the principal config entry.   * - * The error handling here is not great.  We should have a bail_krb5 that uses - * the same logic as messages-krb5.c, which hasn't yet been imported into - * rra-c-util. + * There are two implementations of this function, one if we have native + * Kerberos libraries available and one if we don't.  Uses keytab to obtain + * credentials, and fills in the cache member of the provided config struct.   */ -char * -kerberos_setup(void) +#ifdef HAVE_KERBEROS + +static void +kerberos_kinit(void)  { -    char *path, *krbtgt; -    const char *build, *realm; -    FILE *file; -    char principal[BUFSIZ]; +    char *name, *krbtgt;      krb5_error_code code;      krb5_context ctx;      krb5_ccache ccache; @@ -49,89 +92,397 @@ kerberos_setup(void)      krb5_keytab keytab;      krb5_get_init_creds_opt *opts;      krb5_creds creds; +    const char *realm; -    /* Read the principal name and find the keytab file. */ -    path = test_file_path("data/test.principal"); -    if (path == NULL) -        return NULL; -    file = fopen(path, "r"); -    if (file == NULL) { -        free(path); -        return NULL; -    } -    if (fgets(principal, sizeof(principal), file) == NULL) { -        fclose(file); -        bail("cannot read %s", path); -    } -    fclose(file); -    if (principal[strlen(principal) - 1] != '\n') -        bail("no newline in %s", path); -    free(path); -    principal[strlen(principal) - 1] = '\0'; -    path = test_file_path("data/test.keytab"); -    if (path == NULL) -        return NULL; - -    /* Set the KRB5CCNAME and KRB5_KTNAME environment variables. */ -    build = getenv("BUILD"); -    if (build == NULL) -        build = "."; -    putenv(concat("KRB5CCNAME=", build, "/data/test.cache", (char *) 0)); -    putenv(concat("KRB5_KTNAME=", path, (char *) 0)); - -    /* Now do the Kerberos initialization. */ +    /* +     * Determine the principal corresponding to that keytab.  We copy the +     * memory to ensure that it's allocated in the right memory domain on +     * systems where that may matter (like Windows). +     */      code = krb5_init_context(&ctx);      if (code != 0) -        bail("error initializing Kerberos"); +        bail_krb5(ctx, code, "error initializing Kerberos"); +    kprinc = kerberos_keytab_principal(ctx, config->keytab); +    code = krb5_unparse_name(ctx, kprinc, &name); +    if (code != 0) +        bail_krb5(ctx, code, "error unparsing name"); +    krb5_free_principal(ctx, kprinc); +    config->principal = bstrdup(name); +    krb5_free_unparsed_name(ctx, name); + +    /* Now do the Kerberos initialization. */      code = krb5_cc_default(ctx, &ccache);      if (code != 0) -        bail("error setting ticket cache"); -    code = krb5_parse_name(ctx, principal, &kprinc); +        bail_krb5(ctx, code, "error setting ticket cache"); +    code = krb5_parse_name(ctx, config->principal, &kprinc);      if (code != 0) -        bail("error parsing principal %s", principal); +        bail_krb5(ctx, code, "error parsing principal %s", config->principal);      realm = krb5_principal_get_realm(ctx, kprinc); -    krbtgt = concat("krbtgt/", realm, "@", realm, (char *) 0); -    code = krb5_kt_resolve(ctx, path, &keytab); +    basprintf(&krbtgt, "krbtgt/%s@%s", realm, realm); +    code = krb5_kt_resolve(ctx, config->keytab, &keytab);      if (code != 0) -        bail("cannot open keytab %s", path); +        bail_krb5(ctx, code, "cannot open keytab %s", config->keytab);      code = krb5_get_init_creds_opt_alloc(ctx, &opts);      if (code != 0) -        bail("cannot allocate credential options"); +        bail_krb5(ctx, code, "cannot allocate credential options");      krb5_get_init_creds_opt_set_default_flags(ctx, NULL, realm, opts);      krb5_get_init_creds_opt_set_forwardable(opts, 0);      krb5_get_init_creds_opt_set_proxiable(opts, 0);      code = krb5_get_init_creds_keytab(ctx, &creds, kprinc, keytab, 0, krbtgt,                                        opts);      if (code != 0) -        bail("cannot get Kerberos tickets"); +        bail_krb5(ctx, code, "cannot get Kerberos tickets");      code = krb5_cc_initialize(ctx, ccache, kprinc);      if (code != 0) -        bail("error initializing ticket cache"); +        bail_krb5(ctx, code, "error initializing ticket cache");      code = krb5_cc_store_cred(ctx, ccache, &creds);      if (code != 0) -        bail("error storing credentials"); +        bail_krb5(ctx, code, "error storing credentials");      krb5_cc_close(ctx, ccache);      krb5_free_cred_contents(ctx, &creds);      krb5_kt_close(ctx, keytab);      krb5_free_principal(ctx, kprinc); +    krb5_get_init_creds_opt_free(ctx, opts);      krb5_free_context(ctx);      free(krbtgt); -    free(path); +} -    return xstrdup(principal); +#else /* !HAVE_KERBEROS */ + +static void +kerberos_kinit(void) +{ +    static const char * const format[] = { +        "kinit --no-afslog -k -t %s %s >/dev/null 2>&1 </dev/null", +        "kinit -k -t %s %s >/dev/null 2>&1 </dev/null", +        "kinit -t %s %s >/dev/null 2>&1 </dev/null", +        "kinit -k -K %s %s >/dev/null 2>&1 </dev/null" +    }; +    FILE *file; +    char *path; +    char principal[BUFSIZ], *command; +    size_t i; +    int status; + +    /* Read the principal corresponding to the keytab. */ +    path = test_file_path("config/principal"); +    if (path == NULL) { +        test_file_path_free(config->keytab); +        config->keytab = NULL; +        return; +    } +    file = fopen(path, "r"); +    if (file == NULL) { +        test_file_path_free(path); +        return; +    } +    test_file_path_free(path); +    if (fgets(principal, sizeof(principal), file) == NULL) +        bail("cannot read %s", path); +    fclose(file); +    if (principal[strlen(principal) - 1] != '\n') +        bail("no newline in %s", path); +    principal[strlen(principal) - 1] = '\0'; +    config->principal = bstrdup(principal); + +    /* Now do the Kerberos initialization. */ +    for (i = 0; i < ARRAY_SIZE(format); i++) { +        basprintf(&command, format[i], config->keytab, principal); +        status = system(command); +        free(command); +        if (status != -1 && WEXITSTATUS(status) == 0) +            break; +    } +    if (status == -1 || WEXITSTATUS(status) != 0) +        bail("cannot get Kerberos tickets");  } +#endif /* !HAVE_KERBEROS */ +  /* - * Clean up at the end of a test.  Currently, all this does is remove the - * ticket cache. + * Clean up at the end of a test.  This removes the ticket cache and resets + * and frees the memory allocated for the environment variables so that + * valgrind output on test suites is cleaner.   */  void  kerberos_cleanup(void)  {      char *path; -    path = concatpath(getenv("BUILD"), "data/test.cache"); -    unlink(path); -    free(path); +    if (tmpdir_ticket != NULL) { +        basprintf(&path, "%s/krb5cc_test", tmpdir_ticket); +        unlink(path); +        free(path); +        test_tmpdir_free(tmpdir_ticket); +        tmpdir_ticket = NULL; +    } +    if (config != NULL) { +        if (config->keytab != NULL) { +            test_file_path_free(config->keytab); +            free(config->principal); +            free(config->cache); +        } +        if (config->userprinc != NULL) { +            free(config->userprinc); +            free(config->username); +            free(config->password); +        } +        free(config); +        config = NULL; +    } +    if (krb5ccname != NULL) { +        putenv((char *) "KRB5CCNAME="); +        free(krb5ccname); +        krb5ccname = NULL; +    } +    if (krb5_ktname != NULL) { +        putenv((char *) "KRB5_KTNAME="); +        free(krb5_ktname); +        krb5_ktname = NULL; +    } +} + + +/* + * Obtain Kerberos tickets for the principal specified in config/principal + * using the keytab specified in config/keytab, both of which are presumed to + * be in tests in either the build or the source tree.  Also sets KRB5_KTNAME + * and KRB5CCNAME. + * + * Returns the contents of config/principal in newly allocated memory or NULL + * if Kerberos tests are apparently not configured.  If Kerberos tests are + * configured but something else fails, calls bail. + */ +struct kerberos_config * +kerberos_setup(enum kerberos_needs needs) +{ +    char *path; +    char buffer[BUFSIZ]; +    FILE *file = NULL; + +    /* If we were called before, clean up after the previous run. */ +    if (config != NULL) +        kerberos_cleanup(); +    config = bcalloc(1, sizeof(struct kerberos_config)); + +    /* +     * If we have a config/keytab file, set the KRB5CCNAME and KRB5_KTNAME +     * environment variables and obtain initial tickets. +     */ +    config->keytab = test_file_path("config/keytab"); +    if (config->keytab == NULL) { +        if (needs == TAP_KRB_NEEDS_KEYTAB || needs == TAP_KRB_NEEDS_BOTH) +            skip_all("Kerberos tests not configured"); +    } else { +        tmpdir_ticket = test_tmpdir(); +        basprintf(&config->cache, "%s/krb5cc_test", tmpdir_ticket); +        basprintf(&krb5ccname, "KRB5CCNAME=%s/krb5cc_test", tmpdir_ticket); +        basprintf(&krb5_ktname, "KRB5_KTNAME=%s", config->keytab); +        putenv(krb5ccname); +        putenv(krb5_ktname); +        kerberos_kinit(); +    } + +    /* +     * If we have a config/password file, read it and fill out the relevant +     * members of our config struct. +     */ +    path = test_file_path("config/password"); +    if (path != NULL) +        file = fopen(path, "r"); +    if (file == NULL) { +        if (needs == TAP_KRB_NEEDS_PASSWORD || needs == TAP_KRB_NEEDS_BOTH) +            skip_all("Kerberos tests not configured"); +    } else { +        if (fgets(buffer, sizeof(buffer), file) == NULL) +            bail("cannot read %s", path); +        if (buffer[strlen(buffer) - 1] != '\n') +            bail("no newline in %s", path); +        buffer[strlen(buffer) - 1] = '\0'; +        config->userprinc = bstrdup(buffer); +        if (fgets(buffer, sizeof(buffer), file) == NULL) +            bail("cannot read password from %s", path); +        fclose(file); +        if (buffer[strlen(buffer) - 1] != '\n') +            bail("password too long in %s", path); +        buffer[strlen(buffer) - 1] = '\0'; +        config->password = bstrdup(buffer); + +        /* +         * Strip the realm from the principal and set realm and username. +         * This is not strictly correct; it doesn't cope with escaped @-signs +         * or enterprise names. +         */ +        config->username = bstrdup(config->userprinc); +        config->realm = strchr(config->username, '@'); +        if (config->realm == NULL) +            bail("test principal has no realm"); +        *config->realm = '\0'; +        config->realm++; +    } +    if (path != NULL) +        test_file_path_free(path); + +    /* +     * Register the cleanup function as an atexit handler so that the caller +     * doesn't have to worry about cleanup. +     */ +    if (atexit(kerberos_cleanup) != 0) +        sysdiag("cannot register cleanup function"); + +    /* Return the configuration. */ +    return config; +} + + +/* + * Clean up the krb5.conf file generated by kerberos_generate_conf and free + * the memory used to set the environment variable.  This doesn't fail if the + * file and variable are already gone, allowing it to be harmlessly run + * multiple times. + * + * Normally called via an atexit handler. + */ +void +kerberos_cleanup_conf(void) +{ +    char *path; + +    if (tmpdir_conf != NULL) { +        basprintf(&path, "%s/krb5.conf", tmpdir_conf); +        unlink(path); +        free(path); +        test_tmpdir_free(tmpdir_conf); +        tmpdir_conf = NULL; +    } +    putenv((char *) "KRB5_CONFIG="); +    if (krb5_config != NULL) { +        free(krb5_config); +        krb5_config = NULL; +    }  } + + +/* + * Generate a krb5.conf file for testing and set KRB5_CONFIG to point to it. + * The [appdefaults] section will be stripped out and the default realm will + * be set to the realm specified, if not NULL.  This will use config/krb5.conf + * in preference, so users can configure the tests by creating that file if + * the system file isn't suitable. + * + * Depends on data/generate-krb5-conf being present in the test suite. + */ +void +kerberos_generate_conf(const char *realm) +{ +    char *path; +    const char *argv[3]; + +    if (tmpdir_conf != NULL) +        kerberos_cleanup_conf(); +    path = test_file_path("data/generate-krb5-conf"); +    if (path == NULL) +        bail("cannot find generate-krb5-conf"); +    argv[0] = path; +    argv[1] = realm; +    argv[2] = NULL; +    run_setup(argv); +    test_file_path_free(path); +    tmpdir_conf = test_tmpdir(); +    basprintf(&krb5_config, "KRB5_CONFIG=%s/krb5.conf", tmpdir_conf); +    putenv(krb5_config); +    if (atexit(kerberos_cleanup_conf) != 0) +        sysdiag("cannot register cleanup function"); +} + + +/* + * The remaining functions in this file are only available if Kerberos + * libraries are available. + */ +#ifdef HAVE_KERBEROS + + +/* + * Report a Kerberos error and bail out. + */ +void +bail_krb5(krb5_context ctx, krb5_error_code code, const char *format, ...) +{ +    const char *k5_msg = NULL; +    char *message; +    va_list args; + +    if (ctx != NULL) +        k5_msg = krb5_get_error_message(ctx, code); +    va_start(args, format); +    bvasprintf(&message, format, args); +    va_end(args); +    if (k5_msg == NULL) +        bail("%s", message); +    else +        bail("%s: %s", message, k5_msg); +} + + +/* + * Report a Kerberos error as a diagnostic to stderr. + */ +void +diag_krb5(krb5_context ctx, krb5_error_code code, const char *format, ...) +{ +    const char *k5_msg = NULL; +    char *message; +    va_list args; + +    if (ctx != NULL) +        k5_msg = krb5_get_error_message(ctx, code); +    va_start(args, format); +    bvasprintf(&message, format, args); +    va_end(args); +    if (k5_msg == NULL) +        diag("%s", message); +    else +        diag("%s: %s", message, k5_msg); +    free(message); +    if (k5_msg != NULL) +        krb5_free_error_message(ctx, k5_msg); +} + + +/* + * Find the principal of the first entry of a keytab and return it.  The + * caller is responsible for freeing the result with krb5_free_principal. + * Exit on error. + */ +krb5_principal +kerberos_keytab_principal(krb5_context ctx, const char *path) +{ +    krb5_keytab keytab; +    krb5_kt_cursor cursor; +    krb5_keytab_entry entry; +    krb5_principal princ; +    krb5_error_code status; + +    status = krb5_kt_resolve(ctx, path, &keytab); +    if (status != 0) +        bail_krb5(ctx, status, "error opening %s", path); +    status = krb5_kt_start_seq_get(ctx, keytab, &cursor); +    if (status != 0) +        bail_krb5(ctx, status, "error reading %s", path); +    status = krb5_kt_next_entry(ctx, keytab, &entry, &cursor); +    if (status == 0) { +        status = krb5_copy_principal(ctx, entry.principal, &princ); +        if (status != 0) +            bail_krb5(ctx, status, "error copying principal from %s", path); +        krb5_kt_free_entry(ctx, &entry); +    } +    if (status != 0) +        bail("no principal found in keytab file %s", path); +    krb5_kt_end_seq_get(ctx, keytab, &cursor); +    krb5_kt_close(ctx, keytab); +    return princ; +} + +#endif /* HAVE_KERBEROS */ diff --git a/tests/tap/kerberos.h b/tests/tap/kerberos.h index 1c64f70..31b6343 100644 --- a/tests/tap/kerberos.h +++ b/tests/tap/kerberos.h @@ -1,32 +1,125 @@  /*   * Utility functions for tests that use Kerberos.   * - * Copyright 2006, 2007, 2009 - *     Board of Trustees, Leland Stanford Jr. University + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.   * - * See LICENSE for licensing terms. + * Written by Russ Allbery <rra@stanford.edu> + * Copyright 2006, 2007, 2009, 2011, 2012 + *     The Board of Trustees of the Leland Stanford Junior University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE.   */  #ifndef TAP_KERBEROS_H  #define TAP_KERBEROS_H 1  #include <config.h> -#include <portable/macros.h> +#include <tests/tap/macros.h> + +#ifdef HAVE_KERBEROS +# include <portable/krb5.h> +#endif + +/* Holds the information parsed from the Kerberos test configuration. */ +struct kerberos_config { +    char *keytab;               /* Path to the keytab. */ +    char *principal;            /* Principal whose keys are in the keytab. */ +    char *cache;                /* Path to the Kerberos ticket cache. */ +    char *userprinc;            /* The fully-qualified principal. */ +    char *username;             /* The local (non-realm) part of principal. */ +    char *realm;                /* The realm part of the principal. */ +    char *password;             /* The password. */ +}; + +/* + * Whether to skip all tests (by calling skip_all) in kerberos_setup if + * certain configuration information isn't available. + */ +enum kerberos_needs { +    TAP_KRB_NEEDS_NONE, +    TAP_KRB_NEEDS_KEYTAB, +    TAP_KRB_NEEDS_PASSWORD, +    TAP_KRB_NEEDS_BOTH +};  BEGIN_DECLS  /* - * Set up Kerberos, returning the test principal in newly allocated memory if - * we were successful.  If there is no principal in tests/data/test.principal - * or no keytab in tests/data/test.keytab, return NULL.  Otherwise, on - * failure, calls bail(). + * Set up Kerberos, returning the test configuration information.  This + * obtains Kerberos tickets from config/keytab, if one is present, and stores + * them in a Kerberos ticket cache, sets KRB5_KTNAME and KRB5CCNAME.  It also + * loads the principal and password from config/password, if it exists, and + * stores the principal, password, username, and realm in the returned struct. + * + * If there is no config/keytab file, KRB5_KTNAME and KRB5CCNAME won't be set + * and the keytab field will be NULL.  If there is no config/password file, + * the principal field will be NULL.  If the files exist but loading them + * fails, or authentication fails, kerberos_setup calls bail. + * + * kerberos_cleanup will be set up to run from an atexit handler.  This means + * that any child processes that should not remove the Kerberos ticket cache + * should call _exit instead of exit.  The principal will be automatically + * freed when kerberos_cleanup is called or if kerberos_setup is called again. + * The caller doesn't need to worry about it.   */ -char *kerberos_setup(void) +struct kerberos_config *kerberos_setup(enum kerberos_needs)      __attribute__((__malloc__)); - -/* Clean up at the end of a test. */  void kerberos_cleanup(void); +/* + * Generate a krb5.conf file for testing and set KRB5_CONFIG to point to it. + * The [appdefaults] section will be stripped out and the default realm will + * be set to the realm specified, if not NULL.  This will use config/krb5.conf + * in preference, so users can configure the tests by creating that file if + * the system file isn't suitable. + * + * Depends on data/generate-krb5-conf being present in the test suite. + * + * kerberos_cleanup_conf will clean up after this function, but usually + * doesn't need to be called directly since it's registered as an atexit + * handler. + */ +void kerberos_generate_conf(const char *realm); +void kerberos_cleanup_conf(void); + +/* Thes interfaces are only available with native Kerberos support. */ +#ifdef HAVE_KERBEROS + +/* Bail out with an error, appending the Kerberos error message. */ +void bail_krb5(krb5_context, krb5_error_code, const char *format, ...) +    __attribute__((__noreturn__, __nonnull__, __format__(printf, 3, 4))); + +/* Report a diagnostic with Kerberos error to stderr prefixed with #. */ +void diag_krb5(krb5_context, krb5_error_code, const char *format, ...) +    __attribute__((__nonnull__, __format__(printf, 3, 4))); + +/* + * Given a Kerberos context and the path to a keytab, retrieve the principal + * for the first entry in the keytab and return it.  Calls bail on failure. + * The returned principal should be freed with krb5_free_principal. + */ +krb5_principal kerberos_keytab_principal(krb5_context, const char *path) +    __attribute__((__nonnull__)); + +#endif /* HAVE_KERBEROS */ +  END_DECLS  #endif /* !TAP_MESSAGES_H */ diff --git a/tests/tap/kerberos.sh b/tests/tap/kerberos.sh index 904cae5..d2f174d 100644 --- a/tests/tap/kerberos.sh +++ b/tests/tap/kerberos.sh @@ -1,30 +1,61 @@  # Shell function library to initialize Kerberos credentials  # +# Note that while many of the functions in this library could benefit from +# using "local" to avoid possibly hammering global variables, Solaris /bin/sh +# doesn't support local and this library aspires to be portable to Solaris +# Bourne shell.  Instead, all private variables are prefixed with "tap_". +# +# The canonical version of this file is maintained in the rra-c-util package, +# which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>. +#  # Written by Russ Allbery <rra@stanford.edu> -# Copyright 2009, 2010 Board of Trustees, Leland Stanford Jr. University +# Copyright 2009, 2010, 2011, 2012 +#     The Board of Trustees of the Leland Stanford Junior University +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software.  # -# See LICENSE for licensing terms. +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. + +# We use test_tmpdir. +. "${SOURCE}/tap/libtap.sh"  # Set up Kerberos, including the ticket cache environment variable.  Bail out  # if not successful, return 0 if successful, and return 1 if Kerberos is not  # configured.  Sets the global principal variable to the principal to use.  kerberos_setup () { -    local keytab -    keytab=`test_file_path data/test.keytab` -    principal=`test_file_path data/test.principal` +    tap_keytab=`test_file_path config/keytab` +    principal=`test_file_path config/principal`      principal=`cat "$principal" 2>/dev/null` -    if [ -z "$keytab" ] || [ -z "$principal" ] ; then +    if [ -z "$tap_keytab" ] || [ -z "$principal" ] ; then          return 1      fi -    KRB5CCNAME="$BUILD/data/test.cache"; export KRB5CCNAME -    kinit -k -t "$keytab" "$principal" >/dev/null </dev/null +    KRB5CCNAME=`test_tmpdir`/krb5cc_test; export KRB5CCNAME +    kinit --no-afslog -k -t "$tap_keytab" "$principal" >/dev/null </dev/null      status=$?      if [ $status != 0 ] ; then -        kinit -t "$keytab" "$principal" >/dev/null </dev/null +        kinit -k -t "$tap_keytab" "$principal" >/dev/null </dev/null +        status=$? +    fi +    if [ $status != 0 ] ; then +        kinit -t "$tap_keytab" "$principal" >/dev/null </dev/null          status=$?      fi      if [ $status != 0 ] ; then -        kinit -k -K "$keytab" "$principal" >/dev/null </dev/null +        kinit -k -K "$tap_keytab" "$principal" >/dev/null </dev/null          status=$?      fi      if [ $status != 0 ] ; then @@ -35,7 +66,8 @@ kerberos_setup () {  # Clean up at the end of a test.  Currently only removes the ticket cache.  kerberos_cleanup () { -    rm -f "$BUILD/data/test.cache" +    tap_tmp=`test_tmpdir` +    rm -f "$tap_tmp"/krb5cc_test  }  # List the contents of a keytab with enctypes and keys.  This adjusts for the @@ -44,11 +76,13 @@ kerberos_cleanup () {  # may just hang.  Takes the keytab to list and the file into which to save the  # output, and strips off the header containing the file name.  ktutil_list () { -    if klist -keK "$1" > ktutil-tmp 2>/dev/null ; then +    tap_tmp=`test_tmpdir` +    if klist -keK "$1" > "$tap_tmp"/ktutil-tmp 2>/dev/null ; then          :      else -        ktutil -k "$1" list --keys > ktutil-tmp < /dev/null 2>/dev/null +        ktutil -k "$1" list --keys > "$tap_tmp"/ktutil-tmp </dev/null \ +            2>/dev/null      fi -    sed -e '/Keytab name:/d' -e "/^[^ ]*:/d" ktutil-tmp > "$2" -    rm -f ktutil-tmp +    sed -e '/Keytab name:/d' -e "/^[^ ]*:/d" "$tap_tmp"/ktutil-tmp > "$2" +    rm -f "$tap_tmp"/ktutil-tmp  } diff --git a/tests/tap/libtap.sh b/tests/tap/libtap.sh index a9b46d4..f9347d8 100644 --- a/tests/tap/libtap.sh +++ b/tests/tap/libtap.sh @@ -1,10 +1,36 @@  # Shell function library for test cases.  # +# Note that while many of the functions in this library could benefit from +# using "local" to avoid possibly hammering global variables, Solaris /bin/sh +# doesn't support local and this library aspires to be portable to Solaris +# Bourne shell.  Instead, all private variables are prefixed with "tap_". +# +# This file provides a TAP-compatible shell function library useful for +# writing test cases.  It is part of C TAP Harness, which can be found at +# <http://www.eyrie.org/~eagle/software/c-tap-harness/>. +#  # Written by Russ Allbery <rra@stanford.edu> -# Copyright 2009, 2010 Russ Allbery <rra@stanford.edu> -# Copyright 2006, 2007, 2008 Board of Trustees, Leland Stanford Jr. University +# Copyright 2009, 2010, 2011, 2012 Russ Allbery <rra@stanford.edu> +# Copyright 2006, 2007, 2008 +#     The Board of Trustees of the Leland Stanford Junior University +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions:  # -# See LICENSE for licensing terms. +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE.  # Print out the number of test cases we expect to run.  plan () { @@ -25,33 +51,35 @@ plan_lazy () {  # Report the test status on exit.  finish () { -    local highest looks -    highest=`expr "$count" - 1` +    tap_highest=`expr "$count" - 1`      if [ "$planned" = 0 ] ; then -        echo "1..$highest" -        planned="$highest" +        echo "1..$tap_highest" +        planned="$tap_highest"      fi -    looks='# Looks like you' +    tap_looks='# Looks like you'      if [ "$planned" -gt 0 ] ; then -        if [ "$planned" -gt "$highest" ] ; then +        if [ "$planned" -gt "$tap_highest" ] ; then              if [ "$planned" -gt 1 ] ; then -                echo "$looks planned $planned tests but only ran $highest" +                echo "$tap_looks planned $planned tests but only ran" \ +                    "$tap_highest"              else -                echo "$looks planned $planned test but only ran $highest" +                echo "$tap_looks planned $planned test but only ran" \ +                    "$tap_highest"              fi -        elif [ "$planned" -lt "$highest" ] ; then -            local extra -            extra=`expr "$highest" - "$planned"` +        elif [ "$planned" -lt "$tap_highest" ] ; then +            tap_extra=`expr "$tap_highest" - "$planned"`              if [ "$planned" -gt 1 ] ; then -                echo "$looks planned $planned tests but ran $extra extra" +                echo "$tap_looks planned $planned tests but ran" \ +                    "$tap_extra extra"              else -                echo "$looks planned $planned test but ran $extra extra" +                echo "$tap_looks planned $planned test but ran" \ +                    "$tap_extra extra"              fi          elif [ "$failed" -gt 0 ] ; then              if [ "$failed" -gt 1 ] ; then -                echo "$looks failed $failed tests of $planned" +                echo "$tap_looks failed $failed tests of $planned"              else -                echo "$looks failed $failed test of $planned" +                echo "$tap_looks failed $failed test of $planned"              fi          elif [ "$planned" -gt 1 ] ; then              echo "# All $planned tests successful or skipped" @@ -63,10 +91,9 @@ finish () {  # Skip the entire test suite.  Should be run instead of plan.  skip_all () { -    local desc -    desc="$1" -    if [ -n "$desc" ] ; then -        echo "1..0 # skip $desc" +    tap_desc="$1" +    if [ -n "$tap_desc" ] ; then +        echo "1..0 # skip $tap_desc"      else          echo "1..0 # skip"      fi @@ -77,16 +104,15 @@ skip_all () {  # command is successful, false otherwise.  The count starts at 1 and is  # updated each time ok is printed.  ok () { -    local desc -    desc="$1" -    if [ -n "$desc" ] ; then -        desc=" - $desc" +    tap_desc="$1" +    if [ -n "$tap_desc" ] ; then +        tap_desc=" - $tap_desc"      fi      shift      if "$@" ; then -        echo ok $count$desc +        echo ok "$count$tap_desc"      else -        echo not ok $count$desc +        echo not ok "$count$tap_desc"          failed=`expr $failed + 1`      fi      count=`expr $count + 1` @@ -101,58 +127,80 @@ skip () {  # Report the same status on a whole set of tests.  Takes the count of tests,  # the description, and then the command to run to determine the status.  ok_block () { -    local end i desc -    i=$count -    end=`expr $count + $1` -    shift -    desc="$1" +    tap_i=$count +    tap_end=`expr $count + $1`      shift -    while [ "$i" -lt "$end" ] ; do -        ok "$desc" "$@" -        i=`expr $i + 1` +    while [ "$tap_i" -lt "$tap_end" ] ; do +        ok "$@" +        tap_i=`expr $tap_i + 1`      done  }  # Skip a whole set of tests.  Takes the count and then the reason for skipping  # the test.  skip_block () { -    local i end -    i=$count -    end=`expr $count + $1` +    tap_i=$count +    tap_end=`expr $count + $1`      shift -    while [ "$i" -lt "$end" ] ; do +    while [ "$tap_i" -lt "$tap_end" ] ; do          skip "$@" -        i=`expr $i + 1` +        tap_i=`expr $tap_i + 1`      done  } +# Portable variant of printf '%s\n' "$*".  In the majority of cases, this +# function is slower than printf, because the latter is often implemented +# as a builtin command.  The value of the variable IFS is ignored. +# +# This macro must not be called via backticks inside double quotes, since this +# will result in bizarre escaping behavior and lots of extra backslashes on +# Solaris. +puts () { +    cat << EOH +$@ +EOH +} +  # Run a program expected to succeed, and print ok if it does and produces the  # correct output.  Takes the description, expected exit status, the expected -# output, the command to run, and then any arguments for that command.  Strip -# a colon and everything after it off the output if the expected status is -# non-zero, since this is probably a system-specific error message. +# output, the command to run, and then any arguments for that command. +# Standard output and standard error are combined when analyzing the output of +# the command. +# +# If the command may contain system-specific error messages in its output, +# add strip_colon_error before the command to post-process its output.  ok_program () { -    local desc w_status w_output output status -    desc="$1" +    tap_desc="$1"      shift -    w_status="$1" +    tap_w_status="$1"      shift -    w_output="$1" +    tap_w_output="$1"      shift -    output=`"$@" 2>&1` -    status=$? -    if [ "$w_status" -ne 0 ] ; then -        output=`echo "$output" | sed 's/^\([^:]* [^:]*\):.*/\1/'` -    fi -    if [ $status = $w_status ] && [ x"$output" = x"$w_output" ] ; then -        ok "$desc" true +    tap_output=`"$@" 2>&1` +    tap_status=$? +    if [ $tap_status = $tap_w_status ] \ +        && [ x"$tap_output" = x"$tap_w_output" ] ; then +        ok "$tap_desc" true      else -        echo "#  saw: ($status) $output" -        echo "#  not: ($w_status) $w_output" -        ok "$desc" false +        echo "#  saw: ($tap_status) $tap_output" +        echo "#  not: ($tap_w_status) $tap_w_output" +        ok "$tap_desc" false      fi  } +# Strip a colon and everything after it off the output of a command, as long +# as that colon comes after at least one whitespace character.  (This is done +# to avoid stripping the name of the program from the start of an error +# message.)  This is used to remove system-specific error messages (coming +# from strerror, for example). +strip_colon_error() { +    tap_output=`"$@" 2>&1` +    tap_status=$? +    tap_output=`puts "$tap_output" | sed 's/^\([^ ]* [^:]*\):.*/\1/'` +    puts "$tap_output" +    return $tap_status +} +  # Bail out with an error message.  bail () {      echo 'Bail out!' "$@" @@ -167,12 +215,32 @@ diag () {  # Search for the given file first in $BUILD and then in $SOURCE and echo the  # path where the file was found, or the empty string if the file wasn't  # found. +# +# This macro uses puts, so don't run it using backticks inside double quotes +# or bizarre quoting behavior will happen with Solaris sh.  test_file_path () { -    if [ -f "$BUILD/$1" ] ; then -        echo "$BUILD/$1" -    elif [ -f "$SOURCE/$1" ] ; then -        echo "$SOURCE/$1" +    if [ -n "$BUILD" ] && [ -f "$BUILD/$1" ] ; then +        puts "$BUILD/$1" +    elif [ -n "$SOURCE" ] && [ -f "$SOURCE/$1" ] ; then +        puts "$SOURCE/$1"      else          echo ''      fi  } + +# Create $BUILD/tmp for use by tests for storing temporary files and return +# the path (via standard output). +# +# This macro uses puts, so don't run it using backticks inside double quotes +# or bizarre quoting behavior will happen with Solaris sh. +test_tmpdir () { +    if [ -z "$BUILD" ] ; then +        tap_tmpdir="./tmp" +    else +        tap_tmpdir="$BUILD"/tmp +    fi +    if [ ! -d "$tap_tmpdir" ] ; then +        mkdir "$tap_tmpdir" || bail "Error creating $tap_tmpdir" +    fi +    puts "$tap_tmpdir" +} diff --git a/tests/tap/macros.h b/tests/tap/macros.h new file mode 100644 index 0000000..33fee42 --- /dev/null +++ b/tests/tap/macros.h @@ -0,0 +1,88 @@ +/* + * Helpful macros for TAP header files. + * + * This is not, strictly speaking, related to TAP, but any TAP add-on is + * probably going to need these macros, so define them in one place so that + * everyone can pull them in. + * + * This file is part of C TAP Harness.  The current version plus supporting + * documentation is at <http://www.eyrie.org/~eagle/software/c-tap-harness/>. + * + * Copyright 2008, 2012 Russ Allbery <rra@stanford.edu> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef TAP_MACROS_H +#define TAP_MACROS_H 1 + +/* + * __attribute__ is available in gcc 2.5 and later, but only with gcc 2.7 + * could you use the __format__ form of the attributes, which is what we use + * (to avoid confusion with other macros), and only with gcc 2.96 can you use + * the attribute __malloc__.  2.96 is very old, so don't bother trying to get + * the other attributes to work with GCC versions between 2.7 and 2.96. + */ +#ifndef __attribute__ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 96) +#  define __attribute__(spec)   /* empty */ +# endif +#endif + +/* + * We use __alloc_size__, but it was only available in fairly recent versions + * of GCC.  Suppress warnings about the unknown attribute if GCC is too old. + * We know that we're GCC at this point, so we can use the GCC variadic macro + * extension, which will still work with versions of GCC too old to have C99 + * variadic macro support. + */ +#if !defined(__attribute__) && !defined(__alloc_size__) +# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3) +#  define __alloc_size__(spec, args...) /* empty */ +# endif +#endif + +/* + * LLVM and Clang pretend to be GCC but don't support all of the __attribute__ + * settings that GCC does.  For them, suppress warnings about unknown + * attributes on declarations.  This unfortunately will affect the entire + * compilation context, but there's no push and pop available. + */ +#if !defined(__attribute__) && (defined(__llvm__) || defined(__clang__)) +# pragma GCC diagnostic ignored "-Wattributes" +#endif + +/* Used for unused parameters to silence gcc warnings. */ +#define UNUSED __attribute__((__unused__)) + +/* + * BEGIN_DECLS is used at the beginning of declarations so that C++ + * compilers don't mangle their names.  END_DECLS is used at the end. + */ +#undef BEGIN_DECLS +#undef END_DECLS +#ifdef __cplusplus +# define BEGIN_DECLS    extern "C" { +# define END_DECLS      } +#else +# define BEGIN_DECLS    /* empty */ +# define END_DECLS      /* empty */ +#endif + +#endif /* TAP_MACROS_H */ diff --git a/tests/tap/messages.c b/tests/tap/messages.c index 3bb9a1a..abc2c49 100644 --- a/tests/tap/messages.c +++ b/tests/tap/messages.c @@ -5,24 +5,39 @@   * into a buffer that can be inspected later, allowing testing of error   * handling.   * - * Copyright 2006, 2007, 2009 - *     Board of Trustees, Leland Stanford Jr. University - * Copyright (c) 2004, 2005, 2006 - *     by Internet Systems Consortium, Inc. ("ISC") - * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, - *     2002, 2003 by The Internet Software Consortium and Rich Salz + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.   * - * See LICENSE for licensing terms. + * Copyright 2002, 2004, 2005 Russ Allbery <rra@stanford.edu> + * Copyright 2006, 2007, 2009, 2012 + *     The Board of Trustees of the Leland Stanford Junior University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE.   */  #include <config.h>  #include <portable/system.h> +#include <tests/tap/macros.h>  #include <tests/tap/messages.h> -#include <util/concat.h> -#include <util/macros.h> +#include <tests/tap/string.h>  #include <util/messages.h> -#include <util/xmalloc.h>  /* A global buffer into which message_log_buffer stores error messages. */  char *errors = NULL; @@ -33,18 +48,18 @@ char *errors = NULL;   * error_capture.   */  static void -message_log_buffer(int len, const char *fmt, va_list args, int error UNUSED) +message_log_buffer(int len UNUSED, const char *fmt, va_list args, +                   int error UNUSED)  {      char *message; -    message = xmalloc(len + 1); -    vsnprintf(message, len + 1, fmt, args); -    if (errors == NULL) { -        errors = concat(message, "\n", (char *) 0); -    } else { +    bvasprintf(&message, fmt, args); +    if (errors == NULL) +        basprintf(&errors, "%s\n", message); +    else {          char *new_errors; -        new_errors = concat(errors, message, "\n", (char *) 0); +        basprintf(&new_errors, "%s%s\n", errors, message);          free(errors);          errors = new_errors;      } diff --git a/tests/tap/messages.h b/tests/tap/messages.h index 2b9a7db..0544f2d 100644 --- a/tests/tap/messages.h +++ b/tests/tap/messages.h @@ -1,21 +1,37 @@  /*   * Utility functions to test message handling.   * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>. + * + * Copyright 2002 Russ Allbery <rra@stanford.edu>   * Copyright 2006, 2007, 2009 - *     Board of Trustees, Leland Stanford Jr. University - * Copyright (c) 2004, 2005, 2006 - *     by Internet Systems Consortium, Inc. ("ISC") - * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, - *     2002, 2003 by The Internet Software Consortium and Rich Salz + *     The Board of Trustees of the Leland Stanford Junior University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software.   * - * See LICENSE for licensing terms. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE.   */  #ifndef TAP_MESSAGES_H  #define TAP_MESSAGES_H 1  #include <config.h> -#include <portable/macros.h> +#include <tests/tap/macros.h>  /* A global buffer into which errors_capture stores errors. */  extern char *errors; diff --git a/tests/tap/perl/Test/RRA.pm b/tests/tap/perl/Test/RRA.pm new file mode 100644 index 0000000..2d119f4 --- /dev/null +++ b/tests/tap/perl/Test/RRA.pm @@ -0,0 +1,222 @@ +# Helper functions for test programs written in Perl. +# +# This module provides a collection of helper functions used by test programs +# written in Perl.  This is a general collection of functions that can be used +# by both C packages with Automake and by stand-alone Perl modules.  See +# Test::RRA::Automake for additional functions specifically for C Automake +# distributions. +# +# The canonical version of this file is maintained in the rra-c-util package, +# which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>. +# +# Written by Russ Allbery <rra@stanford.edu> +# Copyright 2013 +#     The Board of Trustees of the Leland Stanford Junior University +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. + +package Test::RRA; + +use 5.006; +use strict; +use warnings; + +use Exporter; +use Test::More; + +# For Perl 5.006 compatibility. +## no critic (ClassHierarchies::ProhibitExplicitISA) + +# Declare variables that should be set in BEGIN for robustness. +our (@EXPORT_OK, @ISA, $VERSION); + +# Set $VERSION and everything export-related in a BEGIN block for robustness +# against circular module loading (not that we load any modules, but +# consistency is good). +BEGIN { +    @ISA       = qw(Exporter); +    @EXPORT_OK = qw(skip_unless_maintainer use_prereq); + +    # This version should match the corresponding rra-c-util release, but with +    # two digits for the minor version, including a leading zero if necessary, +    # so that it will sort properly. +    $VERSION = '4.08'; +} + +# Skip this test unless maintainer tests are requested.  Takes a short +# description of what tests this script would perform, which is used in the +# skip message.  Calls plan skip_all, which will terminate the program. +# +# $description - Short description of the tests +# +# Returns: undef +sub skip_unless_maintainer { +    my ($description) = @_; +    if (!$ENV{RRA_MAINTAINER_TESTS}) { +        plan skip_all => "$description only run for maintainer"; +    } +    return; +} + +# Attempt to load a module and skip the test if the module could not be +# loaded.  If the module could be loaded, call its import function manually. +# If the module could not be loaded, calls plan skip_all, which will terminate +# the program. +# +# The special logic here is based on Test::More and is required to get the +# imports to happen in the caller's namespace. +# +# $module  - Name of the module to load +# @imports - Any arguments to import, possibly including a version +# +# Returns: undef +sub use_prereq { +    my ($module, @imports) = @_; + +    # If the first import looks like a version, pass it as a bare string. +    my $version = q{}; +    if (@imports >= 1 && $imports[0] =~ m{ \A \d+ (?: [.]\d+ )* \z }xms) { +        $version = shift(@imports); +    } + +    # Get caller information to put imports in the correct package. +    my ($package) = caller; + +    # Do the import with eval, and try to isolate it from the surrounding +    # context as much as possible.  Based heavily on Test::More::_eval. +    ## no critic (BuiltinFunctions::ProhibitStringyEval) +    ## no critic (ValuesAndExpressions::ProhibitImplicitNewlines) +    my ($result, $error, $sigdie); +    { +        local $@            = undef; +        local $!            = undef; +        local $SIG{__DIE__} = undef; +        $result = eval qq{ +            package $package; +            use $module $version \@imports; +            1; +        }; +        $error = $@; +        $sigdie = $SIG{__DIE__} || undef; +    } + +    # If the use failed for any reason, skip the test. +    if (!$result || $error) { +        plan skip_all => "$module required for test"; +    } + +    # If the module set $SIG{__DIE__}, we cleared that via local.  Restore it. +    ## no critic (Variables::RequireLocalizedPunctuationVars) +    if (defined($sigdie)) { +        $SIG{__DIE__} = $sigdie; +    } +    return; +} + +1; +__END__ + +=for stopwords +Allbery Allbery's DESC bareword sublicense MERCHANTABILITY NONINFRINGEMENT +rra-c-util + +=head1 NAME + +Test::RRA - Support functions for Perl tests + +=head1 SYNOPSIS + +    use Test::RRA qw(skip_unless_maintainer use_prereq); + +    # Skip this test unless maintainer tests are requested. +    skip_unless_maintainer('Coding style tests'); + +    # Load modules, skipping the test if they're not available. +    use_prereq('File::Slurp'); +    use_prereq('Test::Script::Run', '0.04'); + +=head1 DESCRIPTION + +This module collects utility functions that are useful for Perl test +scripts.  It assumes Russ Allbery's Perl module layout and test +conventions and will only be useful for other people if they use the +same conventions. + +=head1 FUNCTIONS + +None of these functions are imported by default.  The ones used by a +script should be explicitly imported. + +=over 4 + +=item skip_unless_maintainer(DESC) + +Checks whether RRA_MAINTAINER_TESTS is set in the environment and skips +the whole test (by calling C<plan skip_all> from Test::More) if it is not. +DESC is a description of the tests being skipped.  A space and C<only run +for maintainer> will be appended to it and used as the skip reason. + +=item use_prereq(MODULE[, VERSION][, IMPORT ...]) + +Attempts to load MODULE with the given VERSION and import arguments.  If +this fails for any reason, the test will be skipped (by calling C<plan +skip_all> from Test::More) with a skip reason saying that MODULE is +required for the test. + +VERSION will be passed to C<use> as a version bareword if it looks like a +version number.  The remaining IMPORT arguments will be passed as the +value of an array. + +=back + +=head1 AUTHOR + +Russ Allbery <rra@stanford.edu> + +=head1 COPYRIGHT AND LICENSE + +Copyright 2013 The Board of Trustees of the Leland Stanford Junior +University. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +=head1 SEE ALSO + +Test::More(3), Test::RRA::Automake(3), Test::RRA::Config(3) + +This module is maintained in the rra-c-util package.  The current version +is available from L<http://www.eyrie.org/~eagle/software/rra-c-util/>. + +=cut diff --git a/tests/tap/perl/Test/RRA/Automake.pm b/tests/tap/perl/Test/RRA/Automake.pm new file mode 100644 index 0000000..2aadb6a --- /dev/null +++ b/tests/tap/perl/Test/RRA/Automake.pm @@ -0,0 +1,362 @@ +# Helper functions for Perl test programs in Automake distributions. +# +# This module provides a collection of helper functions used by test programs +# written in Perl and included in C source distributions that use Automake. +# They embed knowledge of how I lay out my source trees and test suites with +# Autoconf and Automake.  They may be usable by others, but doing so will +# require closely following the conventions implemented by the rra-c-util +# utility collection. +# +# All the functions here assume that BUILD and SOURCE are set in the +# environment.  This is normally done via the C TAP Harness runtests wrapper. +# +# The canonical version of this file is maintained in the rra-c-util package, +# which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>. +# +# Written by Russ Allbery <rra@stanford.edu> +# Copyright 2013 +#     The Board of Trustees of the Leland Stanford Junior University +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. + +package Test::RRA::Automake; + +use 5.006; +use strict; +use warnings; + +# For Perl 5.006 compatibility. +## no critic (ClassHierarchies::ProhibitExplicitISA) + +use Exporter; +use File::Spec; +use Test::More; +use Test::RRA::Config qw($LIBRARY_PATH); + +# Used below for use lib calls. +my ($PERL_BLIB_ARCH, $PERL_BLIB_LIB); + +# Determine the path to the build tree of any embedded Perl module package in +# this source package.  We do this in a BEGIN block because we're going to use +# the results in a use lib command below. +BEGIN { +    $PERL_BLIB_ARCH = File::Spec->catdir(qw(perl blib arch)); +    $PERL_BLIB_LIB  = File::Spec->catdir(qw(perl blib lib)); + +    # If BUILD is set, we can come up with better values. +    if (defined($ENV{BUILD})) { +        my ($vol, $dirs) = File::Spec->splitpath($ENV{BUILD}, 1); +        my @dirs = File::Spec->splitdir($dirs); +        pop(@dirs); +        $PERL_BLIB_ARCH = File::Spec->catdir(@dirs, qw(perl blib arch)); +        $PERL_BLIB_LIB  = File::Spec->catdir(@dirs, qw(perl blib lib)); +    } +} + +# Prefer the modules built as part of our source package.  Otherwise, we may +# not find Perl modules while testing, or find the wrong versions. +use lib $PERL_BLIB_ARCH; +use lib $PERL_BLIB_LIB; + +# Declare variables that should be set in BEGIN for robustness. +our (@EXPORT_OK, @ISA, $VERSION); + +# Set $VERSION and everything export-related in a BEGIN block for robustness +# against circular module loading (not that we load any modules, but +# consistency is good). +BEGIN { +    @ISA       = qw(Exporter); +    @EXPORT_OK = qw(automake_setup perl_dirs test_file_path); + +    # This version should match the corresponding rra-c-util release, but with +    # two digits for the minor version, including a leading zero if necessary, +    # so that it will sort properly. +    $VERSION = '4.08'; +} + +# Perl directories to skip globally for perl_dirs.  We ignore the perl +# directory if it exists since, in my packages, it is treated as a Perl module +# distribution and has its own standalone test suite. +my @GLOBAL_SKIP = qw(.git perl); + +# Perform initial test setup for running a Perl test in an Automake package. +# This verifies that BUILD and SOURCE are set and then changes directory to +# the SOURCE directory by default.  Sets LD_LIBRARY_PATH if the $LIBRARY_PATH +# configuration option is set.  Calls BAIL_OUT if BUILD or SOURCE are missing +# or if anything else fails. +# +# $args_ref - Reference to a hash of arguments to configure behavior: +#   chdir_build - If set to a true value, changes to BUILD instead of SOURCE +# +# Returns: undef +sub automake_setup { +    my ($args_ref) = @_; + +    # Bail if BUILD or SOURCE are not set. +    if (!$ENV{BUILD}) { +        BAIL_OUT('BUILD not defined (run under runtests)'); +    } +    if (!$ENV{SOURCE}) { +        BAIL_OUT('SOURCE not defined (run under runtests)'); +    } + +    # BUILD or SOURCE will be the test directory.  Change to the parent. +    my $start = $args_ref->{chdir_build} ? $ENV{BUILD} : $ENV{SOURCE}; +    my ($vol, $dirs) = File::Spec->splitpath($start, 1); +    my @dirs = File::Spec->splitdir($dirs); +    pop(@dirs); +    if ($dirs[-1] eq File::Spec->updir) { +        pop(@dirs); +        pop(@dirs); +    } +    my $root = File::Spec->catpath($vol, File::Spec->catdir(@dirs), q{}); +    chdir($root) or BAIL_OUT("cannot chdir to $root: $!"); + +    # If BUILD is a subdirectory of SOURCE, add it to the global ignore list. +    my ($buildvol, $builddirs) = File::Spec->splitpath($ENV{BUILD}, 1); +    my @builddirs = File::Spec->splitdir($builddirs); +    pop(@builddirs); +    if ($buildvol eq $vol && @builddirs == @dirs + 1) { +        while (@dirs && $builddirs[0] eq $dirs[0]) { +            shift(@builddirs); +            shift(@dirs); +        } +        if (@builddirs == 1) { +            push(@GLOBAL_SKIP, $builddirs[0]); +        } +    } + +    # Set LD_LIBRARY_PATH if the $LIBRARY_PATH configuration option is set. +    ## no critic (Variables::RequireLocalizedPunctuationVars) +    if (defined($LIBRARY_PATH)) { +        @builddirs = File::Spec->splitdir($builddirs); +        pop(@builddirs); +        my $libdir = File::Spec->catdir(@builddirs, $LIBRARY_PATH); +        my $path = File::Spec->catpath($buildvol, $libdir, q{}); +        if (-d "$path/.libs") { +            $path .= '/.libs'; +        } +        if ($ENV{LD_LIBRARY_PATH}) { +            $ENV{LD_LIBRARY_PATH} .= ":$path"; +        } else { +            $ENV{LD_LIBRARY_PATH} = $path; +        } +    } +    return; +} + +# Returns a list of directories that may contain Perl scripts and that should +# be passed to Perl test infrastructure that expects a list of directories to +# recursively check.  The list will be all eligible top-level directories in +# the package except for the tests directory, which is broken out to one +# additional level.  Calls BAIL_OUT on any problems +# +# $args_ref - Reference to a hash of arguments to configure behavior: +#   skip - A reference to an array of directories to skip +# +# Returns: List of directories possibly containing Perl scripts to test +sub perl_dirs { +    my ($args_ref) = @_; + +    # Add the global skip list. +    my @skip = $args_ref->{skip} ? @{ $args_ref->{skip} } : (); +    push(@skip, @GLOBAL_SKIP); + +    # Separate directories to skip under tests from top-level directories. +    my @skip_tests = grep { m{ \A tests/ }xms } @skip; +    @skip = grep { !m{ \A tests }xms } @skip; +    for my $skip_dir (@skip_tests) { +        $skip_dir =~ s{ \A tests/ }{}xms; +    } + +    # Convert the skip lists into hashes for convenience. +    my %skip = map { $_ => 1 } @skip, 'tests'; +    my %skip_tests = map { $_ => 1 } @skip_tests; + +    # Build the list of top-level directories to test. +    opendir(my $rootdir, q{.}) or BAIL_OUT("cannot open .: $!"); +    my @dirs = grep { -d $_ && !$skip{$_} } readdir($rootdir); +    closedir($rootdir); +    @dirs = File::Spec->no_upwards(@dirs); + +    # Add the list of subdirectories of the tests directory. +    if (-d 'tests') { +        opendir(my $testsdir, q{tests}) or BAIL_OUT("cannot open tests: $!"); + +        # Skip if found in %skip_tests or if not a directory. +        my $is_skipped = sub { +            my ($dir) = @_; +            return 1 if $skip_tests{$dir}; +            $dir = File::Spec->catdir('tests', $dir); +            return -d $dir ? 0 : 1; +        }; + +        # Build the filtered list of subdirectories of tests. +        my @test_dirs = grep { !$is_skipped->($_) } readdir($testsdir); +        closedir($testsdir); +        @test_dirs = File::Spec->no_upwards(@test_dirs); + +        # Add the tests directory to the start of the directory name. +        push(@dirs, map { File::Spec->catdir('tests', $_) } @test_dirs); +    } +    return @dirs; +} + +# Find a configuration file for the test suite.  Searches relative to BUILD +# first and then SOURCE and returns whichever is found first.  Calls BAIL_OUT +# if the file could not be found. +# +# $file - Partial path to the file +# +# Returns: Full path to the file +sub test_file_path { +    my ($file) = @_; +  BASE: +    for my $base ($ENV{BUILD}, $ENV{SOURCE}) { +        next if !defined($base); +        if (-f "$base/$file") { +            return "$base/$file"; +        } +    } +    BAIL_OUT("cannot find $file"); +    return; +} + +1; +__END__ + +=for stopwords +Allbery Automake Automake-aware Automake-based rra-c-util ARGS +subdirectories sublicense MERCHANTABILITY NONINFRINGEMENT + +=head1 NAME + +Test::RRA::Automake - Automake-aware support functions for Perl tests + +=head1 SYNOPSIS + +    use Test::RRA::Automake qw(automake_setup perl_dirs test_file_path); +    automake_setup({ chdir_build => 1 }); + +    # Paths to directories that may contain Perl scripts. +    my @dirs = perl_dirs({ skip => [qw(lib)] }); + +    # Configuration for Kerberos tests. +    my $keytab = test_file_path('config/keytab'); + +=head1 DESCRIPTION + +This module collects utility functions that are useful for test scripts +written in Perl and included in a C Automake-based package.  They assume +the layout of a package that uses rra-c-util and C TAP Harness for the +test structure. + +Loading this module will also add the directories C<perl/blib/arch> and +C<perl/blib/lib> to the Perl library search path, relative to BUILD if +that environment variable is set.  This is harmless for C Automake +projects that don't contain an embedded Perl module, and for those +projects that do, this will allow subsequent C<use> calls to find modules +that are built as part of the package build process. + +The automake_setup() function should be called before calling any other +functions provided by this module. + +=head1 FUNCTIONS + +None of these functions are imported by default.  The ones used by a +script should be explicitly imported.  On failure, all of these functions +call BAIL_OUT (from Test::More). + +=over 4 + +=item automake_setup([ARGS]) + +Verifies that the BUILD and SOURCE environment variables are set and +then changes directory to the top of the source tree (which is one +directory up from the SOURCE path, since SOURCE points to the top of +the tests directory). + +If ARGS is given, it should be a reference to a hash of configuration +options.  Only one option is supported: C<chdir_build>.  If it is set +to a true value, automake_setup() changes directories to the top of +the build tree instead. + +=item perl_dirs([ARGS]) + +Returns a list of directories that may contain Perl scripts that should be +tested by test scripts that test all Perl in the source tree (such as +syntax or coding style checks).  The paths will be simple directory names +relative to the current directory or two-part directory names under the +F<tests> directory.  (Directories under F<tests> are broken out separately +since it's common to want to apply different policies to different +subdirectories of F<tests>.) + +If ARGS is given, it should be a reference to a hash of configuration +options.  Only one option is supported: C<skip>, whose value should be a +reference to an array of additional top-level directories or directories +starting with C<tests/> that should be skipped. + +=item test_file_path(FILE) + +Given FILE, which should be a relative path, locates that file relative to +the test directory in either the source or build tree.  FILE will be +checked for relative to the environment variable BUILD first, and then +relative to SOURCE.  test_file_path() returns the full path to FILE or +calls BAIL_OUT if FILE could not be found. + +=back + +=head1 AUTHOR + +Russ Allbery <rra@stanford.edu> + +=head1 COPYRIGHT AND LICENSE + +Copyright 2013 The Board of Trustees of the Leland Stanford Junior +University. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +=head1 SEE ALSO + +Test::More(3), Test::RRA(3), Test::RRA::Config(3) + +The C TAP Harness test driver and libraries for TAP-based C testing are +available from L<http://www.eyrie.org/~eagle/software/c-tap-harness/>. + +This module is maintained in the rra-c-util package.  The current version +is available from L<http://www.eyrie.org/~eagle/software/rra-c-util/>. + +=cut diff --git a/tests/tap/perl/Test/RRA/Config.pm b/tests/tap/perl/Test/RRA/Config.pm new file mode 100644 index 0000000..0091b26 --- /dev/null +++ b/tests/tap/perl/Test/RRA/Config.pm @@ -0,0 +1,200 @@ +# Configuration for Perl test cases. +# +# In order to reuse the same Perl test cases in multiple packages, I use a +# configuration file to store some package-specific data.  This module loads +# that configuration and provides the namespace for the configuration +# settings. +# +# The canonical version of this file is maintained in the rra-c-util package, +# which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>. + +package Test::RRA::Config; + +use 5.006; +use strict; +use warnings; + +# For Perl 5.006 compatibility. +## no critic (ClassHierarchies::ProhibitExplicitISA) + +use Exporter; +use Test::More; + +# Declare variables that should be set in BEGIN for robustness. +our (@EXPORT_OK, @ISA, $VERSION); + +# Set $VERSION and everything export-related in a BEGIN block for robustness +# against circular module loading (not that we load any modules, but +# consistency is good). +BEGIN { +    @ISA       = qw(Exporter); +    @EXPORT_OK = qw( +      $COVERAGE_LEVEL @COVERAGE_SKIP_TESTS @CRITIC_IGNORE $LIBRARY_PATH +      $MINIMUM_VERSION %MINIMUM_VERSION @POD_COVERAGE_EXCLUDE +    ); + +    # This version should match the corresponding rra-c-util release, but with +    # two digits for the minor version, including a leading zero if necessary, +    # so that it will sort properly. +    $VERSION = '4.08'; +} + +# If BUILD or SOURCE are set in the environment, look for data/perl.conf under +# those paths for a C Automake package.  Otherwise, look in t/data/perl.conf +# for a standalone Perl module.  Don't use Test::RRA::Automake since it may +# not exist. +our $PATH; +for my $base ($ENV{BUILD}, $ENV{SOURCE}, 't') { +    next if !defined($base); +    my $path = "$base/data/perl.conf"; +    if (-r $path) { +        $PATH = $path; +        last; +    } +} +if (!defined($PATH)) { +    BAIL_OUT('cannot find data/perl.conf'); +} + +# Pre-declare all of our variables and set any defaults. +our $COVERAGE_LEVEL = 100; +our @COVERAGE_SKIP_TESTS; +our @CRITIC_IGNORE; +our $LIBRARY_PATH; +our $MINIMUM_VERSION = '5.008'; +our %MINIMUM_VERSION; +our @POD_COVERAGE_EXCLUDE; + +# Load the configuration. +if (!do($PATH)) { +    my $error = $@ || $! || 'loading file did not return true'; +    BAIL_OUT("cannot load data/perl.conf: $error"); +} + +1; +__END__ + +=for stopwords +Allbery rra-c-util Automake perlcritic .libs namespace sublicense +MERCHANTABILITY NONINFRINGEMENT + +=head1 NAME + +Test::RRA::Config - Perl test configuration + +=head1 SYNOPSIS + +    use Test::RRA::Config qw($MINIMUM_VERSION); +    print "Required Perl version is $MINIMUM_VERSION\n"; + +=head1 DESCRIPTION + +Test::RRA::Config encapsulates per-package configuration for generic Perl +test programs that are shared between multiple packages using the +rra-c-util infrastructure.  It handles locating and loading the test +configuration file for both C Automake packages and stand-alone Perl +modules. + +Test::RRA::Config looks for a file named F<data/perl.conf> relative to the +root of the test directory.  That root is taken from the environment +variables BUILD or SOURCE (in that order) if set, which will be the case +for C Automake packages using C TAP Harness.  If neither is set, it +expects the root of the test directory to be a directory named F<t> +relative to the current directory, which will be the case for stand-alone +Perl modules. + +The following variables are supported: + +=over 4 + +=item $COVERAGE_LEVEL + +The coverage level achieved by the test suite for Perl test coverage +testing using Test::Strict, as a percentage.  The test will fail if test +coverage less than this percentage is achieved.  If not given, defaults +to 100. + +=item @COVERAGE_SKIP_TESTS + +Directories under F<t> whose tests should be skipped when doing coverage +testing.  This can be tests that won't contribute to coverage or tests +that don't run properly under Devel::Cover for some reason (such as ones +that use taint checking).  F<docs> and F<style> will always be skipped +regardless of this setting. + +=item @CRITIC_IGNORE + +Additional directories to ignore when doing recursive perlcritic testing. +The contents of this directory must be either top-level directory names or +directory names starting with F<tests/>. + +=item $LIBRARY_PATH + +Add this directory (or a .libs subdirectory) relative to the top of the +source tree to LD_LIBRARY_PATH when checking the syntax of Perl modules. +This may be required to pick up libraries that are used by in-tree Perl +modules so that Perl scripts can pass a syntax check. + +=item $MINIMUM_VERSION + +Default minimum version requirement for included Perl scripts.  If not +given, defaults to 5.008. + +=item %MINIMUM_VERSION + +Minimum version exceptions for specific directories.  The keys should be +minimum versions of Perl to enforce.  The value for each key should be a +reference to an array of either top-level directory names or directory +names starting with F<tests/>.  All files in those directories will have +that minimum Perl version constraint imposed instead of $MINIMUM_VERSION. + +=item @POD_COVERAGE_EXCLUDE + +Regexes that match method names that should be excluded from POD coverage +testing.  Normally, all methods have to be documented in the POD for a +Perl module, but methods matching any of these regexes will be considered +private and won't require documentation. + +=back + +No variables are exported by default, but the variables can be imported +into the local namespace to avoid long variable names. + +=head1 AUTHOR + +Russ Allbery <rra@stanford.edu> + +=head1 COPYRIGHT AND LICENSE + +Copyright 2013 The Board of Trustees of the Leland Stanford Junior +University. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +=head1 SEE ALSO + +Test::RRA(3), Test::RRA::Automake(3) + +This module is maintained in the rra-c-util package.  The current version +is available from L<http://www.eyrie.org/~eagle/software/rra-c-util/>. + +The C TAP Harness test driver and libraries for TAP-based C testing are +available from L<http://www.eyrie.org/~eagle/software/c-tap-harness/>. + +=cut diff --git a/tests/tap/process.c b/tests/tap/process.c index 16154c7..8ed4cfd 100644 --- a/tests/tap/process.c +++ b/tests/tap/process.c @@ -1,18 +1,37 @@  /*   * 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. + * Provides utility functions for subprocess manipulation.  Specifically, + * provides a function, run_setup, which runs a command and bails if it fails, + * using its error message as the bail output, and 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 + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.   * - * See LICENSE for licensing terms. + * Written by Russ Allbery <rra@stanford.edu> + * Copyright 2002, 2004, 2005 Russ Allbery <rra@stanford.edu> + * Copyright 2009, 2010, 2011 + *     The Board of Trustees of the Leland Stanford Junior University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE.   */  #include <config.h> @@ -22,26 +41,24 @@  #include <tests/tap/basic.h>  #include <tests/tap/process.h> -#include <util/xmalloc.h> +#include <tests/tap/string.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). + * returns the function output in newly allocated memory.  Also captures the + * process exit status.   */ -void -is_function_output(test_function_type function, int status, const char *output, -                   const char *format, ...) +static void +run_child_function(test_function_type function, void *data, int *status, +                   char **output)  {      int fds[2];      pid_t child; -    char *buf, *msg; +    char *buf;      ssize_t count, ret, buflen;      int rval; -    va_list args;      /* Flush stdout before we start to avoid odd forking issues. */      fflush(stdout); @@ -61,7 +78,7 @@ is_function_output(test_function_type function, int status, const char *output,              _exit(255);          /* Now, run the function and exit successfully if it returns. */ -        (*function)(); +        (*function)(data);          fflush(stdout);          _exit(0);      } else { @@ -71,7 +88,7 @@ is_function_output(test_function_type function, int status, const char *output,           */          close(fds[1]);          buflen = BUFSIZ; -        buf = xmalloc(buflen); +        buf = bmalloc(buflen);          count = 0;          do {              ret = read(fds[0], buf + count, buflen - count - 1); @@ -79,18 +96,41 @@ is_function_output(test_function_type function, int status, const char *output,                  count += ret;              if (count >= buflen - 1) {                  buflen += BUFSIZ; -                buf = xrealloc(buf, buflen); +                buf = brealloc(buf, buflen);              }          } while (ret > 0);          buf[count < 0 ? 0 : count] = '\0';          if (waitpid(child, &rval, 0) == (pid_t) -1)              sysbail("waitpid failed"); +        close(fds[0]);      } +    /* Store the output and return. */ +    *status = rval; +    *output = buf; +} + + +/* + * Given a function, data to pass to that 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, void *data, int status, +                   const char *output, const char *format, ...) +{ +    char *buf, *msg; +    int rval; +    va_list args; + +    run_child_function(function, data, &rval, &buf); +      /* Now, check the results against what we expected. */      va_start(args, format); -    if (xvasprintf(&msg, format, args) < 0) -        bail("cannot format test description"); +    bvasprintf(&msg, format, args);      va_end(args);      ok(WIFEXITED(rval), "%s (exited)", msg);      is_int(status, WEXITSTATUS(rval), "%s (status)", msg); @@ -98,3 +138,40 @@ is_function_output(test_function_type function, int status, const char *output,      free(buf);      free(msg);  } + + +/* + * A helper function for run_setup.  This is a function to run an external + * command, suitable for passing into run_child_function.  The expected + * argument must be an argv array, with argv[0] being the command to run. + */ +static void +exec_command(void *data) +{ +    char *const *argv = data; + +    execvp(argv[0], argv); +} + + +/* + * Given a command expressed as an argv struct, with argv[0] the name or path + * to the command, run that command.  If it exits with a non-zero status, use + * the part of its output up to the first newline as the error message when + * calling bail. + */ +void +run_setup(const char *const argv[]) +{ +    char *output, *p; +    int status; + +    run_child_function(exec_command, (void *) argv, &status, &output); +    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { +        p = strchr(output, '\n'); +        if (p != NULL) +            *p = '\0'; +        bail("%s", output); +    } +    free(output); +} diff --git a/tests/tap/process.h b/tests/tap/process.h index b7d3b11..df74b5f 100644 --- a/tests/tap/process.h +++ b/tests/tap/process.h @@ -1,36 +1,64 @@  /*   * 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 + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.   * - * See LICENSE for licensing terms. + * Written by Russ Allbery <rra@stanford.edu> + * Copyright 2009, 2010 + *     The Board of Trustees of the Leland Stanford Junior University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE.   */  #ifndef TAP_PROCESS_H  #define TAP_PROCESS_H 1  #include <config.h> -#include <portable/macros.h> +#include <tests/tap/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). + * the function to always exit (not die from a signal).  data is optional data + * that's passed into the function as its only argument.   *   * 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))); +typedef void (*test_function_type)(void *); +void is_function_output(test_function_type, void *data, int status, +                        const char *output, const char *format, ...) +    __attribute__((__format__(printf, 5, 6), __nonnull__(1))); + +/* + * Run a setup program.  Takes the program to run and its arguments as an argv + * vector, where argv[0] must be either the full path to the program or the + * program name if the PATH should be searched.  If the program does not exit + * successfully, call bail, with the error message being the output from the + * program. + */ +void run_setup(const char *const argv[]) +    __attribute__((__nonnull__));  END_DECLS diff --git a/tests/tap/remctl.sh b/tests/tap/remctl.sh index 9e01bcf..2fd6681 100644 --- a/tests/tap/remctl.sh +++ b/tests/tap/remctl.sh @@ -1,40 +1,67 @@  # Shell function library to start and stop remctld  # +# Note that while many of the functions in this library could benefit from +# using "local" to avoid possibly hammering global variables, Solaris /bin/sh +# doesn't support local and this library aspires to be portable to Solaris +# Bourne shell.  Instead, all private variables are prefixed with "tap_". +# +# The canonical version of this file is maintained in the rra-c-util package, +# which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>. +#  # Written by Russ Allbery <rra@stanford.edu> -# Copyright 2009 Board of Trustees, Leland Stanford Jr. University +# Copyright 2009, 2012 +#     The Board of Trustees of the Leland Stanford Junior University +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software.  # -# See LICENSE for licensing terms. +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE.  # 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=`test_file_path data/test.keytab` -    principal=`test_file_path data/test.principal` -    principal=`cat "$principal" 2>/dev/null` -    if [ -z "$keytab" ] || [ -z "$principal" ] ; then +    tap_pidfile=`test_tmpdir`/remctld.pid +    rm -f "$tap_pidfile" +    tap_keytab=`test_file_path config/keytab` +    tap_principal=`test_file_path config/principal` +    tap_principal=`cat "$tap_principal" 2>/dev/null` +    if [ -z "$tap_keytab" ] || [ -z "$tap_principal" ] ; then          return 1      fi      if [ -n "$VALGRIND" ] ; then          ( "$VALGRIND" --log-file=valgrind.%p --leak-check=full "$1" -m \ -          -p 14373 -s "$principal" -P "$BUILD/data/remctld.pid" -f "$2" -d \ -          -S -F -k "$keytab" &) +          -p 14373 -s "$tap_principal" -P "$tap_pidfile" -f "$2" -d -S -F \ +          -k "$tap_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" &) +        ( "$1" -m -p 14373 -s "$tap_principal" -P "$tap_pidfile" -f "$2" \ +          -d -S -F -k "$tap_keytab" &)      fi -    [ -f "$BUILD/data/remctld.pid" ] || sleep 1 -    if [ ! -f "$BUILD/data/remctld.pid" ] ; then +    [ -f "$tap_pidfile" ] || sleep 1 +    [ -f "$tap_pidfile" ] || sleep 1 +    if [ ! -f "$tap_pidfile" ] ; 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" +    tap_pidfile=`test_tmpdir`/remctld.pid +    if [ -f "$tap_pidfile" ] ; then +        kill -TERM `cat "$tap_pidfile"` +        rm -f "$tap_pidfile"      fi  } diff --git a/tests/tap/string.c b/tests/tap/string.c new file mode 100644 index 0000000..f5c965c --- /dev/null +++ b/tests/tap/string.c @@ -0,0 +1,65 @@ +/* + * String utilities for the TAP protocol. + * + * Additional string utilities that can't be included with C TAP Harness + * because they rely on additional portability code from rra-c-util. + * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>. + * + * Copyright 2011, 2012 Russ Allbery <rra@stanford.edu> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <config.h> +#include <portable/system.h> + +#include <tests/tap/basic.h> +#include <tests/tap/string.h> + + +/* + * vsprintf into a newly allocated string, reporting a fatal error with bail + * on failure. + */ +void +bvasprintf(char **strp, const char *fmt, va_list args) +{ +    int status; + +    status = vasprintf(strp, fmt, args); +    if (status < 0) +        sysbail("failed to allocate memory for vasprintf"); +} + + +/* + * sprintf into a newly allocated string, reporting a fatal error with bail on + * failure. + */ +void +basprintf(char **strp, const char *fmt, ...) +{ +    va_list args; + +    va_start(args, fmt); +    bvasprintf(strp, fmt, args); +    va_end(args); +} diff --git a/tests/tap/string.h b/tests/tap/string.h new file mode 100644 index 0000000..2f699e4 --- /dev/null +++ b/tests/tap/string.h @@ -0,0 +1,49 @@ +/* + * String utilities for the TAP protocol. + * + * Additional string utilities that can't be included with C TAP Harness + * because they rely on additional portability code from rra-c-util. + * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>. + * + * Copyright 2011, 2012 Russ Allbery <rra@stanford.edu> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef TAP_STRING_H +#define TAP_STRING_H 1 + +#include <config.h> +#include <tests/tap/macros.h> + +#include <stdarg.h>             /* va_list */ + +BEGIN_DECLS + +/* sprintf into an allocated string, calling bail on any failure. */ +void basprintf(char **, const char *, ...) +    __attribute__((__nonnull__, __format__(printf, 2, 3))); +void bvasprintf(char **, const char *, va_list) +    __attribute__((__nonnull__)); + +END_DECLS + +#endif /* !TAP_STRING_H */ diff --git a/tests/util/messages-krb5-t.c b/tests/util/messages-krb5-t.c index 02d8f92..e3ffe75 100644 --- a/tests/util/messages-krb5-t.c +++ b/tests/util/messages-krb5-t.c @@ -1,10 +1,30 @@  /*   * Test suite for Kerberos error handling routines.   * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>. + *   * Written by Russ Allbery <rra@stanford.edu> - * Copyright 2010 Board of Trustees, Leland Stanford Jr. University + * Copyright 2010, 2011 + *     The Board of Trustees of the Leland Stanford Junior University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software.   * - * See LICENSE for licensing terms. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE.   */  #include <config.h> @@ -13,6 +33,7 @@  #include <tests/tap/basic.h>  #include <tests/tap/process.h> +#include <util/macros.h>  #include <util/messages-krb5.h>  #include <util/messages.h>  #include <util/xmalloc.h> @@ -22,7 +43,7 @@   * Test functions.   */  static void -test_warn(void) +test_warn(void *data UNUSED)  {      krb5_context ctx;      krb5_error_code code; @@ -40,7 +61,7 @@ test_warn(void)  }  static void -test_die(void) +test_die(void *data UNUSED)  {      krb5_context ctx;      krb5_error_code code; @@ -80,20 +101,20 @@ main(void)      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"); +    is_function_output(test_warn, NULL, 0, wanted, "warn_krb5"); +    is_function_output(test_die, NULL, 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"); +    is_function_output(test_warn, NULL, 0, wanted, "warn_krb5 with name"); +    is_function_output(test_die, NULL, 1, wanted, "die_krb5 with name");      free(wanted);      message_handlers_warn(0); -    is_function_output(test_warn, 0, "", "warn_krb5 with no handlers"); +    is_function_output(test_warn, NULL, 0, "", "warn_krb5 with no handlers");      message_handlers_die(0); -    is_function_output(test_die, 1, "", "warn_krb5 with no handlers"); +    is_function_output(test_die, NULL, 1, "", "warn_krb5 with no handlers");      return 0;  } diff --git a/tests/util/messages-t.c b/tests/util/messages-t.c index a58f82c..54f1cf1 100644 --- a/tests/util/messages-t.c +++ b/tests/util/messages-t.c @@ -1,14 +1,31 @@  /*   * Test suite for error handling routines.   * + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>. + *   * 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 (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, - *     2002, 2003 by The Internet Software Consortium and Rich Salz + * Copyright 2002, 2004, 2005 Russ Allbery <rra@stanford.edu> + * Copyright 2009, 2010, 2011, 2012 + *     The Board of Trustees of the Leland Stanford Junior University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software.   * - * See LICENSE for licensing terms. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE.   */  #include <config.h> @@ -21,7 +38,7 @@  #include <tests/tap/basic.h>  #include <tests/tap/process.h> -#include <util/concat.h> +#include <util/macros.h>  #include <util/messages.h>  #include <util/xmalloc.h> @@ -29,24 +46,27 @@  /*   * Test functions.   */ -static void test1(void) { warn("warning"); } -static void test2(void) { die("fatal"); } -static void test3(void) { errno = EPERM; syswarn("permissions"); } -static void test4(void) { errno = EACCES; sysdie("fatal access"); } -static void test5(void) { +static void test1(void *data UNUSED) { warn("warning"); } +static void test2(void *data UNUSED) { die("fatal"); } +static void test3(void *data UNUSED) { errno = EPERM; syswarn("permissions"); } +static void test4(void *data UNUSED) { +    errno = EACCES; +    sysdie("fatal access"); +} +static void test5(void *data UNUSED) {      message_program_name = "test5";      warn("warning");  } -static void test6(void) { +static void test6(void *data UNUSED) {      message_program_name = "test6";      die("fatal");  } -static void test7(void) { +static void test7(void *data UNUSED) {      message_program_name = "test7";      errno = EPERM;      syswarn("perms %d", 7);  } -static void test8(void) { +static void test8(void *data UNUSED) {      message_program_name = "test8";      errno = EACCES;      sysdie("%st%s", "fa", "al"); @@ -54,17 +74,17 @@ static void test8(void) {  static int return10(void) { return 10; } -static void test9(void) { +static void test9(void *data UNUSED) {      message_fatal_cleanup = return10;      die("fatal");  } -static void test10(void) { +static void test10(void *data UNUSED) {      message_program_name = 0;      message_fatal_cleanup = return10;      errno = EPERM;      sysdie("fatal perm");  } -static void test11(void) { +static void test11(void *data UNUSED) {      message_program_name = "test11";      message_fatal_cleanup = return10;      errno = EPERM; @@ -72,61 +92,61 @@ static void test11(void) {      sysdie("fatal");  } -static void log_msg(int len, const char *format, va_list args, int error) { -    fprintf(stderr, "%d %d ", len, error); +static void log_msg(size_t len, const char *format, va_list args, int error) { +    fprintf(stderr, "%lu %d ", (unsigned long) len, error);      vfprintf(stderr, format, args);      fprintf(stderr, "\n");  } -static void test12(void) { +static void test12(void *data UNUSED) {      message_handlers_warn(1, log_msg);      warn("warning");  } -static void test13(void) { +static void test13(void *data UNUSED) {      message_handlers_die(1, log_msg);      die("fatal");  } -static void test14(void) { +static void test14(void *data UNUSED) {      message_handlers_warn(2, log_msg, log_msg);      errno = EPERM;      syswarn("warning");  } -static void test15(void) { +static void test15(void *data UNUSED) {      message_handlers_die(2, log_msg, log_msg);      message_fatal_cleanup = return10;      errno = EPERM;      sysdie("fatal");  } -static void test16(void) { +static void test16(void *data UNUSED) {      message_handlers_warn(2, message_log_stderr, log_msg);      message_program_name = "test16";      errno = EPERM;      syswarn("warning");  } -static void test17(void) { notice("notice"); } -static void test18(void) { +static void test17(void *data UNUSED) { notice("notice"); } +static void test18(void *data UNUSED) {      message_program_name = "test18";      notice("notice");  } -static void test19(void) { debug("debug"); } -static void test20(void) { +static void test19(void *data UNUSED) { debug("debug"); } +static void test20(void *data UNUSED) {      message_handlers_notice(1, log_msg);      notice("foo");  } -static void test21(void) { +static void test21(void *data UNUSED) {      message_handlers_debug(1, message_log_stdout);      message_program_name = "test23";      debug("baz");  } -static void test22(void) { +static void test22(void *data UNUSED) {      message_handlers_die(0);      die("hi mom!");  } -static void test23(void) { +static void test23(void *data UNUSED) {      message_handlers_warn(0);      warn("this is a test");  } -static void test24(void) { +static void test24(void *data UNUSED) {      notice("first");      message_handlers_notice(0);      notice("second"); @@ -145,9 +165,9 @@ test_strerror(int status, const char *output, int error,  {      char *full_output, *name; -    full_output = concat(output, ": ", strerror(error), "\n", (char *) NULL); +    xasprintf(&full_output, "%s: %s\n", output, strerror(error));      xasprintf(&name, "strerror %lu", testnum / 3 + 1); -    is_function_output(function, status, full_output, "%s", name); +    is_function_output(function, NULL, status, full_output, "%s", name);      free(full_output);      free(name);  } @@ -164,43 +184,43 @@ main(void)      plan(24 * 3); -    is_function_output(test1, 0, "warning\n", "test1"); -    is_function_output(test2, 1, "fatal\n", "test2"); +    is_function_output(test1, NULL, 0, "warning\n", "test1"); +    is_function_output(test2, NULL, 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"); +    is_function_output(test5, NULL, 0, "test5: warning\n", "test5"); +    is_function_output(test6, NULL, 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"); +    is_function_output(test9, NULL, 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"); +    is_function_output(test12, NULL, 0, "7 0 warning\n", "test12"); +    is_function_output(test13, NULL, 1, "5 0 fatal\n", "test13");      sprintf(buff, "%d", EPERM);      xasprintf(&output, "7 %d warning\n7 %d warning\n", EPERM, EPERM); -    is_function_output(test14, 0, output, "test14"); +    is_function_output(test14, NULL, 0, output, "test14");      free(output);      xasprintf(&output, "5 %d fatal\n5 %d fatal\n", EPERM, EPERM); -    is_function_output(test15, 10, output, "test15"); +    is_function_output(test15, NULL, 10, output, "test15");      free(output);      xasprintf(&output, "test16: warning: %s\n7 %d warning\n", strerror(EPERM),                EPERM); -    is_function_output(test16, 0, output, "test16"); +    is_function_output(test16, NULL, 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"); +    is_function_output(test17, NULL, 0, "notice\n", "test17"); +    is_function_output(test18, NULL, 0, "test18: notice\n", "test18"); +    is_function_output(test19, NULL, 0, "", "test19"); +    is_function_output(test20, NULL, 0, "3 0 foo\n", "test20"); +    is_function_output(test21, NULL, 0, "test23: baz\n", "test21");      /* Make sure that it's possible to turn off a message type entirely. */  -    is_function_output(test22, 1, "", "test22"); -    is_function_output(test23, 0, "", "test23"); -    is_function_output(test24, 0, "first\nthird\n", "test24"); +    is_function_output(test22, NULL, 1, "", "test22"); +    is_function_output(test23, NULL, 0, "", "test23"); +    is_function_output(test24, NULL, 0, "first\nthird\n", "test24");      return 0;  } diff --git a/tests/util/xmalloc-t b/tests/util/xmalloc-t index 67d95f6..b6c6dfd 100755 --- a/tests/util/xmalloc-t +++ b/tests/util/xmalloc-t @@ -2,14 +2,31 @@  #  # Test suite for xmalloc and friends.  # +# The canonical version of this file is maintained in the rra-c-util package, +# which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>. +#  # 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 +# Copyright 2000, 2001, 2006 Russ Allbery <rra@stanford.edu> +# Copyright 2008, 2009, 2010, 2012 +#     The Board of Trustees of the Leland Stanford Junior University +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions:  # -# See LICENSE for licensing terms. +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE.  . "$SOURCE/tap/libtap.sh"  cd "$BUILD/util" @@ -24,18 +41,16 @@ ok_xmalloc () {      shift      w_output="$1"      shift -    output=`./xmalloc "$@" 2>&1` +    output=`strip_colon_error ./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 +        diag "$output"          skip "no data limit support"      else -        echo "#  saw: ($status) $output" -        echo "#  not: ($w_status) $w_output" +        diag "saw: ($status) $output" +        diag "not: ($w_status) $w_output"          ok "$desc" false      fi  } @@ -53,75 +68,76 @@ 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 large"    0 "" "m" "5500000" "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 "realloc large"   0 "" "r" "5500000" "0"  ok_xmalloc "strdup small"    0 "" "s" "21"      "0" -ok_xmalloc "strdup large"    0 "" "s" "3500000" "0" +ok_xmalloc "strdup large"    0 "" "s" "5500000" "0"  ok_xmalloc "strndup small"   0 "" "n" "21"      "0" -ok_xmalloc "strndup large"   0 "" "n" "3500000" "0" +ok_xmalloc "strndup large"   0 "" "n" "5500000" "0"  ok_xmalloc "calloc small"    0 "" "c" "24"      "0" -ok_xmalloc "calloc large"    0 "" "c" "3500000" "0" +ok_xmalloc "calloc large"    0 "" "c" "5500000" "0"  ok_xmalloc "asprintf small"  0 "" "a" "24"      "0" -ok_xmalloc "asprintf large"  0 "" "a" "3500000" "0" +ok_xmalloc "asprintf large"  0 "" "a" "5500000" "0"  ok_xmalloc "vasprintf small" 0 "" "v" "24"      "0" -ok_xmalloc "vasprintf large" 0 "" "v" "3500000" "0" +ok_xmalloc "vasprintf large" 0 "" "v" "5500000" "0" -# Now limit our memory to 3.5MB and then try the large ones again, all of +# Now limit our memory to 5.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. +# memory normally.  The amount of memory required varies a lot based on what +# shared libraries are loaded, and if it's too small, all memory allocations +# fail.  5.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" +    "failed to malloc 5500000 bytes at xmalloc.c line 38" \ +    "m" "5500000" "5500000"  ok_xmalloc "realloc fail" 1 \ -    "failed to realloc 3500000 bytes at xmalloc.c line 66" \ -    "r" "3500000" "3500000" +    "failed to realloc 5500000 bytes at xmalloc.c line 66" \ +    "r" "5500000" "5500000"  ok_xmalloc "strdup fail" 1 \ -    "failed to strdup 3500000 bytes at xmalloc.c line 97" \ -    "s" "3500000" "3500000" +    "failed to strdup 5500000 bytes at xmalloc.c line 97" \ +    "s" "5500000" "5500000"  ok_xmalloc "strndup fail" 1 \ -    "failed to strndup 3500000 bytes at xmalloc.c line 124" \ -    "n" "3500000" "3500000" +    "failed to strndup 5500000 bytes at xmalloc.c line 143" \ +    "n" "5500000" "5500000"  ok_xmalloc "calloc fail" 1 \ -    "failed to calloc 3500000 bytes at xmalloc.c line 148" \ -    "c" "3500000" "3500000" +    "failed to calloc 5500000 bytes at xmalloc.c line 167" \ +    "c" "5500000" "5500000"  ok_xmalloc "asprintf fail" 1 \ -    "failed to asprintf 3500000 bytes at xmalloc.c line 172" \ -    "a" "3500000" "3500000" +    "failed to asprintf 5500000 bytes at xmalloc.c line 191" \ +    "a" "5500000" "5500000"  ok_xmalloc "vasprintf fail" 1 \ -    "failed to vasprintf 3500000 bytes at xmalloc.c line 192" \ -    "v" "3500000" "3500000" +    "failed to vasprintf 5500000 bytes at xmalloc.c line 210" \ +    "v" "5500000" "5500000"  # 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 172" \ -    "A" "3500000" "3500000" -ok_xmalloc "vasprintf custom" 1 "vasprintf 3500000 xmalloc.c 192" \ -    "V" "3500000" "3500000" +ok_xmalloc "malloc custom"    1 "malloc 5500000 xmalloc.c 38" \ +    "M" "5500000" "5500000" +ok_xmalloc "realloc custom"   1 "realloc 5500000 xmalloc.c 66" \ +    "R" "5500000" "5500000" +ok_xmalloc "strdup custom"    1 "strdup 5500000 xmalloc.c 97" \ +    "S" "5500000" "5500000" +ok_xmalloc "strndup custom"   1 "strndup 5500000 xmalloc.c 143" \ +    "N" "5500000" "5500000" +ok_xmalloc "calloc custom"    1 "calloc 5500000 xmalloc.c 167" \ +    "C" "5500000" "5500000" +ok_xmalloc "asprintf custom"  1 "asprintf 5500000 xmalloc.c 191" \ +    "A" "5500000" "5500000" +ok_xmalloc "vasprintf custom" 1 "vasprintf 5500000 xmalloc.c 210" \ +    "V" "5500000" "5500000"  # 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" +ok_xmalloc "malloc retry"    0 "" "m" "21" "5500000" +ok_xmalloc "realloc retry"   0 "" "r" "32" "5500000" +ok_xmalloc "strdup retry"    0 "" "s" "64" "5500000" +ok_xmalloc "strndup retry"   0 "" "n" "20" "5500000" +ok_xmalloc "calloc retry"    0 "" "c" "24" "5500000" +ok_xmalloc "asprintf retry"  0 "" "a" "30" "5500000" +ok_xmalloc "vasprintf retry" 0 "" "v" "35" "5500000" diff --git a/tests/util/xmalloc.c b/tests/util/xmalloc.c index c37396e..394cab5 100644 --- a/tests/util/xmalloc.c +++ b/tests/util/xmalloc.c @@ -1,13 +1,30 @@  /*   * 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 + * The canonical version of this file is maintained in the rra-c-util package, + * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.   * - * See LICENSE for licensing terms. + * Copyright 2000, 2001, 2006 Russ Allbery <rra@stanford.edu> + * Copyright 2008, 2012 + *     The Board of Trustees of the Leland Stanford Junior University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE.   */  #line 1 "xmalloc.c" @@ -62,9 +79,9 @@ test_malloc(size_t size)  /* - * Allocate half the memory given, write to it, then reallocate to the desired - * size, writing to the rest and then checking it all.  Returns true on - * success, false on any failure. + * Allocate 10 bytes of memory given, write to it, then reallocate to the + * desired size, writing to the rest and then checking it all.  Returns true + * on success, false on any failure.   */  static int  test_realloc(size_t size) @@ -119,15 +136,34 @@ test_strdup(size_t size)  /*   * Generate a string of the size indicated plus some, call xstrndup on it, and - * then ensure the result matches.  Returns true on success, false on any - * failure. + * then ensure the result matches.  Also test xstrdup on a string that's + * shorter than the specified size and ensure that we don't copy too much, and + * on a string that's not nul-terminated.  Returns true on success, false on + * any failure.   */  static int  test_strndup(size_t size)  {      char *string, *copy; -    int match, toomuch; +    int shortmatch, nonulmatch, match, toomuch; + +    /* Copy a short string. */ +    string = xmalloc(5); +    memcpy(string, "test", 5); +    copy = xstrndup(string, size); +    shortmatch = strcmp(string, copy); +    free(string); +    free(copy); +    /* Copy a string that's not nul-terminated. */ +    string = xmalloc(4); +    memcpy(string, "test", 4); +    copy = xstrndup(string, 4); +    nonulmatch = strcmp(copy, "test"); +    free(string); +    free(copy); + +    /* Now the test of running out of memory. */      string = xmalloc(size + 1);      if (string == NULL)          return 0; @@ -141,7 +177,7 @@ test_strndup(size_t size)      toomuch = strcmp(string, copy);      free(string);      free(copy); -    return (match == 0 && toomuch != 0); +    return (shortmatch == 0 && nonulmatch == 0 && match == 0 && toomuch != 0);  } @@ -195,16 +231,14 @@ test_asprintf(size_t size)  /* Wrapper around vasprintf to do the va_list stuff. */ -static int +static void  xvasprintf_wrapper(char **strp, const char *format, ...)  {      va_list args; -    int status;      va_start(args, format); -    status = xvasprintf(strp, format, args); +    xvasprintf(strp, format, args);      va_end(args); -    return status;  } @@ -298,8 +332,8 @@ main(int argc, char *argv[])          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); +                syswarn("Can't allocate initial memory of %lu (limit %lu)", +                        (unsigned long) size, (unsigned long) limit);                  exit(2);              }              free(tmp); | 
