>From f72b299cdda03fafb6dfad1fcf289ff50cb5ddf5 Mon Sep 17 00:00:00 2001
From: Bernhard Voelker
Date: Wed, 9 Jan 2019 00:24:34 +0100
Subject: [PATCH 01/12] tests: add shell-style test framework
Borrow the 'tests' framework from GNU coreutils. This allows better
shell-style tests with more control over stdin, stdout, stderr, signals,
preparatory steps, cleanup, return code verification, root-only tests,
etc.
* .gitignore: Add entries for per-test *.log and *.trs files, and the
'test-suite.log'.
* .x-update-copyright: Exempt 'tests/init.sh' as this comes from gnulib.
* Makefile.am: Include 'tests/local.mk'.
(EXTRA_DIST): Add 'tests/GNUmakefile'.
(SUBDIRS): Move 'gnulib-tests' to the end, i.e., run our own tests first.
(ALL_RECURSIVE_TARGETS): Initialize.
(update-gnulib-to-latest): Copy 'tests/init.sh' from gnulib.
* cfg.mk: Add some syntax-check rules.
* init.cfg: Add file.
* tests/GNUmakefile: Likewise.
* tests/envvar-check: Likewise.
* tests/init.sh: Likewise.
* tests/lang-default: Likewise.
* tests/local.mk: Likewise.
* tests/misc/help-version.sh: Likewise.
* tests/other-fs-tmpdir: Likewise.
* tests/sample-test: Likewise.
* tests/.gitignore: Likewise.
* NEWS (Changes to the build process): Mention the new test framework.
---
.x-update-copyright | 1 +
Makefile.am | 8 +-
NEWS | 6 +
cfg.mk | 129 ++++++-
init.cfg | 702 +++++++++++++++++++++++++++++++++++++
tests/.gitignore | 3 +
tests/GNUmakefile | 20 ++
tests/envvar-check | 58 +++
tests/init.sh | 618 ++++++++++++++++++++++++++++++++
tests/lang-default | 10 +
tests/local.mk | 109 ++++++
tests/misc/help-version.sh | 64 ++++
tests/other-fs-tmpdir | 55 +++
tests/sample-test | 36 ++
14 files changed, 1816 insertions(+), 3 deletions(-)
create mode 100644 init.cfg
create mode 100644 tests/.gitignore
create mode 100644 tests/GNUmakefile
create mode 100644 tests/envvar-check
create mode 100755 tests/init.sh
create mode 100644 tests/lang-default
create mode 100644 tests/local.mk
create mode 100755 tests/misc/help-version.sh
create mode 100644 tests/other-fs-tmpdir
create mode 100644 tests/sample-test
diff --git a/.x-update-copyright b/.x-update-copyright
index 7306adcc..eabb8227 100644
--- a/.x-update-copyright
+++ b/.x-update-copyright
@@ -1,3 +1,4 @@
^ChangeLog-2013$
^COPYING$
^bootstrap$
+^tests/init\.sh$
diff --git a/Makefile.am b/Makefile.am
index 1acee1bd..1ea7387e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -28,13 +28,16 @@ EXTRA_DIST = \
build-aux/git-version-gen \
config.h.in \
stamp-h.in \
+ tests/GNUmakefile \
tool-versions.txt
DISTCLEANFILES = tool-versions.txt
# "gnulib-tests" is the gnulib unit test dir.
-SUBDIRS = gl gnulib-tests build-aux lib find xargs locate doc po m4
+SUBDIRS = gl build-aux lib find xargs locate doc po m4 gnulib-tests
+
+ALL_RECURSIVE_TARGETS =
ACLOCAL_AMFLAGS = -I gl/m4 -I m4
@@ -107,6 +110,7 @@ gnulib-sync update-gnulib-to-latest:
&& git submodule foreach git pull origin master \
&& cp -v gnulib/doc/COPYINGv3 COPYING \
&& cp -v gnulib/build-aux/bootstrap bootstrap \
+ && cp -v gnulib/tests/init.sh tests/init.sh \
&& git status --short -- gnulib COPYING bootstrap \
)
@@ -123,3 +127,5 @@ coverage-clean:
done
clean-local: coverage-clean
+
+include $(top_srcdir)/tests/local.mk
diff --git a/NEWS b/NEWS
index 0ed37879..47338232 100644
--- a/NEWS
+++ b/NEWS
@@ -145,6 +145,12 @@ The translation files in the PO directory are no longer version controlled;
instead bootstrap auto-updates them from "translationproject.org" during a
maintainer build.
+A shell-style test framework borrowed from GNU coreutils has been added.
+This allows better tests with more control over stdin, stdout, stderr,
+signals, preparatory steps, cleanup, return code verification, root-only
+tests, etc.
+
+
* Major changes in release 4.6.0, 2015-12-28
** Stable Release
diff --git a/cfg.mk b/cfg.mk
index 4bc51872..16d605ea 100644
--- a/cfg.mk
+++ b/cfg.mk
@@ -92,16 +92,62 @@ exclude_file_name_regexp--sc_bindtextdomain = \
# cases where neither argument is a string literal.
local-checks-to-skip += sc_prohibit_strcmp
+# Ensure that each root-requiring test is run via the "check-root" rule.
+sc_root_tests:
+ @t1=sc-root.expected; t2=sc-root.actual; \
+ grep -nl '^ *require_root_$$' `$(VC_LIST) tests` | \
+ sed 's|.*/tests/|tests/|' | sort > $$t1; \
+ for t in $(all_root_tests); do echo $$t; done | sort > $$t2; \
+ st=0; diff -u $$t1 $$t2 || st=1; \
+ rm -f $$t1 $$t2; \
+ exit $$st
+
+# Ensure that all version-controlled test cases are listed in $(all_tests).
+sc_tests_list_consistency:
+ @bs="\\"; \
+ test_extensions_rx=`echo $(TEST_EXTENSIONS) \
+ | sed -e "s/ /|/g" -e "s/$$bs./$$bs$$bs./g"`; \
+ { \
+ for t in $(all_tests); do echo $$t; done; \
+ cd $(top_srcdir); \
+ $(SHELL) build-aux/vc-list-files tests \
+ | grep -Ev '^tests/init\.sh$$' \
+ | $(EGREP) "$$test_extensions_rx\$$"; \
+ } | sort | uniq -u | grep . && exit 1; :
+
+# Ensure that all version-controlled test scripts are executable.
+sc_tests_executable:
+ @set -o noglob 2>/dev/null || set -f; \
+ find_ext="-name '' "`printf -- "-o -name *%s " $(TEST_EXTENSIONS)`;\
+ find $(srcdir)/tests \( $$find_ext \) \! -perm -u+x -print \
+ | { sed "s|^$(srcdir)/||"; git ls-files $(srcdir)/tests/; } \
+ | sort | uniq -d \
+ | sed -e "s/^/$(ME): Please make test executable: /" | grep . \
+ && exit 1; :
+
+# Avoid :>file which doesn't propagate errors
+sc_prohibit_colon_redirection:
+ @cd $(srcdir)/tests && GIT_PAGER= git grep -n ': *>.*||' \
+ && { echo '$(ME): '"The leading colon in :> will hide errors" 1>&2; \
+ exit 1; } \
+ || :
+
# Usage of error() with an exit constant, should instead use die(),
# as that avoids warnings and may generate better code, due to being apparent
# to the compiler that it doesn't return.
sc_die_EXIT_FAILURE:
- @GIT_PAGER= git grep -E 'error \(.*_(FAILURE|INVALID)' \
- -- find lib locate xargs \
+ @cd $(srcdir) \
+ && GIT_PAGER= git grep -E 'error \(.*_(FAILURE|INVALID)' \
+ -- find lib locate xargs \
&& { echo '$(ME): '"Use die() instead of error" 1>&2; \
exit 1; } \
|| :
+sc_prohibit-skip:
+ @prohibit='\|\| skip ' \
+ halt='Use skip_ not skip' \
+ $(_sc_search_regexp)
+
# Disallow the C99 printf size specifiers %z and %j as they're not portable.
# The gnulib printf replacement does support them, however the printf
# replacement is not currently explicitly depended on by the gnulib error()
@@ -114,6 +160,79 @@ sc_prohibit-c99-printf-format:
&& { echo '$(ME): Use PRI*MAX instead of %j or %z' 1>&2; exit 1; } \
|| :
+# Ensure that tests don't use `cmd ... && fail=1` as that hides crashes.
+# The "exclude" expression allows common idioms like `test ... && fail=1`
+# and the 2>... portion allows commands that redirect stderr and so probably
+# independently check its contents and thus detect any crash messages.
+sc_prohibit_and_fail_1:
+ @prohibit='&& fail=1' \
+ exclude='(returns_|stat|kill|test |EGREP|grep|compare|2> *[^/])' \
+ halt='&& fail=1 detected. Please use: returns_ 1 ... || fail=1' \
+ in_vc_files='^tests/' \
+ $(_sc_search_regexp)
+
+# Ensure that env vars are not passed through returns_ as
+# that was seen to fail on FreeBSD /bin/sh at least
+sc_prohibit_env_returns:
+ @prohibit='=[^ ]* returns_ ' \
+ exclude='_ returns_ ' \
+ halt='Passing env vars to returns_ is non portable' \
+ in_vc_files='^tests/' \
+ $(_sc_search_regexp)
+
+# Use framework_failure_, not the old name without the trailing underscore.
+sc_prohibit_framework_failure:
+ @prohibit='\' \
+ halt='use framework_failure_ instead' \
+ $(_sc_search_regexp)
+
+# Prohibit the use of `...` in tests/. Use $(...) instead.
+sc_prohibit_test_backticks:
+ @prohibit='`' in_vc_files='^tests/' \
+ halt='use $$(...), not `...` in tests/' \
+ $(_sc_search_regexp)
+
+# Ensure that compare is used to check empty files
+# so that the unexpected contents are displayed
+sc_prohibit_test_empty:
+ @prohibit='test -s.*&&' in_vc_files='^tests/' \
+ halt='use `compare /dev/null ...`, not `test -s ...` in tests/' \
+ $(_sc_search_regexp)
+
+# Ensure that tests call the get_min_ulimit_v_ function if using ulimit -v
+sc_prohibit_test_ulimit_without_require_:
+ @cd $(srcdir) \
+ && (GIT_PAGER= git grep -l get_min_ulimit_v_ -- tests; \
+ GIT_PAGER= git grep -l 'ulimit -v' -- tests) \
+ | sort | uniq -u | grep . && { echo "$(ME): the above test(s)"\
+ " should match get_min_ulimit_v_ with ulimit -v" 1>&2; exit 1; } || :
+
+# Ensure that tests call the cleanup_ function if using background processes
+sc_prohibit_test_background_without_cleanup_:
+ @cd $(srcdir) \
+ && (GIT_PAGER= git grep -El '( &$$|&[^&]*=\$$!)' -- tests; \
+ GIT_PAGER= git grep -l 'cleanup_()' -- tests | sed p) \
+ | sort | uniq -u | grep . && { echo "$(ME): the above test(s)"\
+ " should use cleanup_ for background processes" 1>&2; exit 1; } || :
+
+# Ensure that tests call the print_ver_ function for programs which are
+# actually used in that test.
+sc_prohibit_test_calls_print_ver_with_irrelevant_argument:
+ @cd $(srcdir) \
+ && GIT_PAGER= git grep -w print_ver_ -- tests \
+ | sed 's#:print_ver_##' \
+ | { fail=0; \
+ while read file name; do \
+ for i in $$name; do \
+ grep -w "$$i" $$file|grep -vw print_ver_|grep -q . \
+ || { fail=1; \
+ echo "*** Test: $$file, offending: $$i." 1>&2; };\
+ done; \
+ done; \
+ test $$fail = 0 || exit 1; \
+ } || { echo "$(ME): the above test(s) call print_ver_ for" \
+ "program(s) they don't use" 1>&2; exit 1; }
+
# Exempt the contents of any usage function from the following.
_continued_string_col_1 = \
s/^usage .*?\n}//ms;/\\\n\w/ and print ("$$ARGV\n"),$$e=1;END{$$e||=0;exit $$e}
@@ -143,6 +262,12 @@ sc_preprocessor_indentation:
echo '$(ME): skipping test $@: cppi not installed' 1>&2; \
fi
+exclude_file_name_regexp--sc_prohibit_test_backticks = \
+ ^tests/(local\.mk|init\.sh)$$
+
+# Now that we have better tests, make this the default.
+export VERBOSE = yes
+
# During 'make update-copyright', convert a sequence with gaps to the minimal
# containing range.
update-copyright-env = \
diff --git a/init.cfg b/init.cfg
new file mode 100644
index 00000000..d4563d5a
--- /dev/null
+++ b/init.cfg
@@ -0,0 +1,702 @@
+# This file is sourced by init.sh, *before* its initialization.
+
+# Copyright (C) 2010-2019 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 .
+
+# This goes hand in hand with the "exec 9>&2;" in tests/Makefile.am's
+# TESTS_ENVIRONMENT definition.
+stderr_fileno_=9
+
+# Having an unsearchable directory in PATH causes execve to fail with EACCES
+# when applied to an unresolvable program name, contrary to the desired ENOENT.
+# Avoid the problem by rewriting PATH to exclude unsearchable directories.
+# Also, if PATH lacks /sbin and/or /usr/sbin, append it/them.
+sanitize_path_()
+{
+ # FIXME: remove double quotes around $IFS when all tests use init.sh.
+ # They constitute a work-around for a bug in FreeBSD 8.1's /bin/sh.
+ local saved_IFS="$IFS"
+ IFS=:
+ set -- $PATH
+ IFS=$saved_IFS
+
+ local d d1
+ local colon=
+ local new_path=
+ for d in "$@"; do
+ test -z "$d" && d1=. || d1=$d
+ if ls -d "$d1/." > /dev/null 2>&1; then
+ new_path="$new_path$colon$d"
+ colon=':'
+ fi
+ done
+
+ for d in /sbin /usr/sbin ; do
+ case ":$new_path:" in
+ *:$d:*) ;;
+ *) new_path="$new_path:$d" ;;
+ esac
+ done
+
+ PATH=$new_path
+ export PATH
+}
+
+getlimits_()
+{
+ eval $(getlimits)
+ test "$INT_MAX" || fatal_ "running getlimits"
+}
+
+require_no_default_acl_()
+{
+ if getfacl --version < /dev/null > /dev/null 2>&1; then
+ getfacl "$1" | grep '^default:' && skip_ 'Default ACL detected'
+ else
+ ls -ld "$1" | grep '.........+' && skip_ 'ACL detected'
+ fi
+}
+
+require_acl_()
+{
+ getfacl --version < /dev/null > /dev/null 2>&1 \
+ && setfacl --version < /dev/null > /dev/null 2>&1 \
+ || skip_ "This test requires getfacl and setfacl."
+
+ id -u bin > /dev/null 2>&1 \
+ || skip_ "This test requires a local user named bin."
+}
+
+is_local_dir_()
+{
+ test $# = 1 || framework_failure_
+ df --local "$1" >/dev/null 2>&1
+}
+
+require_mount_list_()
+{
+ local mount_list_fail='cannot read table of mounted file systems'
+ df --local 2>&1 | grep -F "$mount_list_fail" >/dev/null &&
+ skip_ "$mount_list_fail"
+}
+
+dump_mount_list_()
+{
+ cat /proc/self/mountinfo ||
+ cat /proc/self/mounts ||
+ cat /proc/mounts ||
+ cat /etc/mtab
+}
+
+require_local_dir_()
+{
+ require_mount_list_
+ is_local_dir_ . ||
+ skip_ "This test must be run on a local file system."
+}
+
+require_selinux_()
+{
+ # When in a chroot of an SELinux-enabled system, but with a mock-simulated
+ # SELinux-*disabled* system, recognize that SELinux is disabled system wide:
+ grep 'selinuxfs$' /proc/filesystems > /dev/null \
+ || skip_ "this system lacks SELinux support"
+
+ # Independent of whether SELinux is enabled system-wide,
+ # the current file system may lack SELinux support.
+ # Also the current build may have SELinux support disabled.
+ case $(ls -Zd .) in
+ '? .'|'unlabeled .')
+ test -z "$CONFIG_HEADER" \
+ && framework_failure_ 'CONFIG_HEADER not defined'
+ grep '^#define HAVE_SELINUX_SELINUX_H 1' "$CONFIG_HEADER" > /dev/null \
+ && selinux_missing_="(file) system" || selinux_missing_="build"
+ skip_ "this $selinux_missing_ lacks SELinux support"
+ ;;
+ esac
+}
+
+# Return the SELinux type component if available
+get_selinux_type() { ls -Zd "$1" | sed -n 's/.*:\(.*_t\)[: ].*/\1/p'; }
+
+# Whether SELinux Multi Level Security is enabled
+mls_enabled_() {
+ sestatus 2>&1 |
+ grep 'Policy MLS status:.*enabled' > /dev/null
+}
+
+# Skip this test if we're not in SELinux "enforcing" mode.
+require_selinux_enforcing_()
+{
+ require_selinux_
+ test "$(getenforce)" = Enforcing \
+ || skip_ "This test is useful only with SELinux in Enforcing mode."
+}
+
+require_smack_()
+{
+ grep 'smackfs$' /proc/filesystems > /dev/null \
+ || skip_ "this system lacks SMACK support"
+
+ test "$(ls -Zd .)" != '? .' \
+ || skip_ "this file system lacks SMACK support"
+}
+
+require_openat_support_()
+{
+ # Skip this test if your system has neither the openat-style functions
+ # nor /proc/self/fd support with which to emulate them.
+
+ test -z "$CONFIG_HEADER" \
+ && framework_failure_ 'CONFIG_HEADER not defined'
+
+ _skip=yes
+ grep '^#define HAVE_OPENAT' "$CONFIG_HEADER" > /dev/null && _skip=no
+ test -d /proc/self/fd && _skip=no
+ if test $_skip = yes; then
+ skip_ 'this system lacks openat support'
+ fi
+}
+
+# Return true if command runs with the
+# ulimit specified in the first argument
+ulimit_supported_()
+{
+ local v
+ v="$1"
+ shift
+
+ (
+ # Try to disable core dumps which may
+ # occur with memory constraints
+ trap '' SEGV; ulimit -c 0;
+
+ ulimit -v $v && "$@"
+ ) >/dev/null 2>&1
+}
+
+# Determine the minimum required VM limit to run the given command.
+# Output that value to stdout ... to be used by the caller.
+# Return 0 in case of success, and a non-Zero value otherwise.
+get_min_ulimit_v_()
+{
+ local v
+ local page_size
+
+ # Increase result by this amount to avoid alignment issues
+ page_size=$(getconf PAGESIZE || echo 4096)
+ page_size=$(($page_size / 1024))
+
+ for v in $( seq 5000 5000 50000 ); do
+ if ulimit_supported_ $v "$@"; then
+ local prev_v
+ prev_v=$v
+ for v in $( seq $(($prev_v-1000)) -1000 1000 ); do
+ ulimit_supported_ $v "$@" ||
+ {
+ ret_v=$((prev_v + $page_size))
+ echo $ret_v
+ return 0
+ }
+ prev_v=$v
+ done
+ fi
+ done
+ # The above did not find a working limit. Echo a very small number - just
+ # in case the caller does not handle the non-Zero return value.
+ echo 1; return 1
+}
+
+require_readable_root_()
+{
+ test -r / || skip_ "/ is not readable"
+}
+
+# Skip the current test if strace is not available or doesn't work
+# with the named syscall. Usage: require_strace_ unlink
+require_strace_()
+{
+ test $# = 1 || framework_failure_
+
+ strace -V < /dev/null > /dev/null 2>&1 ||
+ skip_ 'no strace program'
+
+ strace -qe "$1" echo > /dev/null 2>&1 ||
+ skip_ 'strace -qe "'"$1"'" does not work'
+
+ # On some linux/sparc64 systems, strace works fine on 32-bit executables,
+ # but prints only one line of output for every 64-bit executable.
+ strace -o log-help ls --help >/dev/null || framework_failure_
+ n_lines_help=$(wc -l < log-help)
+ rm -f log-help
+ if test $n_lines_help = 0 || test $n_lines_help = 1; then
+ skip_ 'strace produces no more than one line of output'
+ fi
+}
+
+# Skip the current test if valgrind doesn't work,
+# which could happen if not installed,
+# or hasn't support for the built architecture,
+# or hasn't appropriate error suppressions installed etc.
+require_valgrind_()
+{
+ valgrind --error-exitcode=1 true 2>/dev/null ||
+ skip_ "requires a working valgrind"
+}
+
+# Skip the current test if setfacl doesn't work on the current file system,
+# which could happen if not installed, or if ACLs are not supported by the
+# kernel or the file system, or are turned off via mount options.
+#
+# Work around the following two issues:
+#
+# 1) setfacl maps ACLs into file permission bits if on "noacl" file systems.
+#
+# On file systems which do not support ACLs (e.g. ext4 mounted with -o noacl),
+# setfacl operates on the regular file permission bits, and only fails if the
+# given ACL spec does not fit into there. Thus, to test if ACLs really work
+# on the current file system, pass an ACL spec which can't be mapped that way.
+# "Default" ACLs (-d) seem to fulfill this requirement.
+#
+# 2) setfacl only invokes the underlying system call if the ACL would change.
+#
+# If the given ACL spec would not change the ACLs on the file, then setfacl
+# does not invoke the underlying system call - setxattr(). Therefore, to test
+# if setting ACLs really works on the current file system, call setfacl twice
+# with conflictive ACL specs.
+require_setfacl_()
+{
+ local d='acltestdir_'
+ mkdir $d || framework_failure_
+ local f=0
+
+ setfacl -d -m user::r-x $d \
+ && setfacl -d -m user::rwx $d \
+ || f=1
+ rm -rf $d || framework_failure_
+ test $f = 0 \
+ || skip_ "setfacl does not work on the current file system"
+}
+
+# Require a controlling input 'terminal'.
+require_controlling_input_terminal_()
+{
+ have_input_tty=yes
+ tty -s || have_input_tty=no
+ test -t 0 || have_input_tty=no
+ if test "$have_input_tty" = no; then
+ skip_ 'requires controlling input terminal
+This test must have a controlling input "terminal", so it may not be
+run via "batch", "at", or "ssh". On some systems, it may not even be
+run in the background.'
+ fi
+}
+
+require_built_()
+{
+ skip_=no
+ for i in "$@"; do
+ case " $built_programs " in
+ *" $i "*) ;;
+ *) echo "$i: not built" 1>&2; skip_=yes ;;
+ esac
+ done
+
+ test $skip_ = yes && skip_ "required program(s) not built"
+}
+
+require_file_system_bytes_free_()
+{
+ local req=$1
+ local expr=$(stat -f --printf "$req / %S <= %a" .)
+ $AWK "BEGIN{ exit !($expr) }" \
+ || skip_ "this test needs at least $req bytes of free space"
+}
+
+uid_is_privileged_()
+{
+ # Make sure id -u succeeds.
+ my_uid=$(id -u) \
+ || { echo "$0: cannot run 'id -u'" 1>&2; return 1; }
+
+ # Make sure it gives valid output.
+ case $my_uid in
+ 0) ;;
+ *[!0-9]*)
+ echo "$0: invalid output ('$my_uid') from 'id -u'" 1>&2
+ return 1 ;;
+ *) return 1 ;;
+ esac
+}
+
+get_process_status_()
+{
+ sed -n '/^State:[ ]*\([[:alpha:]]\).*/s//\1/p' /proc/$1/status
+}
+
+# Convert an ls-style permission string, like drwxr----x and -rw-r-x-wx
+# to the equivalent chmod --mode (-m) argument, (=,u=rwx,g=r,o=x and
+# =,u=rw,g=rx,o=wx). Ignore ACLs.
+rwx_to_mode_()
+{
+ case $# in
+ 1) rwx=$1;;
+ *) echo "$0: wrong number of arguments" 1>&2
+ echo "Usage: $0 ls-style-mode-string" 1>&2
+ return;;
+ esac
+
+ case $rwx in
+ [ld-][rwx-][rwx-][rwxsS-][rwx-][rwx-][rwxsS-][rwx-][rwx-][rwxtT-]) ;;
+ [ld-][rwx-][rwx-][rwxsS-][rwx-][rwx-][rwxsS-][rwx-][rwx-][rwxtT-][+.]) ;;
+ *) echo "$0: invalid mode string: $rwx" 1>&2; return;;
+ esac
+
+ # Perform these conversions:
+ # S s
+ # s xs
+ # T t
+ # t xt
+ # The 'T' and 't' ones are only valid for 'other'.
+ s='s/S/@/;s/s/x@/;s/@/s/'
+ t='s/T/@/;s/t/x@/;s/@/t/'
+
+ u=$(echo $rwx|sed 's/^.\(...\).*/,u=\1/;s/-//g;s/^,u=$//;'$s)
+ g=$(echo $rwx|sed 's/^....\(...\).*/,g=\1/;s/-//g;s/^,g=$//;'$s)
+ o=$(echo $rwx|sed 's/^.......\(...\).*/,o=\1/;s/-//g;s/^,o=$//;'$s';'$t)
+ echo "=$u$g$o"
+}
+
+skip_if_()
+{
+ case $1 in
+ root) skip_ must be run as root ;;
+ non-root) skip_ must be run as non-root ;;
+ *) ;; # FIXME?
+ esac
+}
+
+very_expensive_()
+{
+ if test "$RUN_VERY_EXPENSIVE_TESTS" != yes; then
+ skip_ 'very expensive: disabled by default
+This test is very expensive, so it is disabled by default.
+To run it anyway, rerun make check with the RUN_VERY_EXPENSIVE_TESTS
+environment variable set to yes. E.g.,
+
+ env RUN_VERY_EXPENSIVE_TESTS=yes make check
+
+or use the shortcut target of the toplevel Makefile,
+
+ make check-very-expensive
+'
+ fi
+}
+
+expensive_()
+{
+ if test "$RUN_EXPENSIVE_TESTS" != yes; then
+ skip_ 'expensive: disabled by default
+This test is relatively expensive, so it is disabled by default.
+To run it anyway, rerun make check with the RUN_EXPENSIVE_TESTS
+environment variable set to yes. E.g.,
+
+ env RUN_EXPENSIVE_TESTS=yes make check
+
+or use the shortcut target of the toplevel Makefile,
+
+ make check-expensive
+'
+ fi
+}
+
+# Test whether we can run our just-built root owned find,
+# i.e., that $NON_ROOT_USERNAME has access to the build directory.
+nonroot_has_perm_()
+{
+ require_built_ chroot
+
+ local find_version=$(
+ chroot --skip-chdir --user=$NON_ROOT_USERNAME / env PATH="$PATH" \
+ find --version |
+ sed -n '1s/.* //p'
+ )
+ case ":$find_version:" in
+ :$PACKAGE_VERSION:) ;;
+ *) return 1;;
+ esac
+}
+
+require_root_()
+{
+ uid_is_privileged_ || skip_ "must be run as root"
+ NON_ROOT_USERNAME=${NON_ROOT_USERNAME=nobody}
+ NON_ROOT_GID=${NON_ROOT_GID=$(id -g $NON_ROOT_USERNAME)}
+
+ # When the current test invokes chroot, call nonroot_has_perm_
+ # to check for a common problem.
+ grep '^[ ]*chroot' "../$0" \
+ && { nonroot_has_perm_ \
+ || skip_ "user $NON_ROOT_USERNAME lacks execute permissions"; }
+}
+
+skip_if_root_() { uid_is_privileged_ && skip_ "must be run as non-root"; }
+
+# Set 'groups' to a space-separated list of at least two groups
+# of which the user is a member.
+require_membership_in_two_groups_()
+{
+ test $# = 0 || framework_failure_
+
+ groups=${FINDUTILS_GROUPS-$( (id -G || /usr/xpg4/bin/id -G) 2>/dev/null)}
+ case "$groups" in
+ *' '*) ;;
+ *) skip_ 'requires membership in two groups
+this test requires that you be a member of more than one group,
+but running '\''id -G'\'' either failed or found just one. If you really
+are a member of at least two groups, then rerun this test with
+FINDUTILS_GROUPS set in your environment to the space-separated list
+of group names or numbers. E.g.,
+
+ env FINDUTILS_GROUPS='\''users cdrom'\'' make check
+
+'
+ ;;
+ esac
+}
+
+# Is /proc/$PID/status supported?
+require_proc_pid_status_()
+{
+ sleep 2 &
+ local pid=$!
+ sleep .5
+ grep '^State:[ ]*[S]' /proc/$pid/status > /dev/null 2>&1 ||
+ skip_ "/proc/$pid/status: missing or 'different'"
+ kill $pid
+}
+
+# Does trap support signal names?
+# Old versions of ash did not.
+require_trap_signame_()
+{
+ (trap '' CHLD) || skip_ 'requires trap with signal name support'
+}
+
+# Does kill support sending signal to whole group?
+# dash 0.5.8 at least does not.
+require_kill_group_()
+{
+ kill -0 -- -1 || skip_ 'requires kill with group signalling support'
+}
+
+# Return nonzero if the specified path is on a file system for
+# which FIEMAP support exists. Note some file systems (like ext3 and btrfs)
+# only support FIEMAP for files, not directories.
+fiemap_capable_()
+{
+ if ! python < /dev/null; then
+ warn_ 'fiemap_capable_: python missing: assuming not fiemap capable'
+ return 1
+ fi
+ python "$abs_srcdir"/tests/fiemap-capable "$@"
+}
+
+# Skip the current test if "." lacks d_type support.
+require_dirent_d_type_()
+{
+ python < /dev/null \
+ || skip_ python missing: assuming no d_type support
+
+ python "$abs_srcdir"/tests/d_type-check \
+ || skip_ requires d_type support
+}
+
+# Skip the current test if we lack Perl.
+require_perl_()
+{
+ : ${PERL=perl}
+ $PERL -e 'use warnings' > /dev/null 2>&1 \
+ || skip_ 'configure did not find a usable version of Perl'
+}
+
+# Does the current (working-dir) file system support sparse files?
+require_sparse_support_()
+{
+ test $# = 0 || framework_failure_
+ # Test whether we can create a sparse file.
+ # For example, on Darwin6.5 with a file system of type hfs, it's not possible.
+ # NTFS requires 128K before a hole appears in a sparse file.
+ t=sparse.$$
+ dd bs=1 seek=128K of=$t < /dev/null 2> /dev/null
+ set x $(du -sk $t)
+ kb_size=$2
+ rm -f $t
+ if test $kb_size -ge 128; then
+ skip_ 'this file system does not support sparse files'
+ fi
+}
+
+# Compile a shared lib using the GCC options for doing so.
+# Pass input and output file as parameters respectively.
+# Any other optional parmeters are passed to $CC.
+gcc_shared_()
+{
+ local in=$1
+ local out=$2
+ shift 2 || return 1
+
+ $CC -Wall -shared --std=gnu99 -fPIC -O2 $* "$in" -o "$out" -ldl
+}
+
+# There are a myriad of ways to build shared libs,
+# so we only consider running tests requiring shared libs,
+# on platforms that support building them as follows.
+require_gcc_shared_()
+{
+ gcc_shared_ '-' 'd.so' -xc < /dev/null 2>&1 \
+ || skip_ '$CC -shared ... failed to build a shared lib'
+ rm -f d.so
+}
+
+mkfifo_or_skip_()
+{
+ test $# = 1 || framework_failure_
+ if ! mkfifo "$1"; then
+ # Make an exception of this case -- usually we interpret framework-creation
+ # failure as a test failure. However, in this case, when running on a SunOS
+ # system using a disk NFS mounted from OpenBSD, the above fails like this:
+ # mkfifo: cannot make fifo 'fifo-10558': Not owner
+ skip_ 'unable to create a fifo'
+ fi
+}
+
+# Disable the current test if the working directory seems to have
+# the setgid bit set.
+skip_if_setgid_()
+{
+ setgid_tmpdir=setgid-$$
+ (umask 77; mkdir $setgid_tmpdir)
+ perms=$(stat --printf %A $setgid_tmpdir)
+ rmdir $setgid_tmpdir
+ case $perms in
+ drwx------);;
+ drwxr-xr-x);; # Windows98 + DJGPP 2.03
+ *) skip_ 'this directory has the setgid bit set';;
+ esac
+}
+
+# Skip if files are created with a different group to the current user
+# This can happen due to a setgid dir, or by some other mechanism on OS X:
+# https://unix.stackexchange.com/q/63865
+# https://bugs.gnu.org/14024#41
+skip_if_nondefault_group_()
+{
+ touch grp.$$
+ gen_ug=$(stat -c '%u:%g' grp.$$)
+ rm grp.$$
+ test "$gen_ug" = "$(id -ru):$(id -rg)" ||
+ skip_ 'Files are created with a different gid'
+}
+
+skip_if_mcstransd_is_running_()
+{
+ test $# = 0 || framework_failure_
+
+ # When mcstransd is running, you'll see only the 3-component
+ # version of file-system context strings. Detect that,
+ # and if it's running, skip this test.
+ __ctx=$(stat --printf='%C\n' .) || framework_failure_
+ case $__ctx in
+ *:*:*:*) __ctx_ok=1 ;; # four components is ok
+ *:*:*) # three components is ok too if there is no MLS
+ mls_enabled_ || __ctx_ok=1 ;;
+ esac
+
+ test "$__ctx_ok" ||
+ skip_ "unexpected context '$__ctx'; turn off mcstransd"
+}
+
+# Skip the current test if umask doesn't work as usual.
+# This test should be run in the temporary directory that ends
+# up being removed via the trap commands.
+working_umask_or_skip_()
+{
+ umask 022
+ touch file1 file2
+ chmod 644 file2
+ perms=$(ls -l file1 file2 | sed 's/ .*//' | uniq)
+ rm -f file1 file2
+
+ case $perms in
+ *'
+ '*) skip_ 'your build directory has unusual umask semantics'
+ esac
+}
+
+# Retry a function requiring a sufficient delay to _pass_
+# using a truncated exponential backoff method.
+# Example: retry_delay_ dd_reblock_1 .1 6
+# This example will call the dd_reblock_1 function with
+# an initial delay of .1 second and call it at most 6 times
+# with a max delay of 3.2s (doubled each time), or a total of 6.3s
+# Note ensure you do _not_ quote the parameter to GNU sleep in
+# your function, as it may contain separate values that sleep
+# needs to accumulate.
+# Further function arguments will be forwarded to the test function.
+retry_delay_()
+{
+ local test_func=$1
+ local init_delay=$2
+ local max_n_tries=$3
+ shift 3 || return 1
+
+ local attempt=1
+ local num_sleeps=$attempt
+ local time_fail
+ while test $attempt -le $max_n_tries; do
+ local delay=$($AWK -v n=$num_sleeps -v s="$init_delay" \
+ 'BEGIN { print s * n }')
+ "$test_func" "$delay" "$@" && { time_fail=0; break; } || time_fail=1
+ attempt=$(expr $attempt + 1)
+ num_sleeps=$(expr $num_sleeps '*' 2)
+ done
+ test "$time_fail" = 0
+}
+
+# Call this with a list of programs under test immediately after
+# sourcing init.sh.
+print_ver_()
+{
+ require_built_ "$@"
+ if test "$VERBOSE" = yes; then
+ local i
+ for i in $*; do
+ env $i --version
+ done
+ fi
+}
+
+# Are we running on GNU/Hurd?
+require_gnu_()
+{
+ test "$(uname)" = GNU \
+ || skip_ 'not running on GNU/Hurd'
+}
+
+# Prepend all our source directories to PATH.
+path_prepend_ "${srcdir=.}/find" "${srcdir=.}/locate" "${srcdir=.}/xargs"
+
+sanitize_path_
diff --git a/tests/.gitignore b/tests/.gitignore
new file mode 100644
index 00000000..702a9418
--- /dev/null
+++ b/tests/.gitignore
@@ -0,0 +1,3 @@
+/*/*.log
+/*/*.trs
+/test-suite.log
diff --git a/tests/GNUmakefile b/tests/GNUmakefile
new file mode 100644
index 00000000..3c178a2c
--- /dev/null
+++ b/tests/GNUmakefile
@@ -0,0 +1,20 @@
+# Provide a compatibility layer so that the commands used before the
+# conversion of tests/ to non-recursive make still work. To do that, we
+# must rerun the "make check" from the parent, and with tests/ prefixed
+# onto any TESTS values. The SUBDIRS=. is to prevent the top-level check
+# rules from descending into e.g., gnulib-test/.
+
+.PHONY: all
+all:
+ @echo 'tests/GNUmakefile: did you mean to make "check"?' 1>&2
+ @exit 1
+
+ifeq ($(TESTS),)
+tests =
+else
+tests = TESTS=$(addprefix tests/,$(TESTS))
+endif
+
+.PHONY: check
+check:
+ cd .. && $(MAKE) $@ $(tests) SUBDIRS=.
diff --git a/tests/envvar-check b/tests/envvar-check
new file mode 100644
index 00000000..c2c7a754
--- /dev/null
+++ b/tests/envvar-check
@@ -0,0 +1,58 @@
+# -*- sh -*-
+# Check environment variables for sane values while testing.
+
+# Copyright (C) 2000-2019 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 .
+
+if (FOO=FOO; unset FOO) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+envvar_check_fail=0
+vars='
+ _POSIX2_VERSION
+ _STDBUF_E
+ _STDBUF_I
+ _STDBUF_O
+ BASH_ENV
+ BLOCKSIZE
+ BLOCK_SIZE
+ CDPATH
+ COLUMNS
+ ENV
+ LANGUAGE
+ POSIXLY_CORRECT
+ QUOTING_STYLE
+ SIMPLE_BACKUP_SUFFIX
+ TABSIZE
+ TERM
+ COLORTERM
+ 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_fail=1
+ fi
+done
+
+test "$envvar_check_fail" = 1 && exit 1
diff --git a/tests/init.sh b/tests/init.sh
new file mode 100755
index 00000000..0d6ddee3
--- /dev/null
+++ b/tests/init.sh
@@ -0,0 +1,618 @@
+# source this file; set up for tests
+
+# Copyright (C) 2009-2019 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 .
+
+# Using this file in a test
+# =========================
+#
+# The typical skeleton of a test looks like this:
+#
+# #!/bin/sh
+# . "${srcdir=.}/init.sh"; path_prepend_ .
+# Execute some commands.
+# Note that these commands are executed in a subdirectory, therefore you
+# need to prepend "../" to relative filenames in the build directory.
+# Note that the "path_prepend_ ." is useful only if the body of your
+# test invokes programs residing in the initial directory.
+# For example, if the programs you want to test are in src/, and this test
+# script is named tests/test-1, then you would use "path_prepend_ ../src",
+# or perhaps export PATH='$(abs_top_builddir)/src$(PATH_SEPARATOR)'"$$PATH"
+# to all tests via automake's TESTS_ENVIRONMENT.
+# Set the exit code 0 for success, 77 for skipped, or 1 or other for failure.
+# Use the skip_ and fail_ functions to print a diagnostic and then exit
+# with the corresponding exit code.
+# Exit $?
+
+# Executing a test that uses this file
+# ====================================
+#
+# Running a single test:
+# $ make check TESTS=test-foo.sh
+#
+# Running a single test, with verbose output:
+# $ make check TESTS=test-foo.sh VERBOSE=yes
+#
+# Running a single test, keeping the temporary directory:
+# $ make check TESTS=test-foo.sh KEEP=yes
+#
+# Running a single test, with single-stepping:
+# 1. Go into a sub-shell:
+# $ bash
+# 2. Set relevant environment variables from TESTS_ENVIRONMENT in the
+# Makefile:
+# $ export srcdir=../../tests # this is an example
+# 3. Execute the commands from the test, copy&pasting them one by one:
+# $ . "$srcdir/init.sh"; path_prepend_ .
+# ...
+# 4. Finally
+# $ exit
+
+ME_=`expr "./$0" : '.*/\(.*\)$'`
+
+# Prepare PATH_SEPARATOR.
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ # Determine PATH_SEPARATOR by trying to find /bin/sh in a PATH which
+ # contains only /bin. Note that ksh looks also at the FPATH variable,
+ # so we have to set that as well for the test.
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 \
+ && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 \
+ || PATH_SEPARATOR=';'
+ }
+fi
+
+# We use a trap below for cleanup. This requires us to go through
+# hoops to get the right exit status transported through the handler.
+# So use 'Exit STATUS' instead of 'exit STATUS' inside of the tests.
+# Turn off errexit here so that we don't trip the bug with OSF1/Tru64
+# sh inside this function.
+Exit () { set +e; (exit $1); exit $1; }
+
+# Print warnings (e.g., about skipped and failed tests) to this file number.
+# Override by defining to say, 9, in init.cfg, and putting say,
+# export ...ENVVAR_SETTINGS...; $(SHELL) 9>&2
+# in the definition of TESTS_ENVIRONMENT in your tests/Makefile.am file.
+# This is useful when using automake's parallel tests mode, to print
+# the reason for skip/failure to console, rather than to the .log files.
+: ${stderr_fileno_=2}
+
+# Note that correct expansion of "$*" depends on IFS starting with ' '.
+# Always write the full diagnostic to stderr.
+# When stderr_fileno_ is not 2, also emit the first line of the
+# diagnostic to that file descriptor.
+warn_ ()
+{
+ # If IFS does not start with ' ', set it and emit the warning in a subshell.
+ case $IFS in
+ ' '*) printf '%s\n' "$*" >&2
+ test $stderr_fileno_ = 2 \
+ || { printf '%s\n' "$*" | sed 1q >&$stderr_fileno_ ; } ;;
+ *) (IFS=' '; warn_ "$@");;
+ esac
+}
+fail_ () { warn_ "$ME_: failed test: $@"; Exit 1; }
+skip_ () { warn_ "$ME_: skipped test: $@"; Exit 77; }
+fatal_ () { warn_ "$ME_: hard error: $@"; Exit 99; }
+framework_failure_ () { warn_ "$ME_: set-up failure: $@"; Exit 99; }
+
+# This is used to simplify checking of the return value
+# which is useful when ensuring a command fails as desired.
+# I.e., just doing `command ... &&fail=1` will not catch
+# a segfault in command for example. With this helper you
+# instead check an explicit exit code like
+# returns_ 1 command ... || fail
+returns_ () {
+ # Disable tracing so it doesn't interfere with stderr of the wrapped command
+ { set +x; } 2>/dev/null
+
+ local exp_exit="$1"
+ shift
+ "$@"
+ test $? -eq $exp_exit && ret_=0 || ret_=1
+
+ if test "$VERBOSE" = yes && test "$gl_set_x_corrupts_stderr_" = false; then
+ set -x
+ fi
+ { return $ret_; } 2>/dev/null
+}
+
+# Sanitize this shell to POSIX mode, if possible.
+DUALCASE=1; export DUALCASE
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in
+ *posix*) set -o posix ;;
+ esac
+fi
+
+# We require $(...) support unconditionally.
+# We require non-surprising "local" semantics (this eliminates dash).
+# This takes the admittedly draconian step of eliminating dash, because the
+# assignment tab=$(printf '\t') works fine, yet preceding it with "local "
+# transforms it into an assignment that sets the variable to the empty string.
+# That is too counter-intuitive, and can lead to subtle run-time malfunction.
+# The example below is less subtle in that with dash, it evokes the run-time
+# exception "dash: 1: local: 1: bad variable name".
+# We require a few additional shell features only when $EXEEXT is nonempty,
+# in order to support automatic $EXEEXT emulation:
+# - hyphen-containing alias names
+# - we prefer to use ${var#...} substitution, rather than having
+# to work around lack of support for that feature.
+# The following code attempts to find a shell with support for these features.
+# If the current shell passes the test, we're done. Otherwise, test other
+# shells until we find one that passes. If one is found, re-exec it.
+# If no acceptable shell is found, skip the current test.
+#
+# The "...set -x; P=1 true 2>err..." test is to disqualify any shell that
+# emits "P=1" into err, as /bin/sh from SunOS 5.11 and OpenBSD 4.7 do.
+#
+# Use "9" to indicate success (rather than 0), in case some shell acts
+# like Solaris 10's /bin/sh but exits successfully instead of with status 2.
+
+# Eval this code in a subshell to determine a shell's suitability.
+# 10 - passes all tests; ok to use
+# 9 - ok, but enabling "set -x" corrupts app stderr; prefer higher score
+# ? - not ok
+gl_shell_test_script_='
+test $(echo y) = y || exit 1
+f_local_() { local v=1; }; f_local_ || exit 1
+f_dash_local_fail_() { local t=$(printf " 1"); }; f_dash_local_fail_
+score_=10
+if test "$VERBOSE" = yes; then
+ test -n "$( (exec 3>&1; set -x; P=1 true 2>&3) 2> /dev/null)" && score_=9
+fi
+test -z "$EXEEXT" && exit $score_
+shopt -s expand_aliases
+alias a-b="echo zoo"
+v=abx
+ test ${v%x} = ab \
+ && test ${v#a} = bx \
+ && test $(a-b) = zoo \
+ && exit $score_
+'
+
+if test "x$1" = "x--no-reexec"; then
+ shift
+else
+ # Assume a working shell. Export to subshells (setup_ needs this).
+ gl_set_x_corrupts_stderr_=false
+ export gl_set_x_corrupts_stderr_
+
+ # Record the first marginally acceptable shell.
+ marginal_=
+
+ # Search for a shell that meets our requirements.
+ for re_shell_ in __current__ "${CONFIG_SHELL:-no_shell}" \
+ /bin/sh bash dash zsh pdksh fail
+ do
+ test "$re_shell_" = no_shell && continue
+
+ # If we've made it all the way to the sentinel, "fail" without
+ # finding even a marginal shell, skip this test.
+ if test "$re_shell_" = fail; then
+ test -z "$marginal_" && skip_ failed to find an adequate shell
+ re_shell_=$marginal_
+ break
+ fi
+
+ # When testing the current shell, simply "eval" the test code.
+ # Otherwise, run it via $re_shell_ -c ...
+ if test "$re_shell_" = __current__; then
+ # 'eval'ing this code makes Solaris 10's /bin/sh exit with
+ # $? set to 2. It does not evaluate any of the code after the
+ # "unexpected" first '('. Thus, we must run it in a subshell.
+ ( eval "$gl_shell_test_script_" ) > /dev/null 2>&1
+ else
+ "$re_shell_" -c "$gl_shell_test_script_" 2>/dev/null
+ fi
+
+ st_=$?
+
+ # $re_shell_ works just fine. Use it.
+ if test $st_ = 10; then
+ gl_set_x_corrupts_stderr_=false
+ break
+ fi
+
+ # If this is our first marginally acceptable shell, remember it.
+ if test "$st_:$marginal_" = 9: ; then
+ marginal_="$re_shell_"
+ gl_set_x_corrupts_stderr_=true
+ fi
+ done
+
+ if test "$re_shell_" != __current__; then
+ # Found a usable shell. Preserve -v and -x.
+ case $- in
+ *v*x* | *x*v*) opts_=-vx ;;
+ *v*) opts_=-v ;;
+ *x*) opts_=-x ;;
+ *) opts_= ;;
+ esac
+ re_shell=$re_shell_
+ export re_shell
+ exec "$re_shell_" $opts_ "$0" --no-reexec "$@"
+ echo "$ME_: exec failed" 1>&2
+ exit 127
+ fi
+fi
+
+# If this is bash, turn off all aliases.
+test -n "$BASH_VERSION" && unalias -a
+
+# Note that when supporting $EXEEXT (transparently mapping from PROG_NAME to
+# PROG_NAME.exe), we want to support hyphen-containing names like test-acos.
+# That is part of the shell-selection test above. Why use aliases rather
+# than functions? Because support for hyphen-containing aliases is more
+# widespread than that for hyphen-containing function names.
+test -n "$EXEEXT" && test -n "$BASH_VERSION" && shopt -s expand_aliases
+
+# Enable glibc's malloc-perturbing option.
+# This is useful for exposing code that depends on the fact that
+# malloc-related functions often return memory that is mostly zeroed.
+# If you have the time and cycles, use valgrind to do an even better job.
+: ${MALLOC_PERTURB_=87}
+export MALLOC_PERTURB_
+
+# This is a stub function that is run upon trap (upon regular exit and
+# interrupt). Override it with a per-test function, e.g., to unmount
+# a partition, or to undo any other global state changes.
+cleanup_ () { :; }
+
+# Emit a header similar to that from diff -u; Print the simulated "diff"
+# command so that the order of arguments is clear. Don't bother with @@ lines.
+emit_diff_u_header_ ()
+{
+ printf '%s\n' "diff -u $*" \
+ "--- $1 1970-01-01" \
+ "+++ $2 1970-01-01"
+}
+
+# Arrange not to let diff or cmp operate on /dev/null,
+# since on some systems (at least OSF/1 5.1), that doesn't work.
+# When there are not two arguments, or no argument is /dev/null, return 2.
+# When one argument is /dev/null and the other is not empty,
+# cat the nonempty file to stderr and return 1.
+# Otherwise, return 0.
+compare_dev_null_ ()
+{
+ test $# = 2 || return 2
+
+ if test "x$1" = x/dev/null; then
+ test -s "$2" || return 0
+ emit_diff_u_header_ "$@"; sed 's/^/+/' "$2"
+ return 1
+ fi
+
+ if test "x$2" = x/dev/null; then
+ test -s "$1" || return 0
+ emit_diff_u_header_ "$@"; sed 's/^/-/' "$1"
+ return 1
+ fi
+
+ return 2
+}
+
+for diff_opt_ in -u -U3 -c '' no; do
+ test "$diff_opt_" != no &&
+ diff_out_=`exec 2>/dev/null; diff $diff_opt_ "$0" "$0" < /dev/null` &&
+ break
+done
+if test "$diff_opt_" != no; then
+ if test -z "$diff_out_"; then
+ compare_ () { diff $diff_opt_ "$@"; }
+ else
+ compare_ ()
+ {
+ # If no differences were found, AIX and HP-UX 'diff' produce output
+ # like "No differences encountered". Hide this output.
+ diff $diff_opt_ "$@" > diff.out
+ diff_status_=$?
+ test $diff_status_ -eq 0 || cat diff.out || diff_status_=2
+ rm -f diff.out || diff_status_=2
+ return $diff_status_
+ }
+ fi
+elif cmp -s /dev/null /dev/null 2>/dev/null; then
+ compare_ () { cmp -s "$@"; }
+else
+ compare_ () { cmp "$@"; }
+fi
+
+# Usage: compare EXPECTED ACTUAL
+#
+# Given compare_dev_null_'s preprocessing, defer to compare_ if 2 or more.
+# Otherwise, propagate $? to caller: any diffs have already been printed.
+compare ()
+{
+ # This looks like it can be factored to use a simple "case $?"
+ # after unchecked compare_dev_null_ invocation, but that would
+ # fail in a "set -e" environment.
+ if compare_dev_null_ "$@"; then
+ return 0
+ else
+ case $? in
+ 1) return 1;;
+ *) compare_ "$@";;
+ esac
+ fi
+}
+
+# An arbitrary prefix to help distinguish test directories.
+testdir_prefix_ () { printf gt; }
+
+# Run the user-overridable cleanup_ function, remove the temporary
+# directory and exit with the incoming value of $?.
+remove_tmp_ ()
+{
+ __st=$?
+ cleanup_
+ if test "$KEEP" = yes; then
+ echo "Not removing temporary directory $test_dir_"
+ else
+ # cd out of the directory we're about to remove
+ cd "$initial_cwd_" || cd / || cd /tmp
+ chmod -R u+rwx "$test_dir_"
+ # If removal fails and exit status was to be 0, then change it to 1.
+ rm -rf "$test_dir_" || { test $__st = 0 && __st=1; }
+ fi
+ exit $__st
+}
+
+# Given a directory name, DIR, if every entry in it that matches *.exe
+# contains only the specified bytes (see the case stmt below), then print
+# a space-separated list of those names and return 0. Otherwise, don't
+# print anything and return 1. Naming constraints apply also to DIR.
+find_exe_basenames_ ()
+{
+ feb_dir_=$1
+ feb_fail_=0
+ feb_result_=
+ feb_sp_=
+ for feb_file_ in $feb_dir_/*.exe; do
+ # If there was no *.exe file, or there existed a file named "*.exe" that
+ # was deleted between the above glob expansion and the existence test
+ # below, just skip it.
+ test "x$feb_file_" = "x$feb_dir_/*.exe" && test ! -f "$feb_file_" \
+ && continue
+ # Exempt [.exe, since we can't create a function by that name, yet
+ # we can't invoke [ by PATH search anyways due to shell builtins.
+ test "x$feb_file_" = "x$feb_dir_/[.exe" && continue
+ case $feb_file_ in
+ *[!-a-zA-Z/0-9_.+]*) feb_fail_=1; break;;
+ *) # Remove leading file name components as well as the .exe suffix.
+ feb_file_=${feb_file_##*/}
+ feb_file_=${feb_file_%.exe}
+ feb_result_="$feb_result_$feb_sp_$feb_file_";;
+ esac
+ feb_sp_=' '
+ done
+ test $feb_fail_ = 0 && printf %s "$feb_result_"
+ return $feb_fail_
+}
+
+# Consider the files in directory, $1.
+# For each file name of the form PROG.exe, create an alias named
+# PROG that simply invokes PROG.exe, then return 0. If any selected
+# file name or the directory name, $1, contains an unexpected character,
+# define no alias and return 1.
+create_exe_shims_ ()
+{
+ case $EXEEXT in
+ '') return 0 ;;
+ .exe) ;;
+ *) echo "$0: unexpected \$EXEEXT value: $EXEEXT" 1>&2; return 1 ;;
+ esac
+
+ base_names_=`find_exe_basenames_ $1` \
+ || { echo "$0 (exe_shim): skipping directory: $1" 1>&2; return 0; }
+
+ if test -n "$base_names_"; then
+ for base_ in $base_names_; do
+ alias "$base_"="$base_$EXEEXT"
+ done
+ fi
+
+ return 0
+}
+
+# Use this function to prepend to PATH an absolute name for each
+# specified, possibly-$initial_cwd_-relative, directory.
+path_prepend_ ()
+{
+ while test $# != 0; do
+ path_dir_=$1
+ case $path_dir_ in
+ '') fail_ "invalid path dir: '$1'";;
+ /* | ?:*) abs_path_dir_=$path_dir_;;
+ *) abs_path_dir_=$initial_cwd_/$path_dir_;;
+ esac
+ case $abs_path_dir_ in
+ *$PATH_SEPARATOR*) fail_ "invalid path dir: '$abs_path_dir_'";;
+ esac
+ PATH="$abs_path_dir_$PATH_SEPARATOR$PATH"
+
+ # Create an alias, FOO, for each FOO.exe in this directory.
+ create_exe_shims_ "$abs_path_dir_" \
+ || fail_ "something failed (above): $abs_path_dir_"
+ shift
+ done
+ export PATH
+}
+
+setup_ ()
+{
+ if test "$VERBOSE" = yes; then
+ # Test whether set -x may cause the selected shell to corrupt an
+ # application's stderr. Many do, including zsh-4.3.10 and the /bin/sh
+ # from SunOS 5.11, OpenBSD 4.7 and Irix 5.x and 6.5.
+ # If enabling verbose output this way would cause trouble, simply
+ # issue a warning and refrain.
+ if $gl_set_x_corrupts_stderr_; then
+ warn_ "using SHELL=$SHELL with 'set -x' corrupts stderr"
+ else
+ set -x
+ fi
+ fi
+
+ initial_cwd_=$PWD
+
+ pfx_=`testdir_prefix_`
+ test_dir_=`mktempd_ "$initial_cwd_" "$pfx_-$ME_.XXXX"` \
+ || fail_ "failed to create temporary directory in $initial_cwd_"
+ cd "$test_dir_" || fail_ "failed to cd to temporary directory"
+
+ # As autoconf-generated configure scripts do, ensure that IFS
+ # is defined initially, so that saving and restoring $IFS works.
+ gl_init_sh_nl_='
+'
+ IFS=" "" $gl_init_sh_nl_"
+
+ # This trap statement, along with a trap on 0 below, ensure that the
+ # temporary directory, $test_dir_, is removed upon exit as well as
+ # upon receipt of any of the listed signals.
+ for sig_ in 1 2 3 13 15; do
+ eval "trap 'Exit $(expr $sig_ + 128)' $sig_"
+ done
+}
+
+# Create a temporary directory, much like mktemp -d does.
+# Written by Jim Meyering.
+#
+# Usage: mktempd_ /tmp phoey.XXXXXXXXXX
+#
+# First, try to use the mktemp program.
+# Failing that, we'll roll our own mktemp-like function:
+# - try to get random bytes from /dev/urandom
+# - failing that, generate output from a combination of quickly-varying
+# sources and gzip. Ignore non-varying gzip header, and extract
+# "random" bits from there.
+# - given those bits, map to file-name bytes using tr, and try to create
+# the desired directory.
+# - make only $MAX_TRIES_ attempts
+
+# Helper function. Print $N pseudo-random bytes from a-zA-Z0-9.
+rand_bytes_ ()
+{
+ n_=$1
+
+ # Maybe try openssl rand -base64 $n_prime_|tr '+/=\012' abcd first?
+ # But if they have openssl, they probably have mktemp, too.
+
+ chars_=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
+ dev_rand_=/dev/urandom
+ if test -r "$dev_rand_"; then
+ # Note: 256-length($chars_) == 194; 3 copies of $chars_ is 186 + 8 = 194.
+ dd ibs=$n_ count=1 if=$dev_rand_ 2>/dev/null \
+ | LC_ALL=C tr -c $chars_ 01234567$chars_$chars_$chars_
+ return
+ fi
+
+ n_plus_50_=`expr $n_ + 50`
+ cmds_='date; date +%N; free; who -a; w; ps auxww; ps -ef'
+ data_=` (eval "$cmds_") 2>&1 | gzip `
+
+ # Ensure that $data_ has length at least 50+$n_
+ while :; do
+ len_=`echo "$data_"|wc -c`
+ test $n_plus_50_ -le $len_ && break;
+ data_=` (echo "$data_"; eval "$cmds_") 2>&1 | gzip `
+ done
+
+ echo "$data_" \
+ | dd bs=1 skip=50 count=$n_ 2>/dev/null \
+ | LC_ALL=C tr -c $chars_ 01234567$chars_$chars_$chars_
+}
+
+mktempd_ ()
+{
+ case $# in
+ 2);;
+ *) fail_ "Usage: mktempd_ DIR TEMPLATE";;
+ esac
+
+ destdir_=$1
+ template_=$2
+
+ MAX_TRIES_=4
+
+ # Disallow any trailing slash on specified destdir:
+ # it would subvert the post-mktemp "case"-based destdir test.
+ case $destdir_ in
+ / | //) destdir_slash_=$destdir;;
+ */) fail_ "invalid destination dir: remove trailing slash(es)";;
+ *) destdir_slash_=$destdir_/;;
+ esac
+
+ case $template_ in
+ *XXXX) ;;
+ *) fail_ \
+ "invalid template: $template_ (must have a suffix of at least 4 X's)";;
+ esac
+
+ # First, try to use mktemp.
+ d=`unset TMPDIR; { mktemp -d -t -p "$destdir_" "$template_"; } 2>/dev/null` &&
+
+ # The resulting name must be in the specified directory.
+ case $d in "$destdir_slash_"*) :;; *) false;; esac &&
+
+ # It must have created the directory.
+ test -d "$d" &&
+
+ # It must have 0700 permissions. Handle sticky "S" bits.
+ perms=`ls -dgo "$d" 2>/dev/null` &&
+ case $perms in drwx--[-S]---*) :;; *) false;; esac && {
+ echo "$d"
+ return
+ }
+
+ # If we reach this point, we'll have to create a directory manually.
+
+ # Get a copy of the template without its suffix of X's.
+ base_template_=`echo "$template_"|sed 's/XX*$//'`
+
+ # Calculate how many X's we've just removed.
+ template_length_=`echo "$template_" | wc -c`
+ nx_=`echo "$base_template_" | wc -c`
+ nx_=`expr $template_length_ - $nx_`
+
+ err_=
+ i_=1
+ while :; do
+ X_=`rand_bytes_ $nx_`
+ candidate_dir_="$destdir_slash_$base_template_$X_"
+ err_=`mkdir -m 0700 "$candidate_dir_" 2>&1` \
+ && { echo "$candidate_dir_"; return; }
+ test $MAX_TRIES_ -le $i_ && break;
+ i_=`expr $i_ + 1`
+ done
+ fail_ "$err_"
+}
+
+# If you want to override the testdir_prefix_ function,
+# or to add more utility functions, use this file.
+test -f "$srcdir/init.cfg" \
+ && . "$srcdir/init.cfg"
+
+setup_ "$@"
+# This trap is here, rather than in the setup_ function, because some
+# shells run the exit trap at shell function exit, rather than script exit.
+trap remove_tmp_ 0
diff --git a/tests/lang-default b/tests/lang-default
new file mode 100644
index 00000000..882672fc
--- /dev/null
+++ b/tests/lang-default
@@ -0,0 +1,10 @@
+#!/bin/sh
+# Set locale-related environment variables so we get consistent
+# message translations, time formats, sort orderings, etc.
+
+LC_ALL=C
+export LC_ALL
+unset LANGUAGE NLSPATH
+
+# These settings shouldn't matter, but unset them anyway just in case.
+unset LANG LC_COLLATE LC_CTYPE LC_MESSAGES LC_MONETARY LC_NUMERIC LC_TIME
diff --git a/tests/local.mk b/tests/local.mk
new file mode 100644
index 00000000..894f612d
--- /dev/null
+++ b/tests/local.mk
@@ -0,0 +1,109 @@
+## Process this file with automake to produce Makefile.in -*-Makefile-*-.
+
+## Copyright (C) 2007-2019 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 .
+
+built_programs = find oldfind xargs frcode locate updatedb
+
+# Indirections required so that we'll still be able to know the
+# complete list of our tests even if the user overrides TESTS
+# from the command line (as permitted by the test harness API).
+TESTS = $(all_tests)
+root_tests = $(all_root_tests)
+
+EXTRA_DIST += $(all_tests)
+
+TEST_EXTENSIONS = .sh
+
+SH_LOG_COMPILER = $(SHELL)
+
+# We don't want this to go in the top-level directory.
+TEST_SUITE_LOG = tests/test-suite.log
+
+# 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 = \
+ . $(srcdir)/tests/lang-default; \
+ tmp__=$${TMPDIR-/tmp}; \
+ test -d "$$tmp__" && test -w "$$tmp__" || tmp__=.; \
+ . $(srcdir)/tests/envvar-check; \
+ TMPDIR=$$tmp__; export TMPDIR; \
+ export \
+ VERSION='$(VERSION)' \
+ 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) $(single_binary_progs)' \
+ fail=0 \
+ host_os=$(host_os) \
+ host_triplet='$(host_triplet)' \
+ srcdir='$(srcdir)' \
+ top_srcdir='$(top_srcdir)' \
+ CONFIG_HEADER='$(abs_top_builddir)/$(CONFIG_INCLUDE)' \
+ CC='$(CC)' \
+ AWK='$(AWK)' \
+ EGREP='$(EGREP)' \
+ EXEEXT='$(EXEEXT)' \
+ MAKE=$(MAKE) \
+ PACKAGE_VERSION=$(PACKAGE_VERSION) \
+ PERL='$(PERL)' \
+ SHELL='$(PREFERABLY_POSIX_SHELL)' \
+ ; test -d /usr/xpg4/bin && PATH='/usr/xpg4/bin$(PATH_SEPARATOR)'"$$PATH"; \
+ PATH='$(abs_top_builddir)/find$(PATH_SEPARATOR)$(abs_top_builddir)/locate$(PATH_SEPARATOR)$(abs_top_builddir)/xargs$(PATH_SEPARATOR)'"$$PATH" \
+ ; 9>&2
+
+# On failure, display the global testsuite log on stdout.
+VERBOSE = yes
+
+EXTRA_DIST += \
+ init.cfg \
+ tests/envvar-check \
+ tests/init.sh \
+ tests/lang-default \
+ tests/other-fs-tmpdir \
+ tests/sample-test
+
+all_root_tests =
+
+ALL_RECURSIVE_TARGETS += check-root
+.PHONY: check-root
+check-root:
+ $(MAKE) check TESTS='$(root_tests)' SUBDIRS=.
+
+# Do not choose a name that is a shell keyword like 'if', or a
+# commonly-used utility like 'cat' or 'test', as the name of a test.
+# Otherwise, VPATH builds will fail on hosts like Solaris, since they
+# will expand 'if test ...' to 'if .../test ...', and the '.../test'
+# will execute the test script rather than the standard utility.
+
+# Notes on the ordering of these tests:
+# Place early in the list tests of the tools that
+# are most commonly used in test scripts themselves.
+# E.g., nearly every test script uses rm and chmod.
+# help-version comes early because it's a basic sanity test.
+# Put seq early, since lots of other tests use it.
+# Put tests that sleep early, but not all together, so in parallel builds
+# they share time with tests that burn CPU, not with others that sleep.
+# Put head-elide-tail early, because it's long-running.
+
+all_tests = \
+ tests/misc/help-version.sh \
+ $(all_root_tests)
+
+$(TEST_LOGS): $(PROGRAMS)
diff --git a/tests/misc/help-version.sh b/tests/misc/help-version.sh
new file mode 100755
index 00000000..ea622421
--- /dev/null
+++ b/tests/misc/help-version.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+# Make sure all of these programs work properly
+# when invoked with --help or --version.
+
+# Copyright (C) 2019 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 .
+
+. "${srcdir=.}/tests/init.sh"
+
+# Terminate any background processes
+cleanup_() { kill $pid 2>/dev/null && wait $pid; }
+echo PATH=$PATH
+test "$built_programs" \
+ || fail_ "built_programs not specified!?!"
+
+test "$VERSION" \
+ || fail_ "set envvar VERSION; it is required for a PATH sanity-check"
+
+# Extract version from --version output of the first program
+for i in $built_programs; do
+ v=$(set -x; env $i --version | sed -n '1s/.* //p;q')
+ break
+done
+
+# Ensure that it matches $VERSION.
+test "x$v" = "x$VERSION" \
+ || fail_ "--version-\$VERSION mismatch"
+
+for i in $built_programs; do
+ # Make sure they exit successfully, under normal conditions.
+ env $i --help >/dev/null || fail=1
+ env $i --version >/dev/null || fail=1
+
+ # Make sure they fail upon 'disk full' error.
+ if test -w /dev/full && test -c /dev/full; then
+ prog=$(set -x; echo $i|sed "s/$EXEEXT$//");
+ eval "expected=\$expected_failure_status_$prog"
+ test x$expected = x && expected=1
+
+ returns_ $expected env $i --help >/dev/full 2>/dev/null &&
+ returns_ $expected env $i --version >/dev/full 2>/dev/null ||
+ {
+ fail=1
+ env $i --help >/dev/full 2>/dev/null
+ status=$?
+ echo "*** $i: bad exit status '$status' (expected $expected)," 1>&2
+ echo " with --help or --version output redirected to /dev/full" 1>&2
+ }
+ fi
+done
+
+Exit $fail
diff --git a/tests/other-fs-tmpdir b/tests/other-fs-tmpdir
new file mode 100644
index 00000000..8efae3d1
--- /dev/null
+++ b/tests/other-fs-tmpdir
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+# Use stat to find a writable directory on a file system different from that
+# of the current directory. If one is found, create a temporary directory
+# inside it.
+
+# Copyright (C) 1998-2019 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 .
+
+test "${CANDIDATE_TMP_DIRS+set}" = set \
+ || CANDIDATE_TMP_DIRS="$TMPDIR /tmp /dev/shm /var/tmp /usr/tmp $HOME"
+
+other_partition_tmpdir=
+
+dot_mount_point=$(stat -c %d .)
+for d in $CANDIDATE_TMP_DIRS; do
+
+ # Skip nonexistent directories.
+ test -d "$d" || continue
+
+ d_mount_point=$(stat -L -c %d "$d")
+
+ # Same partition? Skip it.
+ test "x$d_mount_point" = "x$dot_mount_point" && continue
+
+ # See if we can create a directory in it.
+ if mkdir "$d/tmp$$" > /dev/null 2>&1; then
+ other_partition_tmpdir="$d/tmp$$"
+ break
+ fi
+
+done
+
+if test -z "$other_partition_tmpdir"; then
+ skip_ \
+"requires a writable directory on a different disk partition,
+and I couldn't find one. I tried these:
+ $CANDIDATE_TMP_DIRS
+Set your environment variable CANDIDATE_TMP_DIRS to make
+this test use a different list."
+fi
+
+test "$VERBOSE" = yes && set -x
diff --git a/tests/sample-test b/tests/sample-test
new file mode 100644
index 00000000..a8ff24d6
--- /dev/null
+++ b/tests/sample-test
@@ -0,0 +1,36 @@
+#!/bin/sh
+# FIXME
+
+# Copyright (C) 2019 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 .
+
+. "${srcdir=.}/tests/init.sh"
+print_ver_ FIXME
+
+# FIXME: skip_if_root_
+# FIXME: require_root_
+
+# If used, these must *follow* init.sh.
+# FIXME: cleanup_() { rm -rf "$other_partition_tmpdir"; }
+# FIXME: . "$abs_srcdir/tests/other-fs-tmpdir"
+
+FIXME > out || fail=1
+cat <<\EOF > exp || framework_failure_
+FIXME
+EOF
+
+compare exp out || fail=1
+
+Exit $fail
--
2.20.1