diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/TESTS | 3 | ||||
-rw-r--r-- | tests/data/perl.conf | 5 | ||||
-rwxr-xr-x | tests/perl/module-version-t | 183 | ||||
-rw-r--r-- | tests/portable/asprintf-t.c | 1 | ||||
-rw-r--r-- | tests/portable/snprintf-t.c | 4 | ||||
-rw-r--r-- | tests/portable/strlcat-t.c | 78 | ||||
-rw-r--r-- | tests/portable/strlcat.c | 2 | ||||
-rw-r--r-- | tests/portable/strlcpy-t.c | 70 | ||||
-rw-r--r-- | tests/portable/strlcpy.c | 2 | ||||
-rw-r--r-- | tests/runtests.c | 204 | ||||
-rw-r--r-- | tests/tap/basic.c | 12 | ||||
-rw-r--r-- | tests/tap/basic.h | 8 | ||||
-rw-r--r-- | tests/tap/kerberos.c | 31 | ||||
-rw-r--r-- | tests/tap/kerberos.h | 8 | ||||
-rw-r--r-- | tests/tap/macros.h | 8 | ||||
-rw-r--r-- | tests/tap/messages.c | 4 | ||||
-rw-r--r-- | tests/tap/perl/Test/RRA.pm | 104 | ||||
-rw-r--r-- | tests/tap/perl/Test/RRA/Automake.pm | 166 | ||||
-rw-r--r-- | tests/tap/perl/Test/RRA/Config.pm | 138 | ||||
-rw-r--r-- | tests/tap/perl/Test/RRA/ModuleVersion.pm | 295 | ||||
-rw-r--r-- | tests/tap/process.c | 9 | ||||
-rw-r--r-- | tests/tap/string.h | 2 | ||||
-rw-r--r-- | tests/util/messages-t.c | 5 | ||||
-rwxr-xr-x | tests/util/xmalloc-t | 32 | ||||
-rw-r--r-- | tests/util/xmalloc.c | 7 |
25 files changed, 901 insertions, 480 deletions
diff --git a/tests/TESTS b/tests/TESTS index d947e97..76bd4ae 100644 --- a/tests/TESTS +++ b/tests/TESTS @@ -5,13 +5,12 @@ client/rekey docs/pod docs/pod-spelling perl/minimum-version +perl/module-version perl/strict portable/asprintf portable/mkstemp portable/setenv portable/snprintf -portable/strlcat -portable/strlcpy server/admin server/backend server/keytab diff --git a/tests/data/perl.conf b/tests/data/perl.conf index eaf7443..0c1e34e 100644 --- a/tests/data/perl.conf +++ b/tests/data/perl.conf @@ -1,6 +1,9 @@ # Configuration for Perl tests. -*- perl -*- -# No special configuration yet. +# Wallet::Schema's version number is used to version the database schema and +# requires upgrade SQL files for each version bump. Until this is replaced +# with some better system, exclude it from version checking. +@MODULE_VERSION_IGNORE = qw(perl/lib/Wallet/Schema.pm); # File must end with this line. 1; diff --git a/tests/perl/module-version-t b/tests/perl/module-version-t new file mode 100755 index 0000000..f1ebf0f --- /dev/null +++ b/tests/perl/module-version-t @@ -0,0 +1,183 @@ +#!/usr/bin/perl +# +# Check or update the version of embedded Perl modules. +# +# Examines all module files (*.pm) under the perl/lib directory, if it exists, +# and verifies that their $VERSION is set to the same value as the current +# version number as determined by the NEWS file at the top level of the source +# tree (or the current directory if not being run as a test). +# +# When given the --update option, instead fixes all of the Perl modules found +# to have the correct version. + +use 5.006; +use strict; +use warnings; + +# SOURCE may not be set if we're running this script manually to update +# version numbers. If it isn't, assume we're being run from the top of the +# tree. +BEGIN { + if ($ENV{SOURCE}) { + unshift(@INC, "$ENV{SOURCE}/tap/perl"); + } else { + unshift(@INC, 'tests/tap/perl'); + } +} + +use Getopt::Long qw(GetOptions); +use Test::RRA qw(skip_unless_automated); +use Test::RRA::Automake qw(automake_setup); +use Test::RRA::ModuleVersion qw(test_module_versions update_module_versions); + +# Return the current version and, optionally, the package name from the NEWS +# file. Munges the version to be appropriate for Perl if necessary. +# +# Returns: Scalar: The version number of the latest version in NEWS +# List: The version number and the package name +# Throws: Text exception if NEWS is not found or doesn't contain a version +sub news_version { + my ($package, $version, $news); + for my $path ('NEWS', '../NEWS') { + if (-f $path) { + open($news, q{<}, $path) or die "$0: cannot open $path: $!\n"; + } + } + if (!$news) { + die "$0: cannot find NEWS file\n"; + } + SCAN: + while (defined(my $line = <$news>)) { + ## no critic (RegularExpressions::ProhibitEscapedMetacharacters) + if ($line =~ m{ \A ([\w\s-]+) \s ([\d.]+) \s \( }xms) { + ($package, $version) = ($1, $2); + last SCAN; + } + ## use critic + } + close($news) or die "$0: error reading from NEWS: $!\n"; + if (!defined($version)) { + die "$0: cannot find version number in NEWS\n"; + } + + # Munge the version for Perl purposes by ensuring that each component + # has two digits and by dropping the second period. + $version =~ s{ [.] (\d) (?= [.] | \z ) }{.0$1}xmsg; + $version =~ s{ ([.] \d+) [.] (\d+) }{$1$2}xms; + + # Return the appropriate value based on context. + return wantarray ? ($version, $package) : $version; +} + +# Parse command-line arguments. +my $update; +Getopt::Long::config('bundling', 'no_ignore_case'); +GetOptions('update|u' => \$update) or exit 1; + +# If we're not updating, set up for Automake testing. Otherwise, we assume +# we're running from the top of the source tree. +if (!$update) { + automake_setup(); +} + +# Get the package name and version. +my ($version, $package) = news_version(); + +# rra-c-util itself checks the versions of the testing support modules instead +# of an embedded tree of Perl modules. +my $root = ($package eq 'rra-c-util') ? 'tests/tap/perl' : 'perl/lib'; + +# Main routine. We run as either a test suite or as a script to update all of +# the module versions, selecting based on whether we got the -u / --update +# command-line option. +if ($update) { + update_module_versions($root, $version); +} else { + skip_unless_automated('Module version tests'); + test_module_versions($root, $version); +} +exit 0; +__END__ + +=for stopwords +Allbery sublicense MERCHANTABILITY NONINFRINGEMENT rra-c-util + +=head1 NAME + +module-version-t - Check or update versions of embedded Perl modules + +=head1 SYNOPSIS + +B<module-version-t> [B<--update>] + +=head1 REQUIREMENTS + +Perl 5.6.2 or later. + +=head1 DESCRIPTION + +This script has a dual purpose as either a test script or a utility script. +The intent is to assist with maintaining consistent versions between a larger +primarily C project and any embedded Perl modules, supporting both the package +keyword syntax introduced in Perl 5.12 or the older explicit setting of a +$VERSION variable. + +As a test, it reads the current version of a package from the F<NEWS> file and +then looks for any Perl modules in F<perl/lib>. (As a special exception, if +the package name as determined from the F<NEWS> file is C<rra-c-util>, it +looks for Perl modules in F<tests/tap/perl> instead.) If it finds any, it +checks that the version number of the Perl module matches the version number +of the package from the F<NEWS> file. These test results are reported with +Test::More, suitable for any TAP harness. + +As a utility script, when run with the B<--update> option, it similarly finds +all Perl modules in F<perl/lib> (or F<tests/tap/perl> per above) and then +rewrites their version setting to match the version of the package as +determined from the F<NEWS> file. + +=head1 OPTIONS + +=over 4 + +=item B<-u>, B<--update> + +Rather than test the Perl modules for the correct version, update all Perl +modules found in the tree under F<perl/lib> to the current version from the +NEWS file. + +=back + +=head1 AUTHOR + +Russ Allbery <eagle@eyrie.org> + +=head1 COPYRIGHT AND LICENSE + +Copyright 2014, 2016 Russ Allbery <eagle@eyrie.org> + +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 + +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/portable/asprintf-t.c b/tests/portable/asprintf-t.c index c61c14a..e556d95 100644 --- a/tests/portable/asprintf-t.c +++ b/tests/portable/asprintf-t.c @@ -16,6 +16,7 @@ */ #include <config.h> +#include <portable/macros.h> #include <portable/system.h> #include <tests/tap/basic.h> diff --git a/tests/portable/snprintf-t.c b/tests/portable/snprintf-t.c index 270d2e1..cc8cf00 100644 --- a/tests/portable/snprintf-t.c +++ b/tests/portable/snprintf-t.c @@ -26,7 +26,9 @@ * Disable the requirement that format strings be literals. We need variable * formats for easy testing. */ -#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 2) || defined(__clang__) +# pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif /* * Intentionally don't add the printf attribute here since we pass a diff --git a/tests/portable/strlcat-t.c b/tests/portable/strlcat-t.c deleted file mode 100644 index 58aba58..0000000 --- a/tests/portable/strlcat-t.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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 <eagle@eyrie.org> - * - * 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> -#include <portable/system.h> - -#include <tests/tap/basic.h> - -size_t test_strlcat(char *, const char *, size_t); - - -int -main(void) -{ - char buffer[10] = ""; - - plan(27); - - is_int(3, test_strlcat(buffer, "foo", sizeof(buffer)), - "strlcat into empty buffer"); - is_string("foo", buffer, "...with right output"); - is_int(7, test_strlcat(buffer, " bar", sizeof(buffer)), - "...and append more"); - is_string("foo bar", buffer, "...and output is still correct"); - is_int(9, test_strlcat(buffer, "!!", sizeof(buffer)), - "...and append to buffer limit"); - is_string("foo bar!!", buffer, "...output is still correct"); - is_int(10, test_strlcat(buffer, "!", sizeof(buffer)), - "...append one more character"); - is_string("foo bar!!", buffer, "...and output didn't change"); - ok(buffer[9] == '\0', "...buffer still nul-terminated"); - buffer[0] = '\0'; - is_int(11, test_strlcat(buffer, "hello world", sizeof(buffer)), - "append single long string"); - is_string("hello wor", buffer, "...string truncates properly"); - ok(buffer[9] == '\0', "...buffer still nul-terminated"); - buffer[0] = '\0'; - is_int(7, test_strlcat(buffer, "sausage", 5), "lie about buffer length"); - is_string("saus", buffer, "...contents are correct"); - is_int(14, test_strlcat(buffer, "bacon eggs", sizeof(buffer)), - "...add more up to real size"); - is_string("sausbacon", buffer, "...and result is correct"); - - /* Make sure that with a size of 0, the destination isn't changed. */ - is_int(11, test_strlcat(buffer, "!!", 0), "no change with size of 0"); - is_string("sausbacon", buffer, "...and content is the same"); - - /* Now play with empty strings. */ - is_int(9, test_strlcat(buffer, "", 0), - "correct count when appending empty string"); - is_string("sausbacon", buffer, "...and contents are unchanged"); - buffer[0] = '\0'; - is_int(0, test_strlcat(buffer, "", sizeof(buffer)), - "correct count when appending empty string to empty buffer"); - is_string("", buffer, "...and buffer content is correct"); - is_int(3, test_strlcat(buffer, "foo", 2), "append to length 2 buffer"); - is_string("f", buffer, "...and got only a single character"); - ok(buffer[1] == '\0', "...and buffer is still nul-terminated"); - is_int(1, test_strlcat(buffer, "", sizeof(buffer)), - "append an empty string"); - ok(buffer[1] == '\0', "...and buffer is still nul-terminated"); - - return 0; -} diff --git a/tests/portable/strlcat.c b/tests/portable/strlcat.c deleted file mode 100644 index 8983bd8..0000000 --- a/tests/portable/strlcat.c +++ /dev/null @@ -1,2 +0,0 @@ -#define TESTING 1 -#include <portable/strlcat.c> diff --git a/tests/portable/strlcpy-t.c b/tests/portable/strlcpy-t.c deleted file mode 100644 index 6652a7c..0000000 --- a/tests/portable/strlcpy-t.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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 <eagle@eyrie.org> - * - * 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> -#include <portable/system.h> - -#include <tests/tap/basic.h> - -size_t test_strlcpy(char *, const char *, size_t); - - -int -main(void) -{ - char buffer[10]; - - plan(23); - - is_int(3, test_strlcpy(buffer, "foo", sizeof(buffer)), "simple strlcpy"); - is_string("foo", buffer, "...result is correct"); - is_int(9, test_strlcpy(buffer, "hello wor", sizeof(buffer)), - "strlcpy exact length of buffer"); - is_string("hello wor", buffer, "...result is correct"); - is_int(10, test_strlcpy(buffer, "world hell", sizeof(buffer)), - "strlcpy one more than buffer length"); - is_string("world hel", buffer, "...result is correct"); - ok(buffer[9] == '\0', "...buffer is nul-terminated"); - is_int(11, test_strlcpy(buffer, "hello world", sizeof(buffer)), - "strlcpy more than buffer length"); - is_string("hello wor", buffer, "...result is correct"); - ok(buffer[9] == '\0', "...buffer is nul-terminated"); - - /* Make sure that with a size of 0, the destination isn't changed. */ - is_int(3, test_strlcpy(buffer, "foo", 0), "buffer unchanged if size 0"); - is_string("hello wor", buffer, "...contents still the same"); - - /* Now play with empty strings. */ - is_int(0, test_strlcpy(buffer, "", 0), "copy empty string with size 0"); - is_string("hello wor", buffer, "...buffer unchanged"); - is_int(0, test_strlcpy(buffer, "", sizeof(buffer)), - "copy empty string into full buffer"); - is_string("", buffer, "...buffer now empty string"); - is_int(3, test_strlcpy(buffer, "foo", 2), - "copy string into buffer of size 2"); - is_string("f", buffer, "...got one character"); - ok(buffer[1] == '\0', "...buffer is nul-terminated"); - is_int(0, test_strlcpy(buffer, "", 1), - "copy empty string into buffer of size 1"); - ok(buffer[0] == '\0', "...buffer is empty string"); - - /* Finally, check using strlcpy as strlen. */ - is_int(3, test_strlcpy(NULL, "foo", 0), "use strlcpy as strlen"); - is_int(11, test_strlcpy(NULL, "hello world", 0), "...again"); - - return 0; -} diff --git a/tests/portable/strlcpy.c b/tests/portable/strlcpy.c deleted file mode 100644 index d444595..0000000 --- a/tests/portable/strlcpy.c +++ /dev/null @@ -1,2 +0,0 @@ -#define TESTING 1 -#include <portable/strlcpy.c> diff --git a/tests/runtests.c b/tests/runtests.c index a9d2373..42a73ea 100644 --- a/tests/runtests.c +++ b/tests/runtests.c @@ -3,15 +3,19 @@ * * Usage: * - * runtests [-b <build-dir>] [-s <source-dir>] <test-list> - * runtests -o [-b <build-dir>] [-s <source-dir>] <test> + * runtests [-hv] [-b <build-dir>] [-s <source-dir>] -l <test-list> + * runtests [-hv] [-b <build-dir>] [-s <source-dir>] <test> [<test> ...] + * runtests -o [-h] [-b <build-dir>] [-s <source-dir>] <test> * * 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: + * reporting results. In the second case, use the same infrastructure, but + * run only the tests listed on the command line. + * + * 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> @@ -50,12 +54,16 @@ * directories. These paths can also be set with the -b and -s command-line * options, which will override anything set at build time. * + * If the -v option is given, or the C_TAP_VERBOSE environment variable is set, + * display the full output of each test as it runs rather than showing a + * summary of the results of each test. + * * Any bug reports, bug fixes, and improvements are very much welcome and * 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, 2011, 2012, 2013, - * 2014 Russ Allbery <eagle@eyrie.org> + * 2014, 2015 Russ Allbery <eagle@eyrie.org> * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -146,6 +154,12 @@ enum test_status { TEST_INVALID }; +/* Really, just a boolean, but this is more self-documenting. */ +enum test_verbose { + CONCISE = 0, + VERBOSE = 1 +}; + /* Indicates the state of our plan. */ enum plan_status { PLAN_INIT, /* Nothing seen yet. */ @@ -192,16 +206,18 @@ struct testlist { * split into variables to satisfy the pedantic ISO C90 limit on strings. */ static const char usage_message[] = "\ -Usage: %s [-b <build-dir>] [-s <source-dir>] <test> ...\n\ - %s [-b <build-dir>] [-s <source-dir>] -l <test-list>\n\ - %s -o [-b <build-dir>] [-s <source-dir>] <test>\n\ -\n%s"; -static const char usage_extra[] = "\ +Usage: %s [-hv] [-b <build-dir>] [-s <source-dir>] <test> ...\n\ + %s [-hv] [-b <build-dir>] [-s <source-dir>] -l <test-list>\n\ + %s -o [-h] [-b <build-dir>] [-s <source-dir>] <test>\n\ +\n\ Options:\n\ -b <build-dir> Set the build directory to <build-dir>\n\ +%s"; +static const char usage_extra[] = "\ -l <list> Take the list of tests to run from <test-list>\n\ -o Run a single test rather than a list of tests\n\ -s <source-dir> Set the source directory to <source-dir>\n\ + -v Show the full output of each test\n\ \n\ runtests normally runs each test listed on the command line. With the -l\n\ option, it instead runs every test listed in a file. With the -o option,\n\ @@ -246,8 +262,10 @@ Failed Set Fail/Total (%) Skip Stat Failing Tests\n\ * variadic macro support. */ #if !defined(__attribute__) && !defined(__alloc_size__) -# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3) -# define __alloc_size__(spec, args...) /* empty */ +# if defined(__GNUC__) && !defined(__clang__) +# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3) +# define __alloc_size__(spec, args...) /* empty */ +# endif # endif #endif @@ -591,13 +609,28 @@ resize_results(struct testset *ts, unsigned long n) /* + * Report an invalid test number and set the appropriate flags. Pulled into a + * separate function since we do this in several places. + */ +static void +invalid_test_number(struct testset *ts, long n, enum test_verbose verbose) +{ + if (!verbose) + test_backspace(ts); + printf("ABORTED (invalid test number %ld)\n", n); + ts->aborted = 1; + ts->reported = 1; +} + + +/* * Read the plan line of test output, which should contain the range of test * numbers. We may initialize the testset structure here if we haven't yet * seen a test. Return true if initialization succeeded and the test should * continue, false otherwise. */ static int -test_plan(const char *line, struct testset *ts) +test_plan(const char *line, struct testset *ts, enum test_verbose verbose) { long n; @@ -654,10 +687,7 @@ test_plan(const char *line, struct testset *ts) * range. */ if (ts->plan == PLAN_PENDING && (unsigned long) n < ts->count) { - test_backspace(ts); - printf("ABORTED (invalid test number %lu)\n", ts->count); - ts->aborted = 1; - ts->reported = 1; + invalid_test_number(ts, (long) ts->count, verbose); return 0; } @@ -665,8 +695,8 @@ test_plan(const char *line, struct testset *ts) * Otherwise, allocated or resize the results if needed and update count, * and then record that we've seen a plan. */ - resize_results(ts, n); - ts->count = n; + resize_results(ts, (unsigned long) n); + ts->count = (unsigned long) n; if (ts->plan == PLAN_INIT) ts->plan = PLAN_FIRST; else if (ts->plan == PLAN_PENDING) @@ -682,7 +712,8 @@ test_plan(const char *line, struct testset *ts) * reported status. */ static void -test_checkline(const char *line, struct testset *ts) +test_checkline(const char *line, struct testset *ts, + enum test_verbose verbose) { enum test_status status = TEST_PASS; const char *bail; @@ -701,7 +732,8 @@ test_checkline(const char *line, struct testset *ts) length = strlen(bail); if (bail[length - 1] == '\n') length--; - test_backspace(ts); + if (!verbose) + test_backspace(ts); printf("ABORTED (%.*s)\n", (int) length, bail); ts->reported = 1; } @@ -722,14 +754,15 @@ test_checkline(const char *line, struct testset *ts) /* If we haven't yet seen a plan, look for one. */ if (ts->plan == PLAN_INIT && isdigit((unsigned char)(*line))) { - if (!test_plan(line, ts)) + if (!test_plan(line, ts, verbose)) return; } else if (strncmp(line, "1..", 3) == 0) { if (ts->plan == PLAN_PENDING) { - if (!test_plan(line, ts)) + if (!test_plan(line, ts, verbose)) return; } else { - test_backspace(ts); + if (!verbose) + test_backspace(ts); puts("ABORTED (multiple plans)"); ts->aborted = 1; ts->reported = 1; @@ -748,13 +781,14 @@ test_checkline(const char *line, struct testset *ts) errno = 0; number = strtol(line, &end, 10); if (errno != 0 || end == line) - number = ts->current + 1; - current = number; - if (number <= 0 || (current > ts->count && ts->plan == PLAN_FIRST)) { - test_backspace(ts); - printf("ABORTED (invalid test number %lu)\n", current); - ts->aborted = 1; - ts->reported = 1; + current = ts->current + 1; + else if (number <= 0) { + invalid_test_number(ts, number, verbose); + return; + } else + current = (unsigned long) number; + if (current > ts->count && ts->plan == PLAN_FIRST) { + invalid_test_number(ts, (long) current, verbose); return; } @@ -783,7 +817,8 @@ test_checkline(const char *line, struct testset *ts) /* Make sure that the test number is in range and not a duplicate. */ if (ts->results[current - 1] != TEST_INVALID) { - test_backspace(ts); + if (!verbose) + test_backspace(ts); printf("ABORTED (duplicate test number %lu)\n", current); ts->aborted = 1; ts->reported = 1; @@ -799,13 +834,13 @@ test_checkline(const char *line, struct testset *ts) } ts->current = current; ts->results[current - 1] = status; - if (isatty(STDOUT_FILENO)) { + if (!verbose && isatty(STDOUT_FILENO)) { test_backspace(ts); if (ts->plan == PLAN_PENDING) outlen = printf("%lu/?", current); else outlen = printf("%lu/%lu", current, ts->count); - ts->length = (outlen >= 0) ? outlen : 0; + ts->length = (outlen >= 0) ? (unsigned int) outlen : 0; fflush(stdout); } } @@ -821,7 +856,7 @@ test_checkline(const char *line, struct testset *ts) * disable this). */ static unsigned int -test_print_range(unsigned long first, unsigned long last, unsigned int chars, +test_print_range(unsigned long first, unsigned long last, unsigned long chars, unsigned int limit) { unsigned int needed = 0; @@ -991,7 +1026,7 @@ test_analyze(struct testset *ts) * false otherwise. */ static int -test_run(struct testset *ts) +test_run(struct testset *ts, enum test_verbose verbose) { pid_t testpid, child; int outfd, status; @@ -1008,12 +1043,19 @@ test_run(struct testset *ts) sysdie("fdopen failed"); } - /* Pass each line of output to test_checkline(). */ - while (!ts->aborted && fgets(buffer, sizeof(buffer), output)) - test_checkline(buffer, ts); + /* + * Pass each line of output to test_checkline(), and print the line if + * verbosity is requested. + */ + while (!ts->aborted && fgets(buffer, sizeof(buffer), output)) { + if (verbose) + printf("%s", buffer); + test_checkline(buffer, ts, verbose); + } if (ferror(output) || ts->plan == PLAN_INIT) ts->aborted = 1; - test_backspace(ts); + if (!verbose) + test_backspace(ts); /* * Consume the rest of the test output, close the output descriptor, @@ -1021,7 +1063,8 @@ test_run(struct testset *ts) * for eventual output. */ while (fgets(buffer, sizeof(buffer), output)) - ; + if (verbose) + printf("%s", buffer); fclose(output); child = waitpid(testpid, &ts->status, 0); if (child == (pid_t) -1) { @@ -1129,7 +1172,7 @@ is_valid_test(const char *path) static char * find_test(const char *name, const char *source, const char *build) { - char *path; + char *path = NULL; const char *bases[3], *suffix, *base; unsigned int i, j; const char *suffixes[3] = { "-t", ".t", "" }; @@ -1161,7 +1204,8 @@ find_test(const char *name, const char *source, const char *build) /* * Read a list of tests from a file, returning the list of tests as a struct - * testlist. Reports an error to standard error and exits if the list of + * testlist, or NULL if there were no tests (such as a file containing only + * comments). Reports an error to standard error and exits if the list of * tests cannot be read. */ static struct testlist * @@ -1171,6 +1215,7 @@ read_test_list(const char *filename) unsigned int line; size_t length; char buffer[BUFSIZ]; + const char *testname; struct testlist *listhead, *current; /* Create the initial container list that will hold our results. */ @@ -1193,6 +1238,15 @@ read_test_list(const char *filename) exit(1); } buffer[length] = '\0'; + + /* Skip comments, leading spaces, and blank lines. */ + testname = skip_whitespace(buffer); + if (strlen(testname) == 0) + continue; + if (testname[0] == '#') + continue; + + /* Allocate the new testset structure. */ if (current == NULL) current = listhead; else { @@ -1201,10 +1255,16 @@ read_test_list(const char *filename) } current->ts = xcalloc(1, sizeof(struct testset)); current->ts->plan = PLAN_INIT; - current->ts->file = xstrdup(buffer); + current->ts->file = xstrdup(testname); } fclose(file); + /* If there were no tests, current is still NULL. */ + if (current == NULL) { + free(listhead); + return NULL; + } + /* Return the results. */ return listhead; } @@ -1213,7 +1273,8 @@ read_test_list(const char *filename) /* * Build a list of tests from command line arguments. Takes the argv and argc * representing the command line arguments and returns a newly allocated test - * list. The caller is responsible for freeing. + * list, or NULL if there were no tests. The caller is responsible for + * freeing. */ static struct testlist * build_test_list(char *argv[], int argc) @@ -1238,6 +1299,12 @@ build_test_list(char *argv[], int argc) current->ts->file = xstrdup(argv[i]); } + /* If there were no tests, current is still NULL. */ + if (current == NULL) { + free(listhead); + return NULL; + } + /* Return the results. */ return listhead; } @@ -1263,11 +1330,11 @@ free_testset(struct testset *ts) * frees the test list that's passed in. */ static int -test_batch(struct testlist *tests, const char *source, const char *build) +test_batch(struct testlist *tests, const char *source, const char *build, + enum test_verbose verbose) { - size_t length; - unsigned int i; - unsigned int longest = 0; + size_t length, i; + size_t longest = 0; unsigned int count = 0; struct testset *ts; struct timeval start, end; @@ -1306,15 +1373,20 @@ test_batch(struct testlist *tests, const char *source, const char *build) /* Print out the name of the test file. */ fputs(ts->file, stdout); - for (i = strlen(ts->file); i < longest; i++) - putchar('.'); + if (verbose) + fputs("\n\n", stdout); + else + for (i = strlen(ts->file); i < longest; i++) + putchar('.'); if (isatty(STDOUT_FILENO)) fflush(stdout); /* Run the test. */ ts->path = find_test(ts->file, source, build); - succeeded = test_run(ts); + succeeded = test_run(ts, verbose); fflush(stdout); + if (verbose) + putchar('\n'); /* Record cumulative statistics. */ aborted += ts->aborted; @@ -1416,23 +1488,25 @@ main(int argc, char *argv[]) int option; int status = 0; int single = 0; + enum test_verbose verbose = CONCISE; char *source_env = NULL; char *build_env = NULL; + const char *program; const char *shortlist; const char *list = NULL; const char *source = SOURCE; const char *build = BUILD; struct testlist *tests; - while ((option = getopt(argc, argv, "b:hl:os:")) != EOF) { + program = argv[0]; + while ((option = getopt(argc, argv, "b:hl:os:v")) != EOF) { switch (option) { case 'b': build = optarg; break; case 'h': - printf(usage_message, argv[0], argv[0], argv[0], usage_extra); + printf(usage_message, program, program, program, usage_extra); exit(0); - break; case 'l': list = optarg; break; @@ -1442,6 +1516,9 @@ main(int argc, char *argv[]) case 's': source = optarg; break; + case 'v': + verbose = VERBOSE; + break; default: exit(1); } @@ -1449,10 +1526,17 @@ main(int argc, char *argv[]) argv += optind; argc -= optind; if ((list == NULL && argc < 1) || (list != NULL && argc > 0)) { - fprintf(stderr, usage_message, argv[0], argv[0], argv[0], usage_extra); + fprintf(stderr, usage_message, program, program, program, usage_extra); exit(1); } + /* + * If C_TAP_VERBOSE is set in the environment, that also turns on verbose + * mode. + */ + if (getenv("C_TAP_VERBOSE") != NULL) + verbose = VERBOSE; + /* Set SOURCE and BUILD environment variables. */ if (source != NULL) { source_env = concat("SOURCE=", source, (const char *) 0); @@ -1476,10 +1560,10 @@ main(int argc, char *argv[]) shortlist++; printf(banner, shortlist); tests = read_test_list(list); - status = test_batch(tests, source, build) ? 0 : 1; + status = test_batch(tests, source, build, verbose) ? 0 : 1; } else { tests = build_test_list(argv, argc); - status = test_batch(tests, source, build) ? 0 : 1; + status = test_batch(tests, source, build, verbose) ? 0 : 1; } /* For valgrind cleanliness, free all our memory. */ diff --git a/tests/tap/basic.c b/tests/tap/basic.c index 92a749b..4f8be04 100644 --- a/tests/tap/basic.c +++ b/tests/tap/basic.c @@ -12,7 +12,8 @@ * 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 2009, 2010, 2011, 2012, 2013, 2014 Russ Allbery <eagle@eyrie.org> + * Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015 + * Russ Allbery <eagle@eyrie.org> * Copyright 2001, 2002, 2004, 2005, 2006, 2007, 2008, 2011, 2012, 2013, 2014 * The Board of Trustees of the Leland Stanford Junior University * @@ -105,7 +106,7 @@ static struct cleanup_func *cleanup_funcs = NULL; /* * Registered diag files. Any output found in these files will be printed out * as if it were passed to diag() before any other output we do. This allows - * background processes to log to a file and have that output interleved with + * background processes to log to a file and have that output interleaved with * the test output. */ struct diag_file { @@ -192,7 +193,7 @@ check_diag_files(void) struct diag_file *file; fpos_t where; size_t length; - int incomplete; + int size, incomplete; /* * Walk through each file and read each line of output available. The @@ -216,7 +217,8 @@ check_diag_files(void) /* Continue until we get EOF or an incomplete line of data. */ incomplete = 0; while (!feof(file->file) && !incomplete) { - if (fgets(file->buffer, file->bufsize, file->file) == NULL) { + size = file->bufsize > INT_MAX ? INT_MAX : (int) file->bufsize; + if (fgets(file->buffer, size, file->file) == NULL) { if (ferror(file->file)) sysbail("cannot read from %s", file->name); continue; @@ -807,7 +809,7 @@ bstrndup(const char *s, size_t n) /* Don't assume that the source string is nul-terminated. */ for (p = s; (size_t) (p - s) < n && *p != '\0'; p++) ; - length = p - s; + length = (size_t) (p - s); copy = malloc(length + 1); if (p == NULL) sysbail("failed to strndup %lu bytes", (unsigned long) length); diff --git a/tests/tap/basic.h b/tests/tap/basic.h index c002df9..4ecaaec 100644 --- a/tests/tap/basic.h +++ b/tests/tap/basic.h @@ -4,7 +4,8 @@ * 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 2009, 2010, 2011, 2012, 2013, 2014 Russ Allbery <eagle@eyrie.org> + * Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015 + * Russ Allbery <eagle@eyrie.org> * Copyright 2001, 2002, 2004, 2005, 2006, 2007, 2008, 2011, 2012, 2014 * The Board of Trustees of the Leland Stanford Junior University * @@ -72,7 +73,8 @@ void skip_all(const char *format, ...) */ int ok(int success, const char *format, ...) __attribute__((__format__(printf, 2, 3))); -int okv(int success, const char *format, va_list args); +int okv(int success, const char *format, va_list args) + __attribute__((__format__(printf, 2, 0))); void skip(const char *reason, ...) __attribute__((__format__(printf, 1, 2))); @@ -155,7 +157,7 @@ void test_tmpdir_free(char *path); * registered functions will be run during atexit handling (and are therefore * subject to all the same constraints and caveats as atexit functions). * - * The function must return void and will be passed two argument, an int that + * The function must return void and will be passed two arguments: an int that * will be true if the test completed successfully and false otherwise, and an * int that will be true if the cleanup function is run in the primary process * (the one that called plan or plan_lazy) and false otherwise. diff --git a/tests/tap/kerberos.c b/tests/tap/kerberos.c index 578a858..6a5025a 100644 --- a/tests/tap/kerberos.c +++ b/tests/tap/kerberos.c @@ -55,7 +55,9 @@ * 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" +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 2) || defined(__clang__) +# pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif /* @@ -219,6 +221,8 @@ kerberos_free(void) free(config->userprinc); free(config->username); free(config->password); + free(config->pkinit_principal); + free(config->pkinit_cert); free(config); config = NULL; } @@ -351,6 +355,31 @@ kerberos_setup(enum kerberos_needs needs) test_file_path_free(path); /* + * If we have PKINIT configuration, read it and fill out the relevant + * members of our config struct. + */ + path = test_file_path("config/pkinit-principal"); + if (path != NULL) + file = fopen(path, "r"); + if (file != NULL) { + 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'; + fclose(file); + test_file_path_free(path); + path = test_file_path("config/pkinit-cert"); + if (path != NULL) { + config->pkinit_principal = bstrdup(buffer); + config->pkinit_cert = bstrdup(path); + } + } + test_file_path_free(path); + if (config->pkinit_cert == NULL && (needs & TAP_KRB_NEEDS_PKINIT) != 0) + skip_all("PKINIT tests not configured"); + + /* * Register the cleanup function so that the caller doesn't have to do * explicit cleanup. */ diff --git a/tests/tap/kerberos.h b/tests/tap/kerberos.h index 8be0add..26f45f9 100644 --- a/tests/tap/kerberos.h +++ b/tests/tap/kerberos.h @@ -46,17 +46,21 @@ struct kerberos_config { char *username; /* The local (non-realm) part of principal. */ char *realm; /* The realm part of the principal. */ char *password; /* The password. */ + char *pkinit_principal; /* Principal for PKINIT authentication. */ + char *pkinit_cert; /* Path to certificates for PKINIT. */ }; /* * Whether to skip all tests (by calling skip_all) in kerberos_setup if - * certain configuration information isn't available. + * certain configuration information isn't available. "_BOTH" means that the + * tests require both keytab and password, but PKINIT is not required. */ enum kerberos_needs { TAP_KRB_NEEDS_NONE = 0x00, TAP_KRB_NEEDS_KEYTAB = 0x01, TAP_KRB_NEEDS_PASSWORD = 0x02, - TAP_KRB_NEEDS_BOTH = 0x01 | 0x02 + TAP_KRB_NEEDS_BOTH = 0x01 | 0x02, + TAP_KRB_NEEDS_PKINIT = 0x04 }; BEGIN_DECLS diff --git a/tests/tap/macros.h b/tests/tap/macros.h index 04cc420..139cff0 100644 --- a/tests/tap/macros.h +++ b/tests/tap/macros.h @@ -8,7 +8,7 @@ * 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, 2013 Russ Allbery <eagle@eyrie.org> + * Copyright 2008, 2012, 2013, 2015 Russ Allbery <eagle@eyrie.org> * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -53,8 +53,10 @@ * variadic macro support. */ #if !defined(__attribute__) && !defined(__alloc_size__) -# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3) -# define __alloc_size__(spec, args...) /* empty */ +# if defined(__GNUC__) && !defined(__clang__) +# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3) +# define __alloc_size__(spec, args...) /* empty */ +# endif # endif #endif diff --git a/tests/tap/messages.c b/tests/tap/messages.c index 45b0566..9c28789 100644 --- a/tests/tap/messages.c +++ b/tests/tap/messages.c @@ -8,7 +8,7 @@ * 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, 2004, 2005 Russ Allbery <eagle@eyrie.org> + * Copyright 2002, 2004, 2005, 2015 Russ Allbery <eagle@eyrie.org> * Copyright 2006, 2007, 2009, 2012, 2014 * The Board of Trustees of the Leland Stanford Junior University * @@ -47,7 +47,7 @@ char *errors = NULL; * An error handler that appends all errors to the errors global. Used by * error_capture. */ -static void +static void __attribute__((__format__(printf, 2, 0))) message_log_buffer(int len UNUSED, const char *fmt, va_list args, int error UNUSED) { diff --git a/tests/tap/perl/Test/RRA.pm b/tests/tap/perl/Test/RRA.pm index bb7de7d..8608e31 100644 --- a/tests/tap/perl/Test/RRA.pm +++ b/tests/tap/perl/Test/RRA.pm @@ -5,31 +5,6 @@ # 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 <eagle@eyrie.org> -# Copyright 2013, 2014 -# 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; @@ -56,7 +31,7 @@ BEGIN { # 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 = '5.05'; + $VERSION = '5.10'; } # Skip this test unless author tests are requested. Takes a short description @@ -153,7 +128,7 @@ __END__ =for stopwords Allbery Allbery's DESC bareword sublicense MERCHANTABILITY NONINFRINGEMENT -rra-c-util +rra-c-util CPAN =head1 NAME @@ -176,46 +151,45 @@ Test::RRA - Support functions for Perl tests =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. +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. +None of these functions are imported by default. The ones used by a script +should be explicitly imported. =over 4 =item skip_unless_author(DESC) -Checks whether AUTHOR_TESTING 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 author> will be appended to it and used as the skip reason. +Checks whether AUTHOR_TESTING 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 author> +will be appended to it and used as the skip reason. =item skip_unless_automated(DESC) -Checks whether AUTHOR_TESTING, AUTOMATED_TESTING, or RELEASE_TESTING are -set in the environment and skips the whole test (by calling C<plan -skip_all> from Test::More) if they are not. This should be used by tests -that should not run during end-user installs of the module, but which -should run as part of CPAN smoke testing and release testing. +Checks whether AUTHOR_TESTING, AUTOMATED_TESTING, or RELEASE_TESTING are set +in the environment and skips the whole test (by calling C<plan skip_all> from +Test::More) if they are not. This should be used by tests that should not run +during end-user installs of the module, but which should run as part of CPAN +smoke testing and release testing. DESC is a description of the tests being skipped. A space and C<normally skipped> 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. +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. +version number. The remaining IMPORT arguments will be passed as the value of +an array. =back @@ -228,33 +202,33 @@ Russ Allbery <eagle@eyrie.org> Copyright 2013, 2014 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: +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 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. +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/>. +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 functions to control when tests are run use environment variables -defined by the L<Lancaster +The functions to control when tests are run use environment variables defined +by the L<Lancaster Consensus|https://github.com/Perl-Toolchain-Gang/toolchain-site/blob/master/lancaster-consensus.md>. =cut diff --git a/tests/tap/perl/Test/RRA/Automake.pm b/tests/tap/perl/Test/RRA/Automake.pm index a064ed9..c6399ec 100644 --- a/tests/tap/perl/Test/RRA/Automake.pm +++ b/tests/tap/perl/Test/RRA/Automake.pm @@ -9,31 +9,6 @@ # # 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 <eagle@eyrie.org> -# 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; @@ -87,13 +62,13 @@ BEGIN { # 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 = '5.05'; + $VERSION = '5.10'; } # 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); +my @GLOBAL_SKIP = qw(.git _build perl); # The temporary directory created by test_tmpdir, if any. If this is set, # attempt to remove the directory stored here on program exit (but ignore @@ -126,7 +101,15 @@ sub automake_setup { my ($vol, $dirs) = File::Spec->splitpath($start, 1); my @dirs = File::Spec->splitdir($dirs); pop(@dirs); - if ($dirs[-1] eq File::Spec->updir) { + + # Simplify relative paths at the end of the directory. + my $ups = 0; + my $i = $#dirs; + while ($i > 2 && $dirs[$i] eq File::Spec->updir) { + $ups++; + $i--; + } + for (1 .. $ups) { pop(@dirs); pop(@dirs); } @@ -196,7 +179,7 @@ sub perl_dirs { # 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); + my @dirs = grep { -d && !$skip{$_} } readdir($rootdir); closedir($rootdir); @dirs = File::Spec->no_upwards(@dirs); @@ -288,8 +271,8 @@ END { __END__ =for stopwords -Allbery Automake Automake-aware Automake-based rra-c-util ARGS -subdirectories sublicense MERCHANTABILITY NONINFRINGEMENT umask +Allbery Automake Automake-aware Automake-based rra-c-util ARGS subdirectories +sublicense MERCHANTABILITY NONINFRINGEMENT umask =head1 NAME @@ -309,75 +292,71 @@ Test::RRA::Automake - Automake-aware support functions for Perl tests =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. +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. +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). +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). +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. +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. +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. +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. =item test_tmpdir() -Create a temporary directory for tests to use for transient files and -return the path to that directory. The directory is created relative to -the BUILD environment variable, which must be set. Permissions on the -directory are set using the current umask. test_tmpdir() returns the full -path to the temporary directory or calls BAIL_OUT if it could not be -created. +Create a temporary directory for tests to use for transient files and return +the path to that directory. The directory is created relative to the BUILD +environment variable, which must be set. Permissions on the directory are set +using the current umask. test_tmpdir() returns the full path to the temporary +directory or calls BAIL_OUT if it could not be created. -The directory is automatically removed if possible on program exit. -Failure to remove the directory on exit is reported with diag() and -otherwise ignored. +The directory is automatically removed if possible on program exit. Failure +to remove the directory on exit is reported with diag() and otherwise ignored. =back @@ -387,35 +366,36 @@ Russ Allbery <eagle@eyrie.org> =head1 COPYRIGHT AND LICENSE -Copyright 2013 The Board of Trustees of the Leland Stanford Junior -University +Copyright 2014, 2015 Russ Allbery <eagle@eyrie.org> + +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: +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 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. +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) +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/>. -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 index 3e77650..a5b0d0d 100644 --- a/tests/tap/perl/Test/RRA/Config.pm +++ b/tests/tap/perl/Test/RRA/Config.pm @@ -4,9 +4,6 @@ # 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; @@ -30,22 +27,23 @@ BEGIN { @ISA = qw(Exporter); @EXPORT_OK = qw( $COVERAGE_LEVEL @COVERAGE_SKIP_TESTS @CRITIC_IGNORE $LIBRARY_PATH - $MINIMUM_VERSION %MINIMUM_VERSION @POD_COVERAGE_EXCLUDE @STRICT_IGNORE - @STRICT_PREREQ + $MINIMUM_VERSION %MINIMUM_VERSION @MODULE_VERSION_IGNORE + @POD_COVERAGE_EXCLUDE @STRICT_IGNORE @STRICT_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 = '5.05'; + $VERSION = '5.10'; } # 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. +# for a standalone Perl module or tests/data/perl.conf for Perl tests embedded +# in a larger distribution. Don't use Test::RRA::Automake since it may not +# exist. our $PATH; -for my $base ($ENV{BUILD}, $ENV{SOURCE}, 't') { +for my $base ($ENV{BUILD}, $ENV{SOURCE}, 't', 'tests') { next if !defined($base); my $path = "$base/data/perl.conf"; if (-r $path) { @@ -64,6 +62,7 @@ our @CRITIC_IGNORE; our $LIBRARY_PATH; our $MINIMUM_VERSION = '5.008'; our %MINIMUM_VERSION; +our @MODULE_VERSION_IGNORE; our @POD_COVERAGE_EXCLUDE; our @STRICT_IGNORE; our @STRICT_PREREQ; @@ -78,8 +77,8 @@ if (!do($PATH)) { __END__ =for stopwords -Allbery rra-c-util Automake perlcritic .libs namespace subdirectory -sublicense MERCHANTABILITY NONINFRINGEMENT +Allbery rra-c-util Automake perlcritic .libs namespace subdirectory sublicense +MERCHANTABILITY NONINFRINGEMENT regexes =head1 NAME @@ -92,19 +91,17 @@ Test::RRA::Config - Perl test configuration =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 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. +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: @@ -112,70 +109,75 @@ The following variables are supported: =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. +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. +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 +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 F<.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. +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. +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. +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 @MODULE_VERSION_IGNORE + +File names to ignore when checking that all modules in a distribution have the +same version. Sometimes, some specific modules need separate, special version +handling, such as modules defining database schemata for DBIx::Class, and +can't follow the version of the larger package. =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. +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. =item @STRICT_IGNORE -Additional directories to ignore when doing recursive Test::Strict testing -for C<use strict> and C<use warnings>. The contents of this directory -must be either top-level directory names or directory names starting with -F<tests/>. +Additional directories to ignore when doing recursive Test::Strict testing for +C<use strict> and C<use warnings>. The contents of this directory must be +either top-level directory names or directory names starting with F<tests/>. =item @STRICT_PREREQ A list of Perl modules that have to be available in order to do meaningful Test::Strict testing. If any of the modules cannot be loaded via C<use>, -Test::Strict checking will be skipped. There is currently no way to -require specific versions of the modules. +Test::Strict checking will be skipped. There is currently no way to require +specific versions of the modules. =back -No variables are exported by default, but the variables can be imported -into the local namespace to avoid long variable names. +No variables are exported by default, but the variables can be imported into +the local namespace to avoid long variable names. =head1 AUTHOR @@ -186,31 +188,31 @@ Russ Allbery <eagle@eyrie.org> Copyright 2013, 2014 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: +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 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. +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 -perlcritic(1), Test::MinimumVersion(3), Test::RRA(3), -Test::RRA::Automake(3), Test::Strict(3) +perlcritic(1), Test::MinimumVersion(3), Test::RRA(3), Test::RRA::Automake(3), +Test::Strict(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/>. +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/>. diff --git a/tests/tap/perl/Test/RRA/ModuleVersion.pm b/tests/tap/perl/Test/RRA/ModuleVersion.pm new file mode 100644 index 0000000..f02877a --- /dev/null +++ b/tests/tap/perl/Test/RRA/ModuleVersion.pm @@ -0,0 +1,295 @@ +# Check Perl module versions for consistency. +# +# This module contains the common code for testing and updating Perl module +# versions for consistency within a Perl module distribution and within a +# larger package that contains both Perl modules and other code. + +package Test::RRA::ModuleVersion; + +use 5.006; +use strict; +use warnings; + +use Exporter; +use File::Find qw(find); +use Test::More; +use Test::RRA::Config qw(@MODULE_VERSION_IGNORE); + +# 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(test_module_versions update_module_versions); + + # 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 = '5.10'; +} + +# A regular expression matching the version string for a module using the +# package syntax from Perl 5.12 and later. $1 will contain all of the line +# contents prior to the actual version string, $2 will contain the version +# itself, and $3 will contain the rest of the line. +our $REGEX_VERSION_PACKAGE = qr{ + ( # prefix ($1) + \A \s* # whitespace + package \s+ # package keyword + [\w\:\']+ \s+ # package name + ) + ( v? [\d._]+ ) # the version number itself ($2) + ( # suffix ($3) + \s* ; + ) +}xms; + +# A regular expression matching a $VERSION string in a module. $1 will +# contain all of the line contents prior to the actual version string, $2 will +# contain the version itself, and $3 will contain the rest of the line. +our $REGEX_VERSION_OLD = qr{ + ( # prefix ($1) + \A .* # any prefix, such as "our" + [\$*] # scalar or typeglob + [\w\:\']*\b # optional package name + VERSION\b # version variable + \s* = \s* # assignment + ) + [\"\']? # optional leading quote + ( v? [\d._]+ ) # the version number itself ($2) + [\"\']? # optional trailing quote + ( # suffix ($3) + \s* + ; + ) +}xms; + +# Find all the Perl modules shipped in this package, if any, and returns the +# list of file names. +# +# $dir - The root directory to search +# +# Returns: List of file names +sub _module_files { + my ($dir) = @_; + return if !-d $dir; + my @files; + my %ignore = map { $_ => 1 } @MODULE_VERSION_IGNORE; + my $wanted = sub { + if ($_ eq 'blib') { + $File::Find::prune = 1; + return; + } + if (m{ [.] pm \z }xms && !$ignore{$File::Find::name}) { + push(@files, $File::Find::name); + } + return; + }; + find($wanted, $dir); + return @files; +} + +# Given a module file, read it for the version value and return the value. +# +# $file - File to check, which should be a Perl module +# +# Returns: The version of the module +# Throws: Text exception on I/O failure or inability to find version +sub _module_version { + my ($file) = @_; + open(my $data, q{<}, $file) or die "$0: cannot open $file: $!\n"; + while (defined(my $line = <$data>)) { + if ( $line =~ $REGEX_VERSION_PACKAGE + || $line =~ $REGEX_VERSION_OLD) + { + my ($prefix, $version, $suffix) = ($1, $2, $3); + close($data) or die "$0: error reading from $file: $!\n"; + return $version; + } + } + close($data) or die "$0: error reading from $file: $!\n"; + die "$0: cannot find version number in $file\n"; +} + +# Given a module file and the new version for that module, update the version +# in that module to the new one. +# +# $file - Perl module file whose version should be updated +# $version - The new version number +# +# Returns: undef +# Throws: Text exception on I/O failure or inability to find version +sub _update_module_version { + my ($file, $version) = @_; + open(my $in, q{<}, $file) or die "$0: cannot open $file: $!\n"; + open(my $out, q{>}, "$file.new") + or die "$0: cannot create $file.new: $!\n"; + + # If the version starts with v, use it without quotes. Otherwise, quote + # it to prevent removal of trailing zeroes. + if ($version !~ m{ \A v }xms) { + $version = "'$version'"; + } + + # Scan for the version and replace it. + SCAN: + while (defined(my $line = <$in>)) { + if ( $line =~ s{ $REGEX_VERSION_PACKAGE }{$1$version$3}xms + || $line =~ s{ $REGEX_VERSION_OLD }{$1$version$3}xms) + { + print {$out} $line or die "$0: cannot write to $file.new: $!\n"; + last SCAN; + } + print {$out} $line or die "$0: cannot write to $file.new: $!\n"; + } + + # Copy the rest of the input file to the output file. + print {$out} <$in> or die "$0: cannot write to $file.new: $!\n"; + close($out) or die "$0: cannot flush $file.new: $!\n"; + close($in) or die "$0: error reading from $file: $!\n"; + + # All done. Rename the new file over top of the old file. + rename("$file.new", $file) + or die "$0: cannot rename $file.new to $file: $!\n"; + return; +} + +# Act as a test suite. Find all of the Perl modules under the provided root, +# if any, and check that the version for each module matches the version. +# Reports results with Test::More and sets up a plan based on the number of +# modules found. +# +# $root - Directory under which to look for Perl modules +# $version - The version all those modules should have +# +# Returns: undef +# Throws: Text exception on fatal errors +sub test_module_versions { + my ($root, $version) = @_; + my @modules = _module_files($root); + + # Output the plan. Skip the test if there were no modules found. + if (@modules) { + plan tests => scalar(@modules); + } else { + plan skip_all => 'No Perl modules found'; + return; + } + + # For each module, get the module version and compare. + for my $module (@modules) { + my $module_version = _module_version($module); + is($module_version, $version, "Version for $module"); + } + return; +} + +# Update the versions of all modules to the current distribution version. +# +# $root - Directory under which to look for Perl modules +# $version - The version all those modules should have +# +# Returns: undef +# Throws: Text exception on fatal errors +sub update_module_versions { + my ($root, $version) = @_; + my @modules = _module_files($root); + for my $module (@modules) { + _update_module_version($module, $version); + } + return; +} + +1; +__END__ + +=for stopwords +Allbery sublicense MERCHANTABILITY NONINFRINGEMENT rra-c-util versioning + +=head1 NAME + +Test::RRA::ModuleVersion - Check Perl module versions for consistency + +=head1 SYNOPSIS + + use Test::RRA::ModuleVersion + qw(test_module_versions update_module_versions); + + # Ensure all modules under perl/lib have a version of 3.12. + test_module_versions('perl/lib', '3.12'); + + # Update the version of those modules to 3.12. + update_module_versions('perl/lib', 3.12'); + +=head1 DESCRIPTION + +This module provides functions to test and update the versions of Perl +modules. It helps with enforcing consistency of versioning across all modules +in a Perl distribution or embedded in a larger project containing non-Perl +code. The calling script provides the version with which to be consistent +and the root directory under which modules are found. + +=head1 FUNCTIONS + +None of these functions are imported by default. The ones used by a script +should be explicitly imported. + +=over 4 + +=item test_module_versions(ROOT, VERSION) + +Tests the version of all Perl modules under ROOT to ensure they match VERSION, +reporting the results with Test::More. If the test configuration loaded by +Test::RRA::Config contains a @MODULE_VERSION_EXCLUDE variable, the module +files listed there will be ignored for this test. This function also sets up +a plan based on the number of modules, so should be the only testing function +called in a test script. + +=item update_module_versions(ROOT, VERSION) + +Update the version of all Perl modules found under ROOT to VERSION, except for +any listed in a @MODULE_VERSION_EXCLUDE variable set in the test configuration +loaded by Test::RRA::Config. + +=back + +=head1 AUTHOR + +Russ Allbery <eagle@eyrie.org> + +=head1 COPYRIGHT AND LICENSE + +Copyright 2016 Russ Allbery <eagle@eyrie.org> + +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::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/process.c b/tests/tap/process.c index 6461fb4..8c22324 100644 --- a/tests/tap/process.c +++ b/tests/tap/process.c @@ -47,8 +47,11 @@ # include <sys/select.h> #endif #include <sys/stat.h> -#include <sys/time.h> +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#endif #include <sys/wait.h> +#include <time.h> #include <tests/tap/basic.h> #include <tests/tap/process.h> @@ -229,6 +232,10 @@ process_free(struct process *process) { struct process **prev; + /* Do nothing if called with a NULL argument. */ + if (process == NULL) + return; + /* Remove the process from the global list. */ prev = &processes; while (*prev != NULL && *prev != process) diff --git a/tests/tap/string.h b/tests/tap/string.h index cc51945..d58f75d 100644 --- a/tests/tap/string.h +++ b/tests/tap/string.h @@ -42,7 +42,7 @@ BEGIN_DECLS void basprintf(char **, const char *, ...) __attribute__((__nonnull__, __format__(printf, 2, 3))); void bvasprintf(char **, const char *, va_list) - __attribute__((__nonnull__)); + __attribute__((__nonnull__, __format__(printf, 2, 0))); END_DECLS diff --git a/tests/util/messages-t.c b/tests/util/messages-t.c index f60fa6a..1098314 100644 --- a/tests/util/messages-t.c +++ b/tests/util/messages-t.c @@ -5,7 +5,7 @@ * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>. * * Written by Russ Allbery <eagle@eyrie.org> - * Copyright 2002, 2004, 2005 Russ Allbery <eagle@eyrie.org> + * Copyright 2002, 2004, 2005, 2015 Russ Allbery <eagle@eyrie.org> * Copyright 2009, 2010, 2011, 2012 * The Board of Trustees of the Leland Stanford Junior University * @@ -92,7 +92,8 @@ static void test11(void *data UNUSED) { sysdie("fatal"); } -static void log_msg(size_t len, const char *format, va_list args, int error) { +static void __attribute__((__format__(printf, 2, 0))) +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"); diff --git a/tests/util/xmalloc-t b/tests/util/xmalloc-t index d52c448..af604ed 100755 --- a/tests/util/xmalloc-t +++ b/tests/util/xmalloc-t @@ -99,46 +99,46 @@ ok_xmalloc "vasprintf large" 0 "" "v" "30000000" "0" # 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 30000000 bytes at xmalloc.c line 38" \ + "failed to malloc 30000000 bytes at xmalloc.c line 41" \ "m" "30000000" "30000000" ok_xmalloc "realloc fail" 1 \ - "failed to realloc 30000000 bytes at xmalloc.c line 66" \ + "failed to realloc 30000000 bytes at xmalloc.c line 69" \ "r" "30000000" "30000000" ok_xmalloc "reallocarray fail" 1 \ - "failed to reallocarray 30000000 bytes at xmalloc.c line 96" \ + "failed to reallocarray 30000000 bytes at xmalloc.c line 99" \ "y" "30000000" "30000000" ok_xmalloc "strdup fail" 1 \ - "failed to strdup 30000000 bytes at xmalloc.c line 127" \ + "failed to strdup 30000000 bytes at xmalloc.c line 130" \ "s" "30000000" "30000000" ok_xmalloc "strndup fail" 1 \ - "failed to strndup 30000000 bytes at xmalloc.c line 173" \ + "failed to strndup 30000000 bytes at xmalloc.c line 176" \ "n" "30000000" "30000000" ok_xmalloc "calloc fail" 1 \ - "failed to calloc 30000000 bytes at xmalloc.c line 197" \ + "failed to calloc 30000000 bytes at xmalloc.c line 200" \ "c" "30000000" "30000000" ok_xmalloc "asprintf fail" 1 \ - "failed to asprintf 30000000 bytes at xmalloc.c line 221" \ + "failed to asprintf 30000000 bytes at xmalloc.c line 224" \ "a" "30000000" "30000000" ok_xmalloc "vasprintf fail" 1 \ - "failed to vasprintf 30000000 bytes at xmalloc.c line 240" \ + "failed to vasprintf 30000000 bytes at xmalloc.c line 243" \ "v" "30000000" "30000000" # Check our custom error handler. -ok_xmalloc "malloc custom" 1 "malloc 30000000 xmalloc.c 38" \ +ok_xmalloc "malloc custom" 1 "malloc 30000000 xmalloc.c 41" \ "M" "30000000" "30000000" -ok_xmalloc "realloc custom" 1 "realloc 30000000 xmalloc.c 66" \ +ok_xmalloc "realloc custom" 1 "realloc 30000000 xmalloc.c 69" \ "R" "30000000" "30000000" -ok_xmalloc "reallocarray custom" 1 "reallocarray 30000000 xmalloc.c 96" \ +ok_xmalloc "reallocarray custom" 1 "reallocarray 30000000 xmalloc.c 99" \ "Y" "30000000" "30000000" -ok_xmalloc "strdup custom" 1 "strdup 30000000 xmalloc.c 127" \ +ok_xmalloc "strdup custom" 1 "strdup 30000000 xmalloc.c 130" \ "S" "30000000" "30000000" -ok_xmalloc "strndup custom" 1 "strndup 30000000 xmalloc.c 173" \ +ok_xmalloc "strndup custom" 1 "strndup 30000000 xmalloc.c 176" \ "N" "30000000" "30000000" -ok_xmalloc "calloc custom" 1 "calloc 30000000 xmalloc.c 197" \ +ok_xmalloc "calloc custom" 1 "calloc 30000000 xmalloc.c 200" \ "C" "30000000" "30000000" -ok_xmalloc "asprintf custom" 1 "asprintf 30000000 xmalloc.c 221" \ +ok_xmalloc "asprintf custom" 1 "asprintf 30000000 xmalloc.c 224" \ "A" "30000000" "30000000" -ok_xmalloc "vasprintf custom" 1 "vasprintf 30000000 xmalloc.c 240" \ +ok_xmalloc "vasprintf custom" 1 "vasprintf 30000000 xmalloc.c 243" \ "V" "30000000" "30000000" # Check the smaller ones again just for grins. diff --git a/tests/util/xmalloc.c b/tests/util/xmalloc.c index e222612..84ba081 100644 --- a/tests/util/xmalloc.c +++ b/tests/util/xmalloc.c @@ -34,7 +34,10 @@ #include <ctype.h> #include <errno.h> -#include <sys/time.h> +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#endif +#include <time.h> /* Linux requires sys/time.h be included before sys/resource.h. */ #include <sys/resource.h> @@ -261,7 +264,7 @@ test_asprintf(size_t size) /* Wrapper around vasprintf to do the va_list stuff. */ -static void +static void __attribute__((__format__(printf, 2, 3))) xvasprintf_wrapper(char **strp, const char *format, ...) { va_list args; |