summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJim Meyering <meyering@redhat.com>2008-10-23 22:44:10 +0200
committerJim Meyering <meyering@redhat.com>2009-07-04 18:47:49 +0200
commit582103c67e6718b484e052fc135cba7056eeaf0a (patch)
tree8c0bbe564d6d36d2e5fe1e80ea5165b006eb16ce
parent2da7dc21e77a43d43696abc86700dda9259cf091 (diff)
downloadidutils-582103c67e6718b484e052fc135cba7056eeaf0a.tar.gz
idutils-582103c67e6718b484e052fc135cba7056eeaf0a.tar.bz2
idutils-582103c67e6718b484e052fc135cba7056eeaf0a.zip
tests: add testing framework, and tests for the new option
* NEWS (New features): Mention the new option. * src/mkid.c (main): Adjust a diagnostic. * src/xtokid.c (main): Adjust a diagnostic. * bootstrap.conf (gnulib_modules): Add perl and posix_shell * build-aux/check.mk: New file, from coreutils. * testsuite/Coreutils.pm: Likewise. * testsuite/CuTmpdir.pm: Likewise. * testsuite/check.mk: Likewise. * testsuite/envvar-check: Likewise. * testsuite/files0-from: New test, based on one from coreutils. * testsuite/Makefile.am (TESTS): Add files0-from. (TESTS_ENVIRONMENT): Remove definition. Now it's in check.mk. (EXTRA_DIST): Add Coreutils.pm CuTmpdir.pm envvar-check and check.mk. Include $(srcdir)/check.mk.
-rw-r--r--NEWS6
-rw-r--r--bootstrap.conf2
-rw-r--r--build-aux/check.mk283
-rw-r--r--src/mkid.c2
-rw-r--r--src/xtokid.c2
-rw-r--r--testsuite/Coreutils.pm611
-rw-r--r--testsuite/CuTmpdir.pm109
-rw-r--r--testsuite/Makefile.am20
-rw-r--r--testsuite/check.mk75
-rw-r--r--testsuite/envvar-check55
-rwxr-xr-xtestsuite/files0-from104
11 files changed, 1260 insertions, 9 deletions
diff --git a/NEWS b/NEWS
index 3f8cccd..3c5bcc0 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,12 @@ GNU idutils NEWS -*- outline -*-
* Noteworthy changes in release ?.? (????-??-??) [?]
+** New features
+
+ mkid and xtokid accept a new option --files0-from=FILE, to make them
+ process only the files named in FILE. FILE must contain a list of
+ NUL-terminated file names.
+
* Noteworthy changes in release 4.3.92 (2008-10-18) [beta]
diff --git a/bootstrap.conf b/bootstrap.conf
index e7bed01..4055c41 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -62,6 +62,8 @@ gnulib_modules="
mempcpy
obstack
pathmax
+ perl
+ posix-shell
progname
quote
quotearg
diff --git a/build-aux/check.mk b/build-aux/check.mk
new file mode 100644
index 0000000..92935d9
--- /dev/null
+++ b/build-aux/check.mk
@@ -0,0 +1,283 @@
+## Vaucanson, a generic library for finite state machines.
+## Copyright (C) 2006, 2007 The Vaucanson Group.
+##
+## This program is free software; you can redistribute it and/or
+## modify it under the terms of the GNU General Public License
+## as published by the Free Software Foundation; either version 2
+## of the License, or (at your option) any later version.
+##
+## The complete GNU General Public Licence Notice can be found as the
+## `COPYING' file in the root directory.
+
+## Override the definition from Automake to generate a log file with
+## failed tests. It also supports parallel make checks.
+##
+## This file provides special support for "unit tests", that is to
+## say, tests that (once run) no longer need to be re-compiled and
+## re-run at each "make check", unless their sources changed. To
+## enable unit-test supports, define LAZY_TEST_SUITE. In such a
+## setting, that heavily relies on correct dependencies, its users may
+## prefer to define EXTRA_PROGRAMS instead of check_PROGRAMS, because
+## it allows intertwined compilation and execution of the tests.
+## Sometimes this helps catching errors earlier (you don't have to
+## wait for all the tests to be compiled).
+##
+## Define TEST_SUITE_LOG to be the name of the global log to create.
+## Define TEST_LOGS to the set of logs to include in it. One possibility
+## is $(TESTS:.test=.log).
+##
+## In addition to the magic "exit 77 means SKIP" feature (which was
+## imported from automake), there is a magic "exit 177 means FAIL" feature
+## which is useful if you need to issue a hard error no matter whether the
+## test is XFAIL or not.
+
+# Use a POSIX-compatible shell if available, as this file uses
+# features of the POSIX shell that are not supported by some standard
+# shell implementations (e.g., Solaris 10 /bin/sh).
+SHELL = $(PREFERABLY_POSIX_SHELL)
+
+# Set this to `false' to disable hard errors.
+ENABLE_HARD_ERRORS = :
+
+## We use GNU Make extensions (%-rules) inside GNU_MAKE checks,
+## and we override check-TESTS.
+AUTOMAKE_OPTIONS += -Wno-portability -Wno-override
+
+# Restructured Text title and section.
+am__rst_title = sed 's/.*/ & /;h;s/./=/g;p;x;p;g;p;s/.*//'
+am__rst_section = sed 'p;s/./=/g;p;g'
+
+# Put stdin (possibly several lines separated by ". ") in a box.
+am__text_box = $(AWK) '{gsub ("\\. ", "\n"); print $$0; }' | \
+$(AWK) ' \
+max < length($$0) { \
+ final= final (final ? "\n" : "") " " $$0; \
+ max = length($$0); \
+} \
+END { \
+ for (i = 0; i < max + 2 ; ++i) \
+ line = line "="; \
+ print line; \
+ print final; \
+ print line; \
+}'
+
+# If stdout is a tty and TERM is smart then use colors. If test -t or
+# tput are not supported then this fails; a conservative approach. Of
+# course do not redirect stdout here, just stderr...
+am__tty_colors = \
+red=; \
+grn=; \
+lgn=; \
+blu=; \
+std=; \
+test "X$$TERM" != Xdumb && \
+test -t 1 2>/dev/null && \
+tput bold 1 >/dev/null 2>&1 && \
+tput setaf 1 >/dev/null 2>&1 && \
+tput sgr0 >/dev/null 2>&1 && \
+{ \
+ red=$$(tput setaf 1); \
+ grn=$$(tput setaf 2); \
+ lgn=$$(tput bold)$$(tput setaf 2); \
+ blu=$$(tput setaf 4); \
+ std=$$(tput sgr0); \
+}
+
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL). This contradicts POSIX. Work around the problem
+# by disabling -e (using the XSI extension "set +e") if it's set.
+SH_E_WORKAROUND = case $$- in *e*) set +e;; esac
+
+# Emulate dirname with sed.
+_d_no_slash = s,^[^/]*$$,.,
+_d_strip_trailing = s,\([^/]\)//*$$,\1,
+_d_abs_trivial = s,^//*[^/]*$$,/,
+_d_rm_basename = s,\([^/]\)//*[^/]*$$,\1,
+_dirname = \
+ sed '$(_d_no_slash);$(_d_strip_trailing);$(_d_abs_trivial);$(_d_rm_basename)'
+
+# To be inserted before the command running the test. Creates the
+# directory for the log if needed. Stores in $dir the directory
+# containing $src, and passes TESTS_ENVIRONMENT.
+# Save and restore TERM around use of TESTS_ENVIRONMENT,
+# in case that unsets it.
+am__check_pre = \
+$(SH_E_WORKAROUND); \
+tst=`echo "$$src" | sed 's|^.*/||'`; \
+rm -f $@-t; \
+trap 'st=$$?; rm -f '\''$(abs_builddir)/$@-t'\''; (exit $$st); exit $$st' \
+ 1 2 13 15; \
+$(mkdir_p) "$$(echo '$@'|$(_dirname))" || exit; \
+if test -f "./$$src"; then dir=./; \
+elif test -f "$$src"; then dir=; \
+else dir="$(srcdir)/"; fi; \
+__SAVED_TERM=$$TERM; \
+$(TESTS_ENVIRONMENT)
+
+# To be appended to the command running the test. Handles the stdout
+# and stderr redirection, and catch the exit status.
+am__check_post = \
+>$@-t 2>&1; \
+estatus=$$?; \
+if test $$estatus -eq 177; then \
+ $(ENABLE_HARD_ERRORS) || estatus=1; \
+fi; \
+TERM=$$__SAVED_TERM; export TERM; \
+$(am__tty_colors); \
+xfailed=PASS; \
+for xfail in : $(XFAIL_TESTS); do \
+ case $$src in \
+ $$xfail | */$$xfail) xfailed=XFAIL; break; \
+ esac; \
+done; \
+case $$estatus:$$xfailed in \
+ 0:XFAIL) col=$$red; res=XPASS;; \
+ 0:*) col=$$grn; res=PASS ;; \
+ 77:*) col=$$blu; res=SKIP ;; \
+ 177:*) col=$$red; res=FAIL ;; \
+ *:XFAIL) col=$$lgn; res=XFAIL;; \
+ *:*) col=$$red; res=FAIL ;; \
+esac; \
+echo "$${col}$$res$${std}: $@"; \
+echo "$$res: $@ (exit: $$estatus)" | \
+ $(am__rst_section) >$@; \
+cat $@-t >>$@; \
+rm $@-t
+
+SUFFIXES += .html .log
+
+# From a test (with no extension) to a log file.
+if GNU_MAKE
+%.log: %
+ @src='$<'; $(am__check_pre) "$$dir$$src" $(am__check_post)
+else
+# With POSIX 'make', inference rules cannot have FOO.log depend on FOO.
+# Work around the problem by calculating the dependency dynamically, and
+# then invoking a submake with the calculated dependency.
+CHECK-FORCE:
+DEPENDENCY = CHECK-FORCE
+$(TEST_LOGS): $(DEPENDENCY)
+ @if test '$(DEPENDENCY)' = CHECK-FORCE; then \
+ dst=$@; src=$${dst%.log}; \
+ test -x "$$src" || src='$(srcdir)'/$$src; \
+ exec $(MAKE) $(AM_MAKEFLAGS) DEPENDENCY="$$src" $@; \
+ else \
+ src='$(DEPENDENCY)'; \
+ $(am__check_pre) "$$dir$$src" $(am__check_post); \
+ fi
+endif
+
+#TEST_LOGS = $(TESTS:.test=.log)
+TEST_SUITE_LOG = test-suite.log
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+ @$(SH_E_WORKAROUND); \
+ results=$$(for f in $(TEST_LOGS); do sed 1q $$f; done); \
+ all=$$(echo "$$results" | wc -l | sed -e 's/^[ \t]*//'); \
+ fail=$$(echo "$$results" | grep -c '^FAIL'); \
+ pass=$$(echo "$$results" | grep -c '^PASS'); \
+ skip=$$(echo "$$results" | grep -c '^SKIP'); \
+ xfail=$$(echo "$$results" | grep -c '^XFAIL'); \
+ xpass=$$(echo "$$results" | grep -c '^XPASS'); \
+ failures=$$(expr $$fail + $$xpass); \
+ case fail=$$fail:xpass=$$xpass:xfail=$$xfail in \
+ fail=0:xpass=0:xfail=0) \
+ msg="All $$all tests passed. "; \
+ exit=true;; \
+ fail=0:xpass=0:xfail=*) \
+ msg="All $$all tests behaved as expected"; \
+ msg="$$msg ($$xfail expected failures). "; \
+ exit=true;; \
+ fail=*:xpass=0:xfail=*) \
+ msg="$$fail of $$all tests failed. "; \
+ exit=false;; \
+ fail=*:xpass=*:xfail=*) \
+ msg="$$failures of $$all tests did not behave as expected"; \
+ msg="$$msg ($$xpass unexpected passes). "; \
+ exit=false;; \
+ *) \
+ echo >&2 "incorrect case"; exit 4;; \
+ esac; \
+ if test "$$skip" -ne 0; then \
+ msg="$$msg($$skip tests were not run). "; \
+ fi; \
+ if test "$$failures" -ne 0; then \
+ { \
+ echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \
+ $(am__rst_title); \
+ echo "$$msg"; \
+ echo; \
+ echo ".. contents:: :depth: 2"; \
+ echo; \
+ for f in $(TEST_LOGS); \
+ do \
+ case $$(sed 1q $$f) in \
+ SKIP:*|PASS:*|XFAIL:*);; \
+ *) echo; cat $$f;; \
+ esac; \
+ done; \
+ } >$(TEST_SUITE_LOG).tmp; \
+ mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \
+ msg="$${msg}See $(subdir)/$(TEST_SUITE_LOG). "; \
+ if test -n "$(PACKAGE_BUGREPORT)"; then \
+ msg="$${msg}Please report it to $(PACKAGE_BUGREPORT). "; \
+ fi; \
+ fi; \
+ $(am__tty_colors); \
+ if $$exit; then echo $$grn; else echo $$red; fi; \
+ echo "$$msg" | $(am__text_box); \
+ echo $$std; \
+ test x"$$VERBOSE" = x || $$exit || cat $(TEST_SUITE_LOG); \
+ $$exit
+
+# if test x"$$VERBOSE" != x && ! $exit; then
+
+# Run all the tests.
+check-TESTS:
+ @if test -z '$(LAZY_TEST_SUITE)'; then \
+ rm -f $(TEST_SUITE_LOG) $(TEST_LOGS); \
+ fi
+ @$(MAKE) $(TEST_SUITE_LOG)
+
+
+## -------------- ##
+## Produce HTML. ##
+## -------------- ##
+
+TEST_SUITE_HTML = $(TEST_SUITE_LOG:.log=.html)
+
+.log.html:
+ @for r2h in $(RST2HTML) $$RST2HTML rst2html rst2html.py; \
+ do \
+ if ($$r2h --version) >/dev/null 2>&1; then \
+ R2H=$$r2h; \
+ fi; \
+ done; \
+ if test -z "$$R2H"; then \
+ echo >&2 "cannot find rst2html, cannot create $@"; \
+ exit 2; \
+ fi; \
+ $$R2H $< >$@.tmp
+ @mv $@.tmp $@
+
+# Be sure to run check-TESTS first, and then to convert the result.
+# Beware of concurrent executions. And expect check-TESTS to fail.
+check-html:
+ @if $(MAKE) $(AM_MAKEFLAGS) check-TESTS; then :; else \
+ rv=$$?; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_HTML); \
+ exit $$rv; \
+ fi
+
+.PHONY: check-html
+
+
+## ------- ##
+## Clean. ##
+## ------- ##
+
+check-clean:
+ rm -f $(CHECK_CLEANFILES) $(TEST_SUITE_LOG) $(TEST_SUITE_HTML) $(TEST_LOGS)
+.PHONY: check-clean
+clean-local: check-clean
diff --git a/src/mkid.c b/src/mkid.c
index e3c7377..1b4ab8a 100644
--- a/src/mkid.c
+++ b/src/mkid.c
@@ -311,7 +311,7 @@ main (int argc, char **argv)
{
error (0, 0, _("extra operand %s"), quote (argv[optind]));
fprintf (stderr, "%s\n",
- _("File operands cannot be combined with --files0-from."));
+ _("file operands cannot be combined with --files0-from"));
usage();
}
diff --git a/src/xtokid.c b/src/xtokid.c
index 1c86e05..6311827 100644
--- a/src/xtokid.c
+++ b/src/xtokid.c
@@ -194,7 +194,7 @@ main (int argc, char **argv)
{
error (0, 0, _("extra operand %s"), quote (argv[optind]));
fprintf (stderr, "%s\n",
- _("File operands cannot be combined with --files0-from."));
+ _("file operands cannot be combined with --files0-from"));
usage();
}
diff --git a/testsuite/Coreutils.pm b/testsuite/Coreutils.pm
new file mode 100644
index 0000000..bfb4f13
--- /dev/null
+++ b/testsuite/Coreutils.pm
@@ -0,0 +1,611 @@
+package Coreutils;
+# This is a testing framework.
+
+# Copyright (C) 1998, 2000-2002, 2004-2008 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+use strict;
+use vars qw($VERSION @ISA @EXPORT);
+
+use FileHandle;
+use File::Compare qw(compare);
+
+@ISA = qw(Exporter);
+($VERSION = '$Revision: 1.5 $ ') =~ tr/[0-9].//cd;
+@EXPORT = qw (run_tests triple_test);
+
+my $debug = $ENV{DEBUG};
+
+my @Types = qw (IN IN_PIPE OUT ERR AUX CMP EXIT PRE POST OUT_SUBST
+ ERR_SUBST ENV ENV_DEL);
+my %Types = map {$_ => 1} @Types;
+my %Zero_one_type = map {$_ => 1}
+ qw (OUT ERR EXIT PRE POST OUT_SUBST ERR_SUBST ENV);
+my $srcdir = $ENV{srcdir};
+my $Global_count = 1;
+
+# When running in a DJGPP environment, make $ENV{SHELL} point to bash.
+# Otherwise, a bad shell might be used (e.g. command.com) and many
+# tests would fail.
+defined $ENV{DJDIR}
+ and $ENV{SHELL} = "$ENV{DJDIR}/bin/bash.exe";
+
+# A file spec: a scalar or a reference to a single-keyed hash
+# ================
+# 'contents' contents only (file name is derived from test name)
+# {filename => 'contents'} filename and contents
+# {filename => undef} filename only -- $(srcdir)/filename must exist
+#
+# FIXME: If there is more than one input file, then you can't specify `REDIR'.
+# PIPE is still ok.
+#
+# I/O spec: a hash ref with the following properties
+# ================
+# - one key/value pair
+# - the key must be one of these strings: IN, OUT, ERR, AUX, CMP, EXIT
+# - the value must be a file spec
+# {OUT => 'data'} put data in a temp file and compare it to stdout from cmd
+# {OUT => {'filename'=>undef}} compare contents of existing filename to
+# stdout from cmd
+# {OUT => {'filename'=>[$CTOR, $DTOR]}} $CTOR and $DTOR are references to
+# functions, each which is passed the single argument `filename'.
+# $CTOR must create `filename'.
+# DTOR may be omitted in which case `sub{unlink @_[0]}' is used.
+# FIXME: implement this
+# {ERR => ...}
+# Same as for OUT, but compare with stderr, not stdout.
+# {OUT_SUBST => 's/variable_output/expected_output/'}
+# Transform actual standard output before comparing it against expected output.
+# This is useful e.g. for programs like du that produce output that
+# varies a lot from system. E.g., an empty file may consume zero file
+# blocks, or more, depending on the OS and on the file system type.
+# {ERR_SUBST => 's/variable_output/expected_output/'}
+# Transform actual stderr output before comparing it against expected.
+# This is useful when verifying that we get a meaningful diagnostic.
+# For example, in rm/fail-2eperm, we have to account for three different
+# diagnostics: Operation not permitted, Not owner, and Permission denied.
+# {EXIT => N} expect exit status of cmd to be N
+# {ENV => 'VAR=val ...'}
+# Prepend 'VAR=val ...' to the command that we execute via `system'.
+# {ENV_DEL => 'VAR'}
+# Remove VAR from the environment just before running the corresponding
+# command, and restore any value just afterwards.
+#
+# There may be many input file specs. File names from the input specs
+# are concatenated in order on the command line.
+# There may be at most one of the OUT-, ERR-, and EXIT-keyed specs.
+# If the OUT-(or ERR)-keyed hash ref is omitted, then expect no output
+# on stdout (or stderr).
+# If the EXIT-keyed one is omitted, then expect the exit status to be zero.
+
+# FIXME: Make sure that no junkfile is also listed as a
+# non-junkfile (i.e. with undef for contents)
+
+sub _shell_quote ($)
+{
+ my ($string) = @_;
+ $string =~ s/\'/\'\\\'\'/g;
+ return "'$string'";
+}
+
+sub _create_file ($$$$)
+{
+ my ($program_name, $test_name, $file_name, $data) = @_;
+ my $file;
+ if (defined $file_name)
+ {
+ $file = $file_name;
+ }
+ else
+ {
+ $file = "$test_name.$Global_count";
+ ++$Global_count;
+ }
+
+ warn "creating file `$file' with contents `$data'\n" if $debug;
+
+ # The test spec gave a string.
+ # Write it to a temp file and return tempfile name.
+ my $fh = new FileHandle "> $file";
+ die "$program_name: $file: $!\n" if ! $fh;
+ print $fh $data;
+ $fh->close || die "$program_name: $file: $!\n";
+
+ return $file;
+}
+
+sub _compare_files ($$$$$)
+{
+ my ($program_name, $test_name, $in_or_out, $actual, $expected) = @_;
+
+ my $differ = compare ($expected, $actual);
+ if ($differ)
+ {
+ my $info = (defined $in_or_out ? "std$in_or_out " : '');
+ warn "$program_name: test $test_name: ${info}mismatch, comparing "
+ . "$actual (actual) and $expected (expected)\n";
+ # Ignore any failure, discard stderr.
+ system "diff -c $actual $expected 2>/dev/null";
+ }
+
+ return $differ;
+}
+
+sub _process_file_spec ($$$$$)
+{
+ my ($program_name, $test_name, $file_spec, $type, $junk_files) = @_;
+
+ my ($file_name, $contents);
+ if (!ref $file_spec)
+ {
+ ($file_name, $contents) = (undef, $file_spec);
+ }
+ elsif (ref $file_spec eq 'HASH')
+ {
+ my $n = keys %$file_spec;
+ die "$program_name: $test_name: $type spec has $n elements --"
+ . " expected 1\n"
+ if $n != 1;
+ ($file_name, $contents) = each %$file_spec;
+
+ # This happens for the AUX hash in an io_spec like this:
+ # {CMP=> ['zy123utsrqponmlkji', {'@AUX@'=> undef}]},
+ defined $contents
+ or return $file_name;
+ }
+ else
+ {
+ die "$program_name: $test_name: invalid RHS in $type-spec\n"
+ }
+
+ my $is_junk_file = (! defined $file_name
+ || (($type eq 'IN' || $type eq 'AUX' || $type eq 'CMP')
+ && defined $contents));
+ my $file = _create_file ($program_name, $test_name,
+ $file_name, $contents);
+
+ if ($is_junk_file)
+ {
+ push @$junk_files, $file
+ }
+ else
+ {
+ # FIXME: put $srcdir in here somewhere
+ warn "$program_name: $test_name: specified file `$file' does"
+ . " not exist\n"
+ if ! -f "$srcdir/$file";
+ }
+
+ return $file;
+}
+
+sub _at_replace ($$)
+{
+ my ($map, $s) = @_;
+ foreach my $eo (qw (AUX OUT ERR))
+ {
+ my $f = $map->{$eo};
+ $f
+ and $s =~ /\@$eo\@/
+ and $s =~ s/\@$eo\@/$f/g;
+ }
+ return $s;
+}
+
+# FIXME: cleanup on interrupt
+# FIXME: extract `do_1_test' function
+
+# FIXME: having to include $program_name here is an expedient kludge.
+# Library code doesn't `die'.
+sub run_tests ($$$$$)
+{
+ my ($program_name, $prog, $t_spec, $save_temps, $verbose) = @_;
+
+ # To indicate that $prog is a shell built-in, you'd make it a string 'ref'.
+ # E.g., call run_tests ($prog, \$prog, \@Tests, $save_temps, $verbose);
+ # If it's a ref, invoke it via "env":
+ my @prog = ref $prog ? (qw(env --), $$prog) : $prog;
+
+ # Warn about empty t_spec.
+ # FIXME
+
+ # Remove all temp files upon interrupt.
+ # FIXME
+
+ # Verify that test names are distinct.
+ my $bad_test_name = 0;
+ my %seen;
+ my %seen_8dot3;
+ my $t;
+ foreach $t (@$t_spec)
+ {
+ my $test_name = $t->[0];
+ if ($seen{$test_name})
+ {
+ warn "$program_name: $test_name: duplicate test name\n";
+ $bad_test_name = 1;
+ }
+ $seen{$test_name} = 1;
+
+ if (0)
+ {
+ my $t8 = lc substr $test_name, 0, 8;
+ if ($seen_8dot3{$t8})
+ {
+ warn "$program_name: 8.3 test name conflict: "
+ . "$test_name, $seen_8dot3{$t8}\n";
+ $bad_test_name = 1;
+ }
+ $seen_8dot3{$t8} = $test_name;
+ }
+
+ # The test name may be no longer than 30 bytes.
+ # Yes, this is an arbitrary limit. If it causes trouble,
+ # consider removing it.
+ my $max = 30;
+ if ($max < length $test_name)
+ {
+ warn "$program_name: $test_name: test name is too long (> $max)\n";
+ $bad_test_name = 1;
+ }
+ }
+ return 1 if $bad_test_name;
+
+ # FIXME check exit status
+ system (@prog, '--version') if $verbose;
+
+ my @junk_files;
+ my $fail = 0;
+ foreach my $tt (@$t_spec)
+ {
+ my @post_compare;
+ my @dummy = @$tt;
+ my $t = \@dummy;
+ my $test_name = shift @$t;
+ my $expect = {};
+ my ($pre, $post);
+
+ # FIXME: maybe don't reset this.
+ $Global_count = 1;
+ my @args;
+ my $io_spec;
+ my %seen_type;
+ my @env_delete;
+ my $env_prefix = '';
+ my $input_pipe_cmd;
+ foreach $io_spec (@$t)
+ {
+ if (!ref $io_spec)
+ {
+ push @args, $io_spec;
+ next;
+ }
+
+ if (ref $io_spec ne 'HASH')
+ {
+ eval 'use Data::Dumper';
+ die "$program_name: $test_name: invalid entry in test spec; "
+ . "expected HASH-ref,\nbut got this:\n"
+ . Data::Dumper->Dump ([\$io_spec], ['$io_spec']) . "\n";
+ }
+
+ my $n = keys %$io_spec;
+ die "$program_name: $test_name: spec has $n elements --"
+ . " expected 1\n"
+ if $n != 1;
+ my ($type, $val) = each %$io_spec;
+ die "$program_name: $test_name: invalid key `$type' in test spec\n"
+ if ! $Types{$type};
+
+ # Make sure there's no more than one of OUT, ERR, EXIT, etc.
+ die "$program_name: $test_name: more than one $type spec\n"
+ if $Zero_one_type{$type} and $seen_type{$type}++;
+
+ if ($type eq 'PRE' or $type eq 'POST')
+ {
+ $expect->{$type} = $val;
+ next;
+ }
+
+ if ($type eq 'CMP')
+ {
+ my $t = ref $val;
+ $t && $t eq 'ARRAY'
+ or die "$program_name: $test_name: invalid CMP spec\n";
+ @$val == 2
+ or die "$program_name: $test_name: invalid CMP list; must have"
+ . " exactly 2 elements\n";
+ my @cmp_files;
+ foreach my $e (@$val)
+ {
+ my $r = ref $e;
+ $r && $r ne 'HASH'
+ and die "$program_name: $test_name: invalid element ($r)"
+ . " in CMP list; only scalars and hash references "
+ . "are allowed\n";
+ if ($r && $r eq 'HASH')
+ {
+ my $n = keys %$e;
+ $n == 1
+ or die "$program_name: $test_name: CMP spec has $n "
+ . "elements -- expected 1\n";
+
+ # Replace any `@AUX@' in the key of %$e.
+ my ($ff, $val) = each %$e;
+ my $new_ff = _at_replace $expect, $ff;
+ if ($new_ff ne $ff)
+ {
+ $e->{$new_ff} = $val;
+ delete $e->{$ff};
+ }
+ }
+ my $cmp_file = _process_file_spec ($program_name, $test_name,
+ $e, $type, \@junk_files);
+ push @cmp_files, $cmp_file;
+ }
+ push @post_compare, [@cmp_files];
+
+ $expect->{$type} = $val;
+ next;
+ }
+
+ if ($type eq 'EXIT')
+ {
+ die "$program_name: $test_name: invalid EXIT code\n"
+ if $val !~ /^\d+$/;
+ # FIXME: make sure $data is numeric
+ $expect->{EXIT} = $val;
+ next;
+ }
+
+ if ($type =~ /^(OUT|ERR)_SUBST$/)
+ {
+ $expect->{RESULT_SUBST} ||= {};
+ $expect->{RESULT_SUBST}->{$1} = $val;
+ next;
+ }
+
+ if ($type eq 'ENV')
+ {
+ $env_prefix = "$val ";
+ next;
+ }
+
+ if ($type eq 'ENV_DEL')
+ {
+ push @env_delete, $val;
+ next;
+ }
+
+ my $file = _process_file_spec ($program_name, $test_name, $val,
+ $type, \@junk_files);
+
+ if ($type eq 'IN' || $type eq 'IN_PIPE')
+ {
+ my $quoted_file = _shell_quote $file;
+ if ($type eq 'IN_PIPE')
+ {
+ defined $input_pipe_cmd
+ and die "$program_name: $test_name: only one input"
+ . " may be specified with IN_PIPE\n";
+ $input_pipe_cmd = "cat $quoted_file |";
+ }
+ else
+ {
+ push @args, $quoted_file;
+ }
+ }
+ elsif ($type eq 'AUX' || $type eq 'OUT' || $type eq 'ERR')
+ {
+ $expect->{$type} = $file;
+ }
+ else
+ {
+ die "$program_name: $test_name: invalid type: $type\n"
+ }
+ }
+
+ # Expect an exit status of zero if it's not specified.
+ $expect->{EXIT} ||= 0;
+
+ # Allow ERR to be omitted -- in that case, expect no error output.
+ foreach my $eo (qw (OUT ERR))
+ {
+ if (!exists $expect->{$eo})
+ {
+ $expect->{$eo} = _create_file ($program_name, $test_name,
+ undef, '');
+ push @junk_files, $expect->{$eo};
+ }
+ }
+
+ # FIXME: Does it ever make sense to specify a filename *and* contents
+ # in OUT or ERR spec?
+
+ # FIXME: this is really suboptimal...
+ my @new_args;
+ foreach my $a (@args)
+ {
+ $a = _at_replace $expect, $a;
+ push @new_args, $a;
+ }
+ @args = @new_args;
+
+ warn "$test_name...\n" if $verbose;
+ &{$expect->{PRE}} if $expect->{PRE};
+ my %actual;
+ $actual{OUT} = "$test_name.O";
+ $actual{ERR} = "$test_name.E";
+ push @junk_files, $actual{OUT}, $actual{ERR};
+ my @cmd = (@prog, @args, "> $actual{OUT}", "2> $actual{ERR}");
+ $env_prefix
+ and unshift @cmd, $env_prefix;
+ defined $input_pipe_cmd
+ and unshift @cmd, $input_pipe_cmd;
+ my $cmd_str = join (' ', @cmd);
+
+ # Delete from the environment any symbols specified by syntax
+ # like this: {ENV_DEL => 'TZ'}.
+ my %pushed_env;
+ foreach my $env_sym (@env_delete)
+ {
+ my $val = delete $ENV{$env_sym};
+ defined $val
+ and $pushed_env{$env_sym} = $val;
+ }
+
+ warn "Running command: `$cmd_str'\n" if $debug;
+ my $rc = 0xffff & system $cmd_str;
+
+ # Restore any environment setting we changed via a deletion.
+ foreach my $env_sym (keys %pushed_env)
+ {
+ $ENV{$env_sym} = $pushed_env{$env_sym};
+ }
+
+ if ($rc == 0xff00)
+ {
+ warn "$program_name: test $test_name failed: command failed:\n"
+ . " `$cmd_str': $!\n";
+ $fail = 1;
+ goto cleanup;
+ }
+ $rc >>= 8 if $rc > 0x80;
+ if ($expect->{EXIT} != $rc)
+ {
+ warn "$program_name: test $test_name failed: exit status mismatch:"
+ . " expected $expect->{EXIT}, got $rc\n";
+ $fail = 1;
+ goto cleanup;
+ }
+
+ my %actual_data;
+ # Record actual stdout and stderr contents, if POST may need them.
+ if ($expect->{POST})
+ {
+ foreach my $eo (qw (OUT ERR))
+ {
+ my $out_file = $actual{$eo};
+ open IN, $out_file
+ or (warn "$program_name: cannot open $out_file for reading: $!\n"),
+ $fail = 1, next;
+ $actual_data{$eo} = <IN>;
+ close IN
+ or (warn "$program_name: failed to read $out_file: $!\n"),
+ $fail = 1;
+ }
+ }
+
+ foreach my $eo (qw (OUT ERR))
+ {
+ my $subst_expr = $expect->{RESULT_SUBST}->{$eo};
+ if (defined $subst_expr)
+ {
+ my $out = $actual{$eo};
+ my $orig = "$out.orig";
+
+ # Move $out aside (to $orig), then recreate $out
+ # by transforming each line of $orig via $subst_expr.
+ rename $out, $orig
+ or (warn "$program_name: cannot rename $out to $orig: $!\n"),
+ $fail = 1, next;
+ open IN, $orig
+ or (warn "$program_name: cannot open $orig for reading: $!\n"),
+ $fail = 1, (unlink $orig), next;
+ unlink $orig
+ or (warn "$program_name: cannot unlink $orig: $!\n"),
+ $fail = 1;
+ open OUT, ">$out"
+ or (warn "$program_name: cannot open $out for writing: $!\n"),
+ $fail = 1, next;
+ while (defined (my $line = <IN>))
+ {
+ eval "\$_ = \$line; $subst_expr; \$line = \$_";
+ print OUT $line;
+ }
+ close IN;
+ close OUT
+ or (warn "$program_name: failed to write $out: $!\n"),
+ $fail = 1, next;
+ }
+
+ my $eo_lower = lc $eo;
+ _compare_files ($program_name, $test_name, $eo_lower,
+ $actual{$eo}, $expect->{$eo})
+ and $fail = 1;
+ }
+
+ foreach my $pair (@post_compare)
+ {
+ my ($expected, $actual) = @$pair;
+ _compare_files $program_name, $test_name, undef, $actual, $expected
+ and $fail = 1;
+ }
+
+ cleanup:
+ $expect->{POST}
+ and &{$expect->{POST}} ($actual_data{OUT}, $actual_data{ERR});
+
+ }
+
+ # FIXME: maybe unlink files inside the big foreach loop?
+ unlink @junk_files if ! $save_temps;
+
+ return $fail;
+}
+
+# For each test in @$TESTS, generate two additional tests,
+# one using stdin, the other using a pipe. I.e., given this one
+# ['idem-0', {IN=>''}, {OUT=>''}],
+# generate these:
+# ['idem-0.r', '<', {IN=>''}, {OUT=>''}],
+# ['idem-0.p', {IN_PIPE=>''}, {OUT=>''}],
+# Generate new tests only if there is exactly one input spec.
+# The returned list of tests contains each input test, followed
+# by zero or two derived tests.
+sub triple_test($)
+{
+ my ($tests) = @_;
+ my @new;
+ foreach my $t (@$tests)
+ {
+ push @new, $t;
+
+ my @in;
+ my @args;
+ my @list_of_hash;
+ foreach my $e (@$t)
+ {
+ !ref $e
+ and push (@args, $e), next;
+
+ ref $e && ref $e eq 'HASH'
+ or (warn "$0: $t->[0]: unexpected entry type\n"), next;
+ defined $e->{IN}
+ and (push @in, $e->{IN}), next;
+ push @list_of_hash, $e;
+ }
+ # Add variants IFF there is exactly one input file.
+ @in == 1
+ or next;
+ shift @args; # discard test name
+ push @new, ["$t->[0].r", @args, '<', {IN => $in[0]}, @list_of_hash];
+ push @new, ["$t->[0].p", @args, {IN_PIPE => $in[0]}, @list_of_hash];
+ }
+ return @new;
+}
+
+## package return
+1;
diff --git a/testsuite/CuTmpdir.pm b/testsuite/CuTmpdir.pm
new file mode 100644
index 0000000..166e50b
--- /dev/null
+++ b/testsuite/CuTmpdir.pm
@@ -0,0 +1,109 @@
+package CuTmpdir;
+# create, then chdir into a temporary sub-directory
+
+# Copyright (C) 2007-2008 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+use strict;
+use warnings;
+
+use File::Temp;
+use File::Find;
+
+our $ME = $0 || "<???>";
+
+my $dir;
+
+sub skip_test($)
+{
+ warn "$ME: skipping test: unsafe working directory name: `$_[0]'\n";
+ exit 77;
+}
+
+sub chmod_1
+{
+ my $name = $_;
+
+ # Skip symlinks and non-directories.
+ -l $name || !-d _
+ and return;
+
+ chmod 0700, $name;
+}
+
+sub chmod_tree
+{
+ if (defined $dir && chdir $dir)
+ {
+ # Perform the equivalent of find . -type d -print0|xargs -0 chmod -R 700.
+ my $options = {untaint => 1, wanted => \&chmod_1};
+ find ($options, '.');
+ }
+ else
+ {
+ warn "$ME: failed to chdir to $dir: $!\n";
+ }
+}
+
+sub import {
+ my $prefix = $_[1];
+
+ $ME eq '-' && defined $prefix
+ and $ME = $prefix;
+
+ if ($prefix !~ /^\//)
+ {
+ eval 'use Cwd';
+ my $cwd = $@ ? '.' : Cwd::getcwd();
+ $prefix = "$cwd/$prefix";
+ }
+
+ # Untaint for the upcoming mkdir.
+ $prefix =~ m!^([-+\@\w./]+)$!
+ or skip_test $prefix;
+ $prefix = $1;
+
+ my $original_pid = $$;
+
+ my $on_sig_remove_tmpdir = sub {
+ my ($sig) = @_;
+ if ($$ == $original_pid and defined $dir)
+ {
+ chmod_tree;
+ # Older versions of File::Temp lack this method.
+ exists &File::Temp::cleanup
+ and &File::Temp::cleanup;
+ }
+ $SIG{$sig} = 'DEFAULT';
+ kill $sig, $$;
+ };
+
+ foreach my $sig (qw (INT TERM HUP))
+ {
+ $SIG{$sig} = $on_sig_remove_tmpdir;
+ }
+
+ $dir = File::Temp::tempdir("$prefix.tmp-XXXX", CLEANUP => 1 );
+ chdir $dir
+ or warn "$ME: failed to chdir to $dir: $!\n";
+}
+
+END {
+ my $saved_errno = $?;
+ chmod_tree;
+ $? = $saved_errno;
+}
+
+1;
diff --git a/testsuite/Makefile.am b/testsuite/Makefile.am
index 32e0eff..cbadbfc 100644
--- a/testsuite/Makefile.am
+++ b/testsuite/Makefile.am
@@ -1,16 +1,22 @@
## Process this file with automake to create Makefile.in
-TESTS = \
- consistency \
+TESTS = \
+ consistency \
+ files0-from \
infloop-kawa-el
-TESTS_ENVIRONMENT = \
- abs_top_srcdir='$(abs_top_srcdir)' \
- PATH="$(abs_top_builddir)/src$(PATH_SEPARATOR)$(abs_top_srcdir)/src$(PATH_SEPARATOR)$$PATH"
-
-EXTRA_DIST = $(TESTS) single_file_token_bug.c
+EXTRA_DIST = \
+ $(TESTS) \
+ Coreutils.pm \
+ CuTmpdir.pm \
+ check.mk \
+ envvar-check \
+ single_file_token_bug.c
DISTCLEANFILES = ID
consistency-real: all
$(SHELL) $(srcdir)/consistency $(top_srcdir) $(top_builddir)/src
+
+AUTOMAKE_OPTIONS =
+include $(srcdir)/check.mk
diff --git a/testsuite/check.mk b/testsuite/check.mk
new file mode 100644
index 0000000..be9b466
--- /dev/null
+++ b/testsuite/check.mk
@@ -0,0 +1,75 @@
+# Include this file at the end of each tests/*/Makefile.am.
+# Copyright (C) 2007, 2008 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+built_programs = \
+ (cd $(top_builddir)/src && MAKEFLAGS= $(MAKE) -s built_programs.list)
+
+# Note that the first lines are statements. They ensure that environment
+# variables that can perturb tests are unset or set to expected values.
+# The rest are envvar settings that propagate build-related Makefile
+# variables to test scripts.
+TESTS_ENVIRONMENT = \
+ tmp__=$$TMPDIR; test -d "$$tmp__" || tmp__=.; \
+ . $(srcdir)/envvar-check; \
+ TMPDIR=$$tmp__; export TMPDIR; \
+ shell_or_perl_() { \
+ if grep '^\#!/usr/bin/perl' "$$1" > /dev/null; then \
+ if $(PERL) -e 'use warnings' > /dev/null 2>&1; then \
+ grep '^\#!/usr/bin/perl -T' "$$1" > /dev/null && T_=T || T_=; \
+ $(PERL) -w$$T_ -I$(srcdir) -MCoreutils \
+ -M"CuTmpdir qw($$tst)" -- "$$1"; \
+ else \
+ echo 1>&2 "$$tst: configure did not find a usable version of Perl," \
+ "so skipping this test"; \
+ (exit 77); \
+ fi; \
+ else \
+ $(SHELL) "$$1"; \
+ fi; \
+ }; \
+ export \
+ LOCALE_FR='$(LOCALE_FR)' \
+ LOCALE_FR_UTF8='$(LOCALE_FR_UTF8)' \
+ abs_top_builddir='$(abs_top_builddir)' \
+ abs_top_srcdir='$(abs_top_srcdir)' \
+ abs_srcdir='$(abs_srcdir)' \
+ built_programs="`$(built_programs)`" \
+ host_os=$(host_os) \
+ host_triplet='$(host_triplet)' \
+ srcdir='$(srcdir)' \
+ top_srcdir='$(top_srcdir)' \
+ CONFIG_HEADER='$(abs_top_builddir)/lib/config.h' \
+ CU_TEST_NAME=`basename '$(abs_srcdir)'`,$$tst \
+ AWK='$(AWK)' \
+ EGREP='$(EGREP)' \
+ EXEEXT='$(EXEEXT)' \
+ MAKE=$(MAKE) \
+ PACKAGE_BUGREPORT='$(PACKAGE_BUGREPORT)' \
+ PACKAGE_VERSION=$(PACKAGE_VERSION) \
+ PERL='$(PERL)' \
+ PREFERABLY_POSIX_SHELL='$(PREFERABLY_POSIX_SHELL)' \
+ REPLACE_GETCWD=$(REPLACE_GETCWD) \
+ PATH="$(abs_top_builddir)/src$(PATH_SEPARATOR)$(abs_top_srcdir)/src$(PATH_SEPARATOR)$$PATH" \
+ ; shell_or_perl_
+
+TEST_LOGS = $(TESTS:=.log)
+
+# Parallel replacement of Automake's check-TESTS target.
+SUFFIXES =
+include $(top_srcdir)/build-aux/check.mk
+
+VERBOSE = yes
diff --git a/testsuite/envvar-check b/testsuite/envvar-check
new file mode 100644
index 0000000..bb93c16
--- /dev/null
+++ b/testsuite/envvar-check
@@ -0,0 +1,55 @@
+# -*- sh -*-
+# Check environment variables for sane values while testing.
+
+# Copyright (C) 2000-2001, 2003-2004, 2006-2008 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+if (FOO=FOO; unset FOO) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+envvar_check_failed=0
+vars='
+ _POSIX2_VERSION
+ BLOCKSIZE
+ BLOCK_SIZE
+ CDPATH
+ COLUMNS
+ DF_BLOCK_SIZE
+ DU_BLOCK_SIZE
+ LS_BLOCK_SIZE
+ LS_COLORS
+ POSIXLY_CORRECT
+ QUOTING_STYLE
+ SIMPLE_BACKUP_SUFFIX
+ TABSIZE
+ TERM
+ TIME_STYLE
+ TMPDIR
+ VERSION_CONTROL
+'
+for var in $vars
+do
+ $as_unset $var
+ if eval test \"\${$var+set}\" = set; then
+ echo "$0: the $var environment variable is set --" \
+ ' unset it and rerun this test' >&2
+ envvar_check_failed=1
+ fi
+done
+
+test "$envvar_check_fail" = 1 && exit 1
diff --git a/testsuite/files0-from b/testsuite/files0-from
new file mode 100755
index 0000000..8a7b834
--- /dev/null
+++ b/testsuite/files0-from
@@ -0,0 +1,104 @@
+#!/usr/bin/perl
+# Exercise the --files0-from option.
+# FIXME: keep this file in sync with coreutils/tests/du/files0-from.
+
+# Copyright (C) 2006-2008 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+use strict;
+
+(my $program_name = $0) =~ s|.*/||;
+
+my $prog = 'mkid';
+
+# Turn off localization of executable's output.
+@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3;
+
+my $nothing_to_do = "$prog: nothing to do\n";
+
+my @Tests =
+ (
+ # invalid extra command line argument
+ ['f-extra-arg', '--files0-from=- no-such', {IN=>"a"}, {EXIT=>1},
+ {ERR => "$prog: extra operand `no-such'\n"
+ . "file operands cannot be combined with --files0-from\n"
+ . "Try `$prog --help' for more information.\n"}
+ ],
+
+ # missing input file
+ ['missing', '--files0-from=missing', {EXIT=>1},
+ {ERR => "$prog: cannot open `missing' for reading: "
+ . "No such file or directory\n"}],
+
+ # input file name of '-'
+ ['minus-in-stdin', '--files0-from=-', '<', {IN=>{f=>'-'}}, {EXIT=>1},
+ {ERR => "$prog: when reading file names from stdin, no file name of"
+ . " `-' allowed\n" . $nothing_to_do}],
+
+ # empty input, regular file
+ ['empty', '--files0-from=@AUX@', {AUX=>''}, {ERR => $nothing_to_do}],
+
+ # empty input, from non-regular file
+ ['empty-nonreg', '--files0-from=/dev/null', {ERR => $nothing_to_do}],
+
+ # one NUL
+ ['nul-1', '--files0-from=-', '<', {IN=>"\0"}, {EXIT=>1},
+ {ERR => "$prog: -:1: invalid zero-length file name\n" . $nothing_to_do}],
+
+ # two NULs
+ ['nul-2', '--files0-from=-', '<', {IN=>"\0\0"}, {EXIT=>1},
+ {ERR => "$prog: -:1: invalid zero-length file name\n"
+ . "$prog: -:2: invalid zero-length file name\n"
+ . $nothing_to_do }],
+
+ # one file name, no NUL
+ ['1', '--files0-from=-', '<',
+ {IN=>{f=>"g"}}, {AUX=>{g=>''}},
+ {ERR => $nothing_to_do}],
+
+ # one file name, with NUL
+ ['1a', '--files0-from=-', '<',
+ {IN=>{f=>"g\0"}}, {AUX=>{g=>''}},
+ {ERR => $nothing_to_do}],
+
+ # two file names, no final NUL
+ ['2', '--files0-from=-', '<',
+ {IN=>{f=>"g\0g"}}, {AUX=>{g=>''}},
+ {ERR => $nothing_to_do}],
+
+ # two file names, with final NUL
+ ['2a', '--files0-from=-', '<',
+ {IN=>{f=>"g\0g\0"}}, {AUX=>{g=>''}},
+ {ERR => $nothing_to_do}],
+
+ # Ensure that $prog processes FILEs following a zero-length name.
+ ['zero-len', '--files0-from=-', '<',
+ {IN=>{f=>"\0g\0"}}, {AUX=>{g=>''}},
+ {ERR => "$prog: -:1: invalid zero-length file name\n" . $nothing_to_do},
+ {EXIT=>1} ],
+ );
+
+# Insert '--lang-map=/dev/null' after each test name.
+my $t;
+foreach $t (@Tests)
+ {
+ splice @$t, 1, 0, '--lang-map=/dev/null';
+ }
+
+my $save_temps = $ENV{DEBUG};
+my $verbose = $ENV{VERBOSE};
+
+my $fail = run_tests ($program_name, $prog, \@Tests, $save_temps, $verbose);
+exit $fail;