refspec: factor out parsing a single refspec
[gitweb.git] / t / test-lib.sh
index afd3053f96b789a73274217b384d245583500c04..ea2bbaaa7ab4dc77d0ef13cafbe45d1b12bd67e8 100644 (file)
@@ -1,7 +1,65 @@
-#!/bin/sh
+# Test framework for git.  See t/README for usage.
 #
 # Copyright (c) 2005 Junio C Hamano
 #
+# 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.
+#
+# 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/ .
+
+# Test the binaries we have just built.  The tests are kept in
+# t/ subdirectory and are run in 'trash directory' subdirectory.
+if test -z "$TEST_DIRECTORY"
+then
+       # We allow tests to override this, in case they want to run tests
+       # outside of t/, e.g. for running tests on the test library
+       # itself.
+       TEST_DIRECTORY=$(pwd)
+else
+       # ensure that TEST_DIRECTORY is an absolute path so that it
+       # is valid even if the current working directory is changed
+       TEST_DIRECTORY=$(cd "$TEST_DIRECTORY" && pwd) || exit 1
+fi
+if test -z "$TEST_OUTPUT_DIRECTORY"
+then
+       # Similarly, override this to store the test-results subdir
+       # elsewhere
+       TEST_OUTPUT_DIRECTORY=$TEST_DIRECTORY
+fi
+GIT_BUILD_DIR="$TEST_DIRECTORY"/..
+
+# If we were built with ASAN, it may complain about leaks
+# of program-lifetime variables. Disable it by default to lower
+# the noise level. This needs to happen at the start of the script,
+# before we even do our "did we build git yet" check (since we don't
+# want that one to complain to stderr).
+: ${ASAN_OPTIONS=detect_leaks=0:abort_on_error=1}
+export ASAN_OPTIONS
+
+# If LSAN is in effect we _do_ want leak checking, but we still
+# want to abort so that we notice the problems.
+: ${LSAN_OPTIONS=abort_on_error=1}
+export LSAN_OPTIONS
+
+################################################################
+# It appears that people try to run tests without building...
+"$GIT_BUILD_DIR/git" >/dev/null
+if test $? != 1
+then
+       echo >&2 'error: you do not seem to have built git yet.'
+       exit 1
+fi
+
+. "$GIT_BUILD_DIR"/GIT-BUILD-OPTIONS
+export PERL_PATH SHELL_PATH
 
 # if --tee was passed, write the output not only to the terminal, but
 # additionally to the file test-results/$BASENAME.out, too.
@@ -9,76 +67,135 @@ case "$GIT_TEST_TEE_STARTED, $* " in
 done,*)
        # do not redirect again
        ;;
-*' --tee '*|*' --va'*)
-       mkdir -p test-results
-       BASE=test-results/$(basename "$0" .sh)
-       (GIT_TEST_TEE_STARTED=done ${SHELL-sh} "$0" "$@" 2>&1;
-        echo $? > $BASE.exit) | tee $BASE.out
-       test "$(cat $BASE.exit)" = 0
+*' --tee '*|*' --va'*|*' --verbose-log '*)
+       mkdir -p "$TEST_OUTPUT_DIRECTORY/test-results"
+       BASE="$TEST_OUTPUT_DIRECTORY/test-results/$(basename "$0" .sh)"
+
+       # Make this filename available to the sub-process in case it is using
+       # --verbose-log.
+       GIT_TEST_TEE_OUTPUT_FILE=$BASE.out
+       export GIT_TEST_TEE_OUTPUT_FILE
+
+       # Truncate before calling "tee -a" to get rid of the results
+       # from any previous runs.
+       >"$GIT_TEST_TEE_OUTPUT_FILE"
+
+       (GIT_TEST_TEE_STARTED=done ${TEST_SHELL_PATH} "$0" "$@" 2>&1;
+        echo $? >"$BASE.exit") | tee -a "$GIT_TEST_TEE_OUTPUT_FILE"
+       test "$(cat "$BASE.exit")" = 0
        exit
        ;;
 esac
 
-# Keep the original TERM for say_color
-ORIGINAL_TERM=$TERM
-
 # For repeatability, reset the environment to known value.
+# TERM is sanitized below, after saving color control sequences.
 LANG=C
 LC_ALL=C
 PAGER=cat
 TZ=UTC
-TERM=dumb
-export LANG LC_ALL PAGER TERM TZ
+export LANG LC_ALL PAGER TZ
 EDITOR=:
-unset VISUAL
-unset GIT_EDITOR
-unset AUTHOR_DATE
-unset AUTHOR_EMAIL
-unset AUTHOR_NAME
-unset COMMIT_AUTHOR_EMAIL
-unset COMMIT_AUTHOR_NAME
-unset EMAIL
-unset GIT_ALTERNATE_OBJECT_DIRECTORIES
-unset GIT_AUTHOR_DATE
+# A call to "unset" with no arguments causes at least Solaris 10
+# /usr/xpg4/bin/sh and /bin/ksh to bail out.  So keep the unsets
+# deriving from the command substitution clustered with the other
+# ones.
+unset VISUAL EMAIL LANGUAGE COLUMNS $("$PERL_PATH" -e '
+       my @env = keys %ENV;
+       my $ok = join("|", qw(
+               TRACE
+               DEBUG
+               TEST
+               .*_TEST
+               PROVE
+               VALGRIND
+               UNZIP
+               PERF_
+               CURL_VERBOSE
+               TRACE_CURL
+       ));
+       my @vars = grep(/^GIT_/ && !/^GIT_($ok)/o, @env);
+       print join("\n", @vars);
+')
+unset XDG_CACHE_HOME
+unset XDG_CONFIG_HOME
+unset GITPERLLIB
 GIT_AUTHOR_EMAIL=author@example.com
 GIT_AUTHOR_NAME='A U Thor'
-unset GIT_COMMITTER_DATE
 GIT_COMMITTER_EMAIL=committer@example.com
 GIT_COMMITTER_NAME='C O Mitter'
-unset GIT_DIFF_OPTS
-unset GIT_DIR
-unset GIT_WORK_TREE
-unset GIT_EXTERNAL_DIFF
-unset GIT_INDEX_FILE
-unset GIT_OBJECT_DIRECTORY
-unset GIT_CEILING_DIRECTORIES
-unset SHA1_FILE_DIRECTORIES
-unset SHA1_FILE_DIRECTORY
 GIT_MERGE_VERBOSITY=5
-export GIT_MERGE_VERBOSITY
+GIT_MERGE_AUTOEDIT=no
+export GIT_MERGE_VERBOSITY GIT_MERGE_AUTOEDIT
 export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
 export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
 export EDITOR
-GIT_TEST_CMP=${GIT_TEST_CMP:-diff -u}
+
+# Tests using GIT_TRACE typically don't want <timestamp> <file>:<line> output
+GIT_TRACE_BARE=1
+export GIT_TRACE_BARE
+
+if test -n "${TEST_GIT_INDEX_VERSION:+isset}"
+then
+       GIT_INDEX_VERSION="$TEST_GIT_INDEX_VERSION"
+       export GIT_INDEX_VERSION
+fi
+
+# Add libc MALLOC and MALLOC_PERTURB test
+# only if we are not executing the test with valgrind
+if expr " $GIT_TEST_OPTS " : ".* --valgrind " >/dev/null ||
+   test -n "$TEST_NO_MALLOC_CHECK"
+then
+       setup_malloc_check () {
+               : nothing
+       }
+       teardown_malloc_check () {
+               : nothing
+       }
+else
+       setup_malloc_check () {
+               MALLOC_CHECK_=3 MALLOC_PERTURB_=165
+               export MALLOC_CHECK_ MALLOC_PERTURB_
+       }
+       teardown_malloc_check () {
+               unset MALLOC_CHECK_ MALLOC_PERTURB_
+       }
+fi
 
 # Protect ourselves from common misconfiguration to export
 # CDPATH into the environment
 unset CDPATH
 
+unset GREP_OPTIONS
+unset UNZIP
+
 case $(echo $GIT_TRACE |tr "[A-Z]" "[a-z]") in
-       1|2|true)
-               echo "* warning: Some tests will not work if GIT_TRACE" \
-                       "is set as to trace on STDERR ! *"
-               echo "* warning: Please set GIT_TRACE to something" \
-                       "other than 1, 2 or true ! *"
-               ;;
+1|2|true)
+       GIT_TRACE=4
+       ;;
 esac
 
 # Convenience
 #
-# A regexp to match 5 and 40 hexdigits
+# A regexp to match 5, 35 and 40 hexdigits
 _x05='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
-_x40="$_x05$_x05$_x05$_x05$_x05$_x05$_x05$_x05"
+_x35="$_x05$_x05$_x05$_x05$_x05$_x05$_x05"
+_x40="$_x35$_x05"
+
+# Zero SHA-1
+_z40=0000000000000000000000000000000000000000
+
+EMPTY_TREE=4b825dc642cb6eb9a060e54bf8d69288fbee4904
+EMPTY_BLOB=e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
+
+# Line feed
+LF='
+'
+
+# UTF-8 ZERO WIDTH NON-JOINER, which HFS+ ignores
+# when case-folding filenames
+u200c=$(printf '\342\200\214')
+
+export _x05 _x35 _x40 _z40 LF u200c EMPTY_TREE EMPTY_BLOB
 
 # Each test should start with something like this, after copyright notices:
 #
@@ -86,10 +203,8 @@ _x40="$_x05$_x05$_x05$_x05$_x05$_x05$_x05$_x05"
 # This test checks if command xyzzy does the right thing...
 # '
 # . ./test-lib.sh
-[ "x$ORIGINAL_TERM" != "xdumb" ] && (
-               TERM=$ORIGINAL_TERM &&
-               export TERM &&
-               [ -t 1 ] &&
+test "x$TERM" != "xdumb" && (
+               test -t 1 &&
                tput bold >/dev/null 2>&1 &&
                tput setaf 1 >/dev/null 2>&1 &&
                tput sgr0 >/dev/null 2>&1
@@ -105,57 +220,129 @@ do
                immediate=t; shift ;;
        -l|--l|--lo|--lon|--long|--long-|--long-t|--long-te|--long-tes|--long-test|--long-tests)
                GIT_TEST_LONG=t; export GIT_TEST_LONG; shift ;;
+       -r)
+               shift; test "$#" -ne 0 || {
+                       echo 'error: -r requires an argument' >&2;
+                       exit 1;
+               }
+               run_list=$1; shift ;;
+       --run=*)
+               run_list=${1#--*=}; shift ;;
        -h|--h|--he|--hel|--help)
                help=t; shift ;;
        -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
                verbose=t; shift ;;
+       --verbose-only=*)
+               verbose_only=${1#--*=}
+               shift ;;
        -q|--q|--qu|--qui|--quie|--quiet)
-               quiet=t; shift ;;
+               # Ignore --quiet under a TAP::Harness. Saying how many tests
+               # passed without the ok/not ok details is always an error.
+               test -z "$HARNESS_ACTIVE" && quiet=t; shift ;;
        --with-dashes)
                with_dashes=t; shift ;;
        --no-color)
                color=; shift ;;
-       --no-python)
-               # noop now...
-               shift ;;
        --va|--val|--valg|--valgr|--valgri|--valgrin|--valgrind)
-               valgrind=t; verbose=t; shift ;;
+               valgrind=memcheck
+               shift ;;
+       --valgrind=*)
+               valgrind=${1#--*=}
+               shift ;;
+       --valgrind-only=*)
+               valgrind_only=${1#--*=}
+               shift ;;
        --tee)
                shift ;; # was handled already
        --root=*)
-               root=$(expr "z$1" : 'z[^=]*=\(.*\)')
+               root=${1#--*=}
+               shift ;;
+       --chain-lint)
+               GIT_TEST_CHAIN_LINT=1
+               shift ;;
+       --no-chain-lint)
+               GIT_TEST_CHAIN_LINT=0
+               shift ;;
+       -x)
+               # Some test scripts can't be reliably traced  with '-x',
+               # unless the test is run with a Bash version supporting
+               # BASH_XTRACEFD (introduced in Bash v4.1).  Check whether
+               # this test is marked as such, and ignore '-x' if it
+               # isn't executed with a suitable Bash version.
+               if test -z "$test_untraceable" || {
+                    test -n "$BASH_VERSION" && {
+                      test ${BASH_VERSINFO[0]} -gt 4 || {
+                        test ${BASH_VERSINFO[0]} -eq 4 &&
+                        test ${BASH_VERSINFO[1]} -ge 1
+                      }
+                    }
+                  }
+               then
+                       trace=t
+               else
+                       echo >&2 "warning: ignoring -x; '$0' is untraceable without BASH_XTRACEFD"
+               fi
+               shift ;;
+       --verbose-log)
+               verbose_log=t
                shift ;;
        *)
                echo "error: unknown test option '$1'" >&2; exit 1 ;;
        esac
 done
 
-if test -n "$color"; then
+if test -n "$valgrind_only"
+then
+       test -z "$valgrind" && valgrind=memcheck
+       test -z "$verbose" && verbose_only="$valgrind_only"
+elif test -n "$valgrind"
+then
+       test -z "$verbose_log" && verbose=t
+fi
+
+if test -n "$trace" && test -z "$verbose_log"
+then
+       verbose=t
+fi
+
+if test -n "$color"
+then
+       # Save the color control sequences now rather than run tput
+       # each time say_color() is called.  This is done for two
+       # reasons:
+       #   * TERM will be changed to dumb
+       #   * HOME will be changed to a temporary directory and tput
+       #     might need to read ~/.terminfo from the original HOME
+       #     directory to get the control sequences
+       # Note:  This approach assumes the control sequences don't end
+       # in a newline for any terminal of interest (command
+       # substitutions strip trailing newlines).  Given that most
+       # (all?) terminals in common use are related to ECMA-48, this
+       # shouldn't be a problem.
+       say_color_error=$(tput bold; tput setaf 1) # bold red
+       say_color_skip=$(tput setaf 4) # blue
+       say_color_warn=$(tput setaf 3) # brown/yellow
+       say_color_pass=$(tput setaf 2) # green
+       say_color_info=$(tput setaf 6) # cyan
+       say_color_reset=$(tput sgr0)
+       say_color_="" # no formatting for normal text
        say_color () {
-               (
-               TERM=$ORIGINAL_TERM
-               export TERM
-               case "$1" in
-                       error) tput bold; tput setaf 1;; # bold red
-                       skip)  tput bold; tput setaf 2;; # bold green
-                       pass)  tput setaf 2;;            # green
-                       info)  tput setaf 3;;            # brown
-                       *) test -n "$quiet" && return;;
-               esac
+               test -z "$1" && test -n "$quiet" && return
+               eval "say_color_color=\$say_color_$1"
                shift
-               printf "* %s" "$*"
-               tput sgr0
-               echo
-               )
+               printf "%s\\n" "$say_color_color$*$say_color_reset"
        }
 else
        say_color() {
                test -z "$1" && test -n "$quiet" && return
                shift
-               echo "* $*"
+               printf "%s\n" "$*"
        }
 fi
 
+TERM=dumb
+export TERM
+
 error () {
        say_color error "error: $*"
        GIT_EXIT_OK=t
@@ -166,29 +353,59 @@ say () {
        say_color info "$*"
 }
 
+if test -n "$HARNESS_ACTIVE"
+then
+       if test "$verbose" = t || test -n "$verbose_only"
+       then
+               printf 'Bail out! %s\n' \
+                'verbose mode forbidden under TAP harness; try --verbose-log'
+               exit 1
+       fi
+fi
+
 test "${test_description}" != "" ||
 error "Test script did not set test_description."
 
 if test "$help" = "t"
 then
-       echo "$test_description"
+       printf '%s\n' "$test_description"
        exit 0
 fi
 
 exec 5>&1
-if test "$verbose" = "t"
+exec 6<&0
+exec 7>&2
+if test "$verbose_log" = "t"
+then
+       exec 3>>"$GIT_TEST_TEE_OUTPUT_FILE" 4>&3
+elif test "$verbose" = "t"
 then
        exec 4>&2 3>&1
 else
        exec 4>/dev/null 3>/dev/null
 fi
 
+# Send any "-x" output directly to stderr to avoid polluting tests
+# which capture stderr. We can do this unconditionally since it
+# has no effect if tracing isn't turned on.
+#
+# Note that this sets up the trace fd as soon as we assign the variable, so it
+# must come after the creation of descriptor 4 above. Likewise, we must never
+# unset this, as it has the side effect of closing descriptor 4, which we
+# use to show verbose tests to the user.
+#
+# Note also that we don't need or want to export it. The tracing is local to
+# this shell, and we would not want to influence any shells we exec.
+BASH_XTRACEFD=4
+
 test_failure=0
 test_count=0
 test_fixed=0
 test_broken=0
 test_success=0
 
+test_external_has_tap=0
+
 die () {
        code=$?
        if test -n "$GIT_EXIT_OK"
@@ -202,390 +419,426 @@ die () {
 
 GIT_EXIT_OK=
 trap 'die' EXIT
+trap 'exit $?' INT
 
-# The semantics of the editor variables are that of invoking
-# sh -c "$EDITOR \"$@\"" files ...
-#
-# If our trash directory contains shell metacharacters, they will be
-# interpreted if we just set $EDITOR directly, so do a little dance with
-# environment variables to work around this.
-#
-# In particular, quoting isn't enough, as the path may contain the same quote
-# that we're using.
-test_set_editor () {
-       FAKE_EDITOR="$1"
-       export FAKE_EDITOR
-       EDITOR='"$FAKE_EDITOR"'
-       export EDITOR
-}
-
-test_decode_color () {
-       sed     -e 's/.\[1m/<WHITE>/g' \
-               -e 's/.\[31m/<RED>/g' \
-               -e 's/.\[32m/<GREEN>/g' \
-               -e 's/.\[33m/<YELLOW>/g' \
-               -e 's/.\[34m/<BLUE>/g' \
-               -e 's/.\[35m/<MAGENTA>/g' \
-               -e 's/.\[36m/<CYAN>/g' \
-               -e 's/.\[m/<RESET>/g'
-}
-
-q_to_nul () {
-       perl -pe 'y/Q/\000/'
-}
-
-q_to_cr () {
-       tr Q '\015'
-}
-
-append_cr () {
-       sed -e 's/$/Q/' | tr Q '\015'
-}
-
-remove_cr () {
-       tr '\015' Q | sed -e 's/Q$//'
-}
-
-test_tick () {
-       if test -z "${test_tick+set}"
-       then
-               test_tick=1112911993
-       else
-               test_tick=$(($test_tick + 60))
-       fi
-       GIT_COMMITTER_DATE="$test_tick -0700"
-       GIT_AUTHOR_DATE="$test_tick -0700"
-       export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
-}
-
-# Call test_commit with the arguments "<message> [<file> [<contents>]]"
-#
-# This will commit a file with the given contents and the given commit
-# message.  It will also add a tag with <message> as name.
-#
-# Both <file> and <contents> default to <message>.
-
-test_commit () {
-       file=${2:-"$1.t"}
-       echo "${3-$1}" > "$file" &&
-       git add "$file" &&
-       test_tick &&
-       git commit -m "$1" &&
-       git tag "$1"
-}
-
-# Call test_merge with the arguments "<message> <commit>", where <commit>
-# can be a tag pointing to the commit-to-merge.
-
-test_merge () {
-       test_tick &&
-       git merge -m "$1" "$2" &&
-       git tag "$1"
-}
-
-# This function helps systems where core.filemode=false is set.
-# Use it instead of plain 'chmod +x' to set or unset the executable bit
-# of a file in the working directory and add it to the index.
-
-test_chmod () {
-       chmod "$@" &&
-       git update-index --add "--chmod=$@"
-}
-
-# Use test_set_prereq to tell that a particular prerequisite is available.
-# The prerequisite can later be checked for in two ways:
-#
-# - Explicitly using test_have_prereq.
-#
-# - Implicitly by specifying the prerequisite tag in the calls to
-#   test_expect_{success,failure,code}.
-#
-# The single parameter is the prerequisite tag (a simple word, in all
-# capital letters by convention).
-
-test_set_prereq () {
-       satisfied="$satisfied$1 "
-}
-satisfied=" "
-
-test_have_prereq () {
-       case $satisfied in
-       *" $1 "*)
-               : yes, have it ;;
-       *)
-               ! : nope ;;
-       esac
-}
+# The user-facing functions are loaded from a separate file so that
+# test_perf subshells can have them too
+. "$TEST_DIRECTORY/test-lib-functions.sh"
 
 # You are not expected to call test_ok_ and test_failure_ directly, use
-# the text_expect_* functions instead.
+# the test_expect_* functions instead.
 
 test_ok_ () {
        test_success=$(($test_success + 1))
-       say_color "" "  ok $test_count: $@"
+       say_color "" "ok $test_count - $@"
 }
 
 test_failure_ () {
        test_failure=$(($test_failure + 1))
-       say_color error "FAIL $test_count: $1"
+       say_color error "not ok $test_count - $1"
        shift
-       echo "$@" | sed -e 's/^/        /'
+       printf '%s\n' "$*" | sed -e 's/^/#      /'
        test "$immediate" = "" || { GIT_EXIT_OK=t; exit 1; }
 }
 
 test_known_broken_ok_ () {
        test_fixed=$(($test_fixed+1))
-       say_color "" "  FIXED $test_count: $@"
+       say_color error "ok $test_count - $@ # TODO known breakage vanished"
 }
 
 test_known_broken_failure_ () {
        test_broken=$(($test_broken+1))
-       say_color skip "  still broken $test_count: $@"
+       say_color warn "not ok $test_count - $@ # TODO known breakage"
 }
 
 test_debug () {
        test "$debug" = "" || eval "$1"
 }
 
-test_run_ () {
-       eval >&3 2>&4 "$1"
-       eval_ret="$?"
-       return 0
+match_pattern_list () {
+       arg="$1"
+       shift
+       test -z "$*" && return 1
+       for pattern_
+       do
+               case "$arg" in
+               $pattern_)
+                       return 0
+               esac
+       done
+       return 1
 }
 
-test_skip () {
-       test_count=$(($test_count+1))
-       to_skip=
-       for skp in $GIT_SKIP_TESTS
+match_test_selector_list () {
+       title="$1"
+       shift
+       arg="$1"
+       shift
+       test -z "$1" && return 0
+
+       # Both commas and whitespace are accepted as separators.
+       OLDIFS=$IFS
+       IFS='   ,'
+       set -- $1
+       IFS=$OLDIFS
+
+       # If the first selector is negative we include by default.
+       include=
+       case "$1" in
+               !*) include=t ;;
+       esac
+
+       for selector
        do
-               case $this_test.$test_count in
-               $skp)
-                       to_skip=t
+               orig_selector=$selector
+
+               positive=t
+               case "$selector" in
+                       !*)
+                               positive=
+                               selector=${selector##?}
+                               ;;
+               esac
+
+               test -z "$selector" && continue
+
+               case "$selector" in
+                       *-*)
+                               if expr "z${selector%%-*}" : "z[0-9]*[^0-9]" >/dev/null
+                               then
+                                       echo "error: $title: invalid non-numeric in range" \
+                                               "start: '$orig_selector'" >&2
+                                       exit 1
+                               fi
+                               if expr "z${selector#*-}" : "z[0-9]*[^0-9]" >/dev/null
+                               then
+                                       echo "error: $title: invalid non-numeric in range" \
+                                               "end: '$orig_selector'" >&2
+                                       exit 1
+                               fi
+                               ;;
+                       *)
+                               if expr "z$selector" : "z[0-9]*[^0-9]" >/dev/null
+                               then
+                                       echo "error: $title: invalid non-numeric in test" \
+                                               "selector: '$orig_selector'" >&2
+                                       exit 1
+                               fi
+               esac
+
+               # Short cut for "obvious" cases
+               test -z "$include" && test -z "$positive" && continue
+               test -n "$include" && test -n "$positive" && continue
+
+               case "$selector" in
+                       -*)
+                               if test $arg -le ${selector#-}
+                               then
+                                       include=$positive
+                               fi
+                               ;;
+                       *-)
+                               if test $arg -ge ${selector%-}
+                               then
+                                       include=$positive
+                               fi
+                               ;;
+                       *-*)
+                               if test ${selector%%-*} -le $arg \
+                                       && test $arg -le ${selector#*-}
+                               then
+                                       include=$positive
+                               fi
+                               ;;
+                       *)
+                               if test $arg -eq $selector
+                               then
+                                       include=$positive
+                               fi
+                               ;;
                esac
        done
-       if test -z "$to_skip" && test -n "$prereq" &&
-          ! test_have_prereq "$prereq"
-       then
-               to_skip=t
-       fi
-       case "$to_skip" in
-       t)
-               say_color skip >&3 "skipping test: $@"
-               say_color skip "skip $test_count: $1"
-               : true
-               ;;
-       *)
-               false
-               ;;
-       esac
+
+       test -n "$include"
 }
 
-test_expect_failure () {
-       test "$#" = 3 && { prereq=$1; shift; } || prereq=
-       test "$#" = 2 ||
-       error "bug in the test script: not 2 or 3 parameters to test-expect-failure"
-       if ! test_skip "$@"
+maybe_teardown_verbose () {
+       test -z "$verbose_only" && return
+       exec 4>/dev/null 3>/dev/null
+       verbose=
+}
+
+last_verbose=t
+maybe_setup_verbose () {
+       test -z "$verbose_only" && return
+       if match_pattern_list $test_count $verbose_only
        then
-               say >&3 "checking known breakage: $2"
-               test_run_ "$2"
-               if [ "$?" = 0 -a "$eval_ret" = 0 ]
-               then
-                       test_known_broken_ok_ "$1"
-               else
-                       test_known_broken_failure_ "$1"
-               fi
+               exec 4>&2 3>&1
+               # Emit a delimiting blank line when going from
+               # non-verbose to verbose.  Within verbose mode the
+               # delimiter is printed by test_expect_*.  The choice
+               # of the initial $last_verbose is such that before
+               # test 1, we do not print it.
+               test -z "$last_verbose" && echo >&3 ""
+               verbose=t
+       else
+               exec 4>/dev/null 3>/dev/null
+               verbose=
        fi
-       echo >&3 ""
+       last_verbose=$verbose
+}
+
+maybe_teardown_valgrind () {
+       test -z "$GIT_VALGRIND" && return
+       GIT_VALGRIND_ENABLED=
 }
 
-test_expect_success () {
-       test "$#" = 3 && { prereq=$1; shift; } || prereq=
-       test "$#" = 2 ||
-       error "bug in the test script: not 2 or 3 parameters to test-expect-success"
-       if ! test_skip "$@"
+maybe_setup_valgrind () {
+       test -z "$GIT_VALGRIND" && return
+       if test -z "$valgrind_only"
        then
-               say >&3 "expecting success: $2"
-               test_run_ "$2"
-               if [ "$?" = 0 -a "$eval_ret" = 0 ]
-               then
-                       test_ok_ "$1"
-               else
-                       test_failure_ "$@"
-               fi
+               GIT_VALGRIND_ENABLED=t
+               return
+       fi
+       GIT_VALGRIND_ENABLED=
+       if match_pattern_list $test_count $valgrind_only
+       then
+               GIT_VALGRIND_ENABLED=t
        fi
-       echo >&3 ""
 }
 
-test_expect_code () {
-       test "$#" = 4 && { prereq=$1; shift; } || prereq=
-       test "$#" = 3 ||
-       error "bug in the test script: not 3 or 4 parameters to test-expect-code"
-       if ! test_skip "$@"
-       then
-               say >&3 "expecting exit code $1: $3"
-               test_run_ "$3"
-               if [ "$?" = 0 -a "$eval_ret" = "$1" ]
+want_trace () {
+       test "$trace" = t && {
+               test "$verbose" = t || test "$verbose_log" = t
+       }
+}
+
+# This is a separate function because some tests use
+# "return" to end a test_expect_success block early
+# (and we want to make sure we run any cleanup like
+# "set +x").
+test_eval_inner_ () {
+       # Do not add anything extra (including LF) after '$*'
+       eval "
+               want_trace && set -x
+               $*"
+}
+
+test_eval_ () {
+       # If "-x" tracing is in effect, then we want to avoid polluting stderr
+       # with non-test commands. But once in "set -x" mode, we cannot prevent
+       # the shell from printing the "set +x" to turn it off (nor the saving
+       # of $? before that). But we can make sure that the output goes to
+       # /dev/null.
+       #
+       # There are a few subtleties here:
+       #
+       #   - we have to redirect descriptor 4 in addition to 2, to cover
+       #     BASH_XTRACEFD
+       #
+       #   - the actual eval has to come before the redirection block (since
+       #     it needs to see descriptor 4 to set up its stderr)
+       #
+       #   - likewise, any error message we print must be outside the block to
+       #     access descriptor 4
+       #
+       #   - checking $? has to come immediately after the eval, but it must
+       #     be _inside_ the block to avoid polluting the "set -x" output
+       #
+
+       test_eval_inner_ "$@" </dev/null >&3 2>&4
+       {
+               test_eval_ret_=$?
+               if want_trace
                then
-                       test_ok_ "$2"
-               else
-                       test_failure_ "$@"
+                       set +x
                fi
+       } 2>/dev/null 4>&2
+
+       if test "$test_eval_ret_" != 0 && want_trace
+       then
+               say_color error >&4 "error: last command exited with \$?=$test_eval_ret_"
        fi
-       echo >&3 ""
+       return $test_eval_ret_
 }
 
-# test_external runs external test scripts that provide continuous
-# test output about their progress, and succeeds/fails on
-# zero/non-zero exit code.  It outputs the test output on stdout even
-# in non-verbose mode, and announces the external script with "* run
-# <n>: ..." before running it.  When providing relative paths, keep in
-# mind that all scripts run in "trash directory".
-# Usage: test_external description command arguments...
-# Example: test_external 'Perl API' perl ../path/to/test.pl
-test_external () {
-       test "$#" = 4 && { prereq=$1; shift; } || prereq=
-       test "$#" = 3 ||
-       error >&5 "bug in the test script: not 3 or 4 parameters to test_external"
-       descr="$1"
-       shift
-       if ! test_skip "$descr" "$@"
-       then
-               # Announce the script to reduce confusion about the
-               # test output that follows.
-               say_color "" " run $test_count: $descr ($*)"
-               # Run command; redirect its stderr to &4 as in
-               # test_run_, but keep its stdout on our stdout even in
-               # non-verbose mode.
-               "$@" 2>&4
-               if [ "$?" = 0 ]
+test_run_ () {
+       test_cleanup=:
+       expecting_failure=$2
+
+       if test "${GIT_TEST_CHAIN_LINT:-1}" != 0; then
+               # turn off tracing for this test-eval, as it simply creates
+               # confusing noise in the "-x" output
+               trace_tmp=$trace
+               trace=
+               # 117 is magic because it is unlikely to match the exit
+               # code of other programs
+               if test "OK-117" != "$(test_eval_ "(exit 117) && $1${LF}${LF}echo OK-\$?" 3>&1)"
                then
-                       test_ok_ "$descr"
-               else
-                       test_failure_ "$descr" "$@"
+                       error "bug in the test script: broken &&-chain or run-away HERE-DOC: $1"
                fi
+               trace=$trace_tmp
        fi
-}
 
-# Like test_external, but in addition tests that the command generated
-# no output on stderr.
-test_external_without_stderr () {
-       # The temporary file has no (and must have no) security
-       # implications.
-       tmp="$TMPDIR"; if [ -z "$tmp" ]; then tmp=/tmp; fi
-       stderr="$tmp/git-external-stderr.$$.tmp"
-       test_external "$@" 4> "$stderr"
-       [ -f "$stderr" ] || error "Internal error: $stderr disappeared."
-       descr="no stderr: $1"
-       shift
-       say >&3 "expecting no stderr from previous command"
-       if [ ! -s "$stderr" ]; then
-               rm "$stderr"
-               test_ok_ "$descr"
-       else
-               if [ "$verbose" = t ]; then
-                       output=`echo; echo Stderr is:; cat "$stderr"`
-               else
-                       output=
-               fi
-               # rm first in case test_failure exits.
-               rm "$stderr"
-               test_failure_ "$descr" "$@" "$output"
+       setup_malloc_check
+       test_eval_ "$1"
+       eval_ret=$?
+       teardown_malloc_check
+
+       if test -z "$immediate" || test $eval_ret = 0 ||
+          test -n "$expecting_failure" && test "$test_cleanup" != ":"
+       then
+               setup_malloc_check
+               test_eval_ "$test_cleanup"
+               teardown_malloc_check
+       fi
+       if test "$verbose" = "t" && test -n "$HARNESS_ACTIVE"
+       then
+               echo ""
        fi
+       return "$eval_ret"
 }
 
-# This is not among top-level (test_expect_success | test_expect_failure)
-# but is a prefix that can be used in the test script, like:
-#
-#      test_expect_success 'complain and die' '
-#           do something &&
-#           do something else &&
-#          test_must_fail git checkout ../outerspace
-#      '
-#
-# Writing this as "! git checkout ../outerspace" is wrong, because
-# the failure could be due to a segv.  We want a controlled failure.
+test_start_ () {
+       test_count=$(($test_count+1))
+       maybe_setup_verbose
+       maybe_setup_valgrind
+}
 
-test_must_fail () {
-       "$@"
-       test $? -gt 0 -a $? -le 129 -o $? -gt 192
+test_finish_ () {
+       echo >&3 ""
+       maybe_teardown_valgrind
+       maybe_teardown_verbose
 }
 
-# test_cmp is a helper function to compare actual and expected output.
-# You can use it like:
-#
-#      test_expect_success 'foo works' '
-#              echo expected >expected &&
-#              foo >actual &&
-#              test_cmp expected actual
-#      '
-#
-# This could be written as either "cmp" or "diff -u", but:
-# - cmp's output is not nearly as easy to read as diff -u
-# - not all diff versions understand "-u"
+test_skip () {
+       to_skip=
+       skipped_reason=
+       if match_pattern_list $this_test.$test_count $GIT_SKIP_TESTS
+       then
+               to_skip=t
+               skipped_reason="GIT_SKIP_TESTS"
+       fi
+       if test -z "$to_skip" && test -n "$test_prereq" &&
+          ! test_have_prereq "$test_prereq"
+       then
+               to_skip=t
 
-test_cmp() {
-       $GIT_TEST_CMP "$@"
+               of_prereq=
+               if test "$missing_prereq" != "$test_prereq"
+               then
+                       of_prereq=" of $test_prereq"
+               fi
+               skipped_reason="missing $missing_prereq${of_prereq}"
+       fi
+       if test -z "$to_skip" && test -n "$run_list" &&
+               ! match_test_selector_list '--run' $test_count "$run_list"
+       then
+               to_skip=t
+               skipped_reason="--run"
+       fi
+
+       case "$to_skip" in
+       t)
+               say_color skip >&3 "skipping test: $@"
+               say_color skip "ok $test_count # skip $1 ($skipped_reason)"
+               : true
+               ;;
+       *)
+               false
+               ;;
+       esac
 }
 
-# Most tests can use the created repository, but some may need to create more.
-# Usage: test_create_repo <directory>
-test_create_repo () {
-       test "$#" = 1 ||
-       error "bug in the test script: not 1 parameter to test-create-repo"
-       owd=`pwd`
-       repo="$1"
-       mkdir -p "$repo"
-       cd "$repo" || error "Cannot setup test environment"
-       "$GIT_EXEC_PATH/git-init" "--template=$TEST_DIRECTORY/../templates/blt/" >&3 2>&4 ||
-       error "cannot run git init -- have you built things yet?"
-       mv .git/hooks .git/hooks-disabled
-       cd "$owd"
+# stub; perf-lib overrides it
+test_at_end_hook_ () {
+       :
 }
 
 test_done () {
        GIT_EXIT_OK=t
-       test_results_dir="$TEST_DIRECTORY/test-results"
-       mkdir -p "$test_results_dir"
-       test_results_path="$test_results_dir/${0%.sh}-$$"
 
-       echo "total $test_count" >> $test_results_path
-       echo "success $test_success" >> $test_results_path
-       echo "fixed $test_fixed" >> $test_results_path
-       echo "broken $test_broken" >> $test_results_path
-       echo "failed $test_failure" >> $test_results_path
-       echo "" >> $test_results_path
+       if test -z "$HARNESS_ACTIVE"
+       then
+               test_results_dir="$TEST_OUTPUT_DIRECTORY/test-results"
+               mkdir -p "$test_results_dir"
+               base=${0##*/}
+               test_results_path="$test_results_dir/${base%.sh}.counts"
+
+               cat >"$test_results_path" <<-EOF
+               total $test_count
+               success $test_success
+               fixed $test_fixed
+               broken $test_broken
+               failed $test_failure
+
+               EOF
+       fi
 
        if test "$test_fixed" != 0
        then
-               say_color pass "fixed $test_fixed known breakage(s)"
+               say_color error "# $test_fixed known breakage(s) vanished; please update test(s)"
        fi
        if test "$test_broken" != 0
        then
-               say_color error "still have $test_broken known breakage(s)"
-               msg="remaining $(($test_count-$test_broken)) test(s)"
+               say_color warn "# still have $test_broken known breakage(s)"
+       fi
+       if test "$test_broken" != 0 || test "$test_fixed" != 0
+       then
+               test_remaining=$(( $test_count - $test_broken - $test_fixed ))
+               msg="remaining $test_remaining test(s)"
        else
+               test_remaining=$test_count
                msg="$test_count test(s)"
        fi
        case "$test_failure" in
        0)
-               say_color pass "passed all $msg"
+               if test $test_external_has_tap -eq 0
+               then
+                       if test $test_remaining -gt 0
+                       then
+                               say_color pass "# passed all $msg"
+                       fi
 
-               test -d "$remove_trash" &&
-               cd "$(dirname "$remove_trash")" &&
-               rm -rf "$(basename "$remove_trash")"
+                       # Maybe print SKIP message
+                       test -z "$skip_all" || skip_all="# SKIP $skip_all"
+                       case "$test_count" in
+                       0)
+                               say "1..$test_count${skip_all:+ $skip_all}"
+                               ;;
+                       *)
+                               test -z "$skip_all" ||
+                               say_color warn "$skip_all"
+                               say "1..$test_count"
+                               ;;
+                       esac
+               fi
+
+               if test -z "$debug"
+               then
+                       test -d "$TRASH_DIRECTORY" ||
+                       error "Tests passed but trash directory already removed before test cleanup; aborting"
+
+                       cd "$TRASH_DIRECTORY/.." &&
+                       rm -fr "$TRASH_DIRECTORY" ||
+                       error "Tests passed but test cleanup failed; aborting"
+               fi
+               test_at_end_hook_
 
                exit 0 ;;
 
        *)
-               say_color error "failed $test_failure among $msg"
+               if test $test_external_has_tap -eq 0
+               then
+                       say_color error "# failed $test_failure among $msg"
+                       say "1..$test_count"
+               fi
+
                exit 1 ;;
 
        esac
 }
 
-# Test the binaries we have just built.  The tests are kept in
-# t/ subdirectory and are run in 'trash directory' subdirectory.
-TEST_DIRECTORY=$(pwd)
 if test -n "$valgrind"
 then
        make_symlink () {
@@ -608,11 +861,21 @@ then
        }
 
        make_valgrind_symlink () {
-               # handle only executables
-               test -x "$1" || return
+               # handle only executables, unless they are shell libraries that
+               # need to be in the exec-path.
+               test -x "$1" ||
+               test "# " = "$(head -c 2 <"$1")" ||
+               return;
 
                base=$(basename "$1")
-               symlink_target=$TEST_DIRECTORY/../$base
+               case "$base" in
+               test-*)
+                       symlink_target="$GIT_BUILD_DIR/t/helper/$base"
+                       ;;
+               *)
+                       symlink_target="$GIT_BUILD_DIR/$base"
+                       ;;
+               esac
                # do not override scripts
                if test -x "$symlink_target" &&
                    test ! -d "$symlink_target" &&
@@ -631,10 +894,12 @@ then
        # override all git executables in TEST_DIRECTORY/..
        GIT_VALGRIND=$TEST_DIRECTORY/valgrind
        mkdir -p "$GIT_VALGRIND"/bin
-       for file in $TEST_DIRECTORY/../git* $TEST_DIRECTORY/../test-*
+       for file in $GIT_BUILD_DIR/git* $GIT_BUILD_DIR/t/helper/test-*
        do
                make_valgrind_symlink $file
        done
+       # special-case the mergetools loadables
+       make_symlink "$GIT_BUILD_DIR"/mergetools "$GIT_VALGRIND/bin/mergetools"
        OLDIFS=$IFS
        IFS=:
        for path in $PATH
@@ -649,92 +914,97 @@ then
        PATH=$GIT_VALGRIND/bin:$PATH
        GIT_EXEC_PATH=$GIT_VALGRIND/bin
        export GIT_VALGRIND
-elif test -n "$GIT_TEST_INSTALLED" ; then
+       GIT_VALGRIND_MODE="$valgrind"
+       export GIT_VALGRIND_MODE
+       GIT_VALGRIND_ENABLED=t
+       test -n "$valgrind_only" && GIT_VALGRIND_ENABLED=
+       export GIT_VALGRIND_ENABLED
+elif test -n "$GIT_TEST_INSTALLED"
+then
        GIT_EXEC_PATH=$($GIT_TEST_INSTALLED/git --exec-path)  ||
        error "Cannot run git from $GIT_TEST_INSTALLED."
-       PATH=$GIT_TEST_INSTALLED:$TEST_DIRECTORY/..:$PATH
+       PATH=$GIT_TEST_INSTALLED:$GIT_BUILD_DIR:$PATH
        GIT_EXEC_PATH=${GIT_TEST_EXEC_PATH:-$GIT_EXEC_PATH}
 else # normal case, use ../bin-wrappers only unless $with_dashes:
-       git_bin_dir="$TEST_DIRECTORY/../bin-wrappers"
-       if ! test -x "$git_bin_dir/git" ; then
-               if test -z "$with_dashes" ; then
+       git_bin_dir="$GIT_BUILD_DIR/bin-wrappers"
+       if ! test -x "$git_bin_dir/git"
+       then
+               if test -z "$with_dashes"
+               then
                        say "$git_bin_dir/git is not executable; using GIT_EXEC_PATH"
                fi
                with_dashes=t
        fi
        PATH="$git_bin_dir:$PATH"
-       GIT_EXEC_PATH=$TEST_DIRECTORY/..
-       if test -n "$with_dashes" ; then
-               PATH="$TEST_DIRECTORY/..:$PATH"
+       GIT_EXEC_PATH=$GIT_BUILD_DIR
+       if test -n "$with_dashes"
+       then
+               PATH="$GIT_BUILD_DIR:$PATH"
        fi
 fi
-GIT_TEMPLATE_DIR=$(pwd)/../templates/blt
-unset GIT_CONFIG
+GIT_TEMPLATE_DIR="$GIT_BUILD_DIR"/templates/blt
 GIT_CONFIG_NOSYSTEM=1
-GIT_CONFIG_NOGLOBAL=1
-export PATH GIT_EXEC_PATH GIT_TEMPLATE_DIR GIT_CONFIG_NOSYSTEM GIT_CONFIG_NOGLOBAL
+GIT_ATTR_NOSYSTEM=1
+export PATH GIT_EXEC_PATH GIT_TEMPLATE_DIR GIT_CONFIG_NOSYSTEM GIT_ATTR_NOSYSTEM
 
-. ../GIT-BUILD-OPTIONS
+if test -z "$GIT_TEST_CMP"
+then
+       if test -n "$GIT_TEST_CMP_USE_COPIED_CONTEXT"
+       then
+               GIT_TEST_CMP="$DIFF -c"
+       else
+               GIT_TEST_CMP="$DIFF -u"
+       fi
+fi
 
-GITPERLLIB=$(pwd)/../perl/blib/lib:$(pwd)/../perl/blib/arch/auto/Git
+GITPERLLIB="$GIT_BUILD_DIR"/perl/build/lib
 export GITPERLLIB
-test -d ../templates/blt || {
+test -d "$GIT_BUILD_DIR"/templates/blt || {
        error "You haven't built things yet, have you?"
 }
 
-if test -z "$GIT_TEST_INSTALLED" && test -z "$NO_PYTHON"
+if ! test -x "$GIT_BUILD_DIR"/t/helper/test-tool
 then
-       GITPYTHONLIB="$(pwd)/../git_remote_helpers/build/lib"
-       export GITPYTHONLIB
-       test -d ../git_remote_helpers/build || {
-               error "You haven't built git_remote_helpers yet, have you?"
-       }
-fi
-
-if ! test -x ../test-chmtime; then
-       echo >&2 'You need to build test-chmtime:'
-       echo >&2 'Run "make test-chmtime" in the source (toplevel) directory'
+       echo >&2 'You need to build test-tool:'
+       echo >&2 'Run "make t/helper/test-tool" in the source (toplevel) directory'
        exit 1
 fi
 
 # Test repository
-test="trash directory.$(basename "$0" .sh)"
-test -n "$root" && test="$root/$test"
-case "$test" in
-/*) TRASH_DIRECTORY="$test" ;;
- *) TRASH_DIRECTORY="$TEST_DIRECTORY/$test" ;;
+TRASH_DIRECTORY="trash directory.$(basename "$0" .sh)"
+test -n "$root" && TRASH_DIRECTORY="$root/$TRASH_DIRECTORY"
+case "$TRASH_DIRECTORY" in
+/*) ;; # absolute path is good
+ *) TRASH_DIRECTORY="$TEST_OUTPUT_DIRECTORY/$TRASH_DIRECTORY" ;;
 esac
-test ! -z "$debug" || remove_trash=$TRASH_DIRECTORY
-rm -fr "$test" || {
+rm -fr "$TRASH_DIRECTORY" || {
        GIT_EXIT_OK=t
        echo >&5 "FATAL: Cannot prepare test area"
        exit 1
 }
 
-test_create_repo "$test"
+HOME="$TRASH_DIRECTORY"
+GNUPGHOME="$HOME/gnupg-home-not-used"
+export HOME GNUPGHOME
+
+if test -z "$TEST_NO_CREATE_REPO"
+then
+       test_create_repo "$TRASH_DIRECTORY"
+else
+       mkdir -p "$TRASH_DIRECTORY"
+fi
 # Use -P to resolve symlinks in our working directory so that the cwd
 # in subprocesses like git equals our $PWD (for pathname comparisons).
-cd -P "$test" || exit 1
+cd -P "$TRASH_DIRECTORY" || exit 1
 
 this_test=${0##*/}
 this_test=${this_test%%-*}
-for skp in $GIT_SKIP_TESTS
-do
-       to_skip=
-       for skp in $GIT_SKIP_TESTS
-       do
-               case "$this_test" in
-               $skp)
-                       to_skip=t
-               esac
-       done
-       case "$to_skip" in
-       t)
-               say_color skip >&3 "skipping test $this_test altogether"
-               say_color skip "skip all tests in $this_test"
-               test_done
-       esac
-done
+if match_pattern_list "$this_test" $GIT_SKIP_TESTS
+then
+       say_color info >&3 "skipping test $this_test altogether"
+       skip_all="skip all tests in $this_test"
+       test_done
+fi
 
 # Provide an implementation of the 'yes' utility
 yes () {
@@ -745,14 +1015,17 @@ yes () {
                y="$*"
        fi
 
-       while echo "$y"
+       i=0
+       while test $i -lt 99
        do
-               :
+               echo "$y"
+               i=$(($i+1))
        done
 }
 
 # Fix some commands on Windows
-case $(uname -s) in
+uname_s=$(uname -s)
+case $uname_s in
 *MINGW*)
        # Windows has its own (incompatible) sort and find
        sort () {
@@ -761,9 +1034,6 @@ case $(uname -s) in
        find () {
                /usr/bin/find "$@"
        }
-       sum () {
-               md5sum "$@"
-       }
        # git sees Windows-style pwd
        pwd () {
                builtin pwd -W
@@ -771,6 +1041,18 @@ case $(uname -s) in
        # no POSIX permissions
        # backslashes in pathspec are converted to '/'
        # exec does not inherit the PID
+       test_set_prereq MINGW
+       test_set_prereq NATIVE_CRLF
+       test_set_prereq SED_STRIPS_CR
+       test_set_prereq GREP_STRIPS_CR
+       GIT_TEST_CMP=mingw_test_cmp
+       ;;
+*CYGWIN*)
+       test_set_prereq POSIXPERM
+       test_set_prereq EXECKEEPSPID
+       test_set_prereq CYGWIN
+       test_set_prereq SED_STRIPS_CR
+       test_set_prereq GREP_STRIPS_CR
        ;;
 *)
        test_set_prereq POSIXPERM
@@ -779,9 +1061,154 @@ case $(uname -s) in
        ;;
 esac
 
+( COLUMNS=1 && test $COLUMNS = 1 ) && test_set_prereq COLUMNS_CAN_BE_1
 test -z "$NO_PERL" && test_set_prereq PERL
+test -z "$NO_PTHREADS" && test_set_prereq PTHREADS
 test -z "$NO_PYTHON" && test_set_prereq PYTHON
+test -n "$USE_LIBPCRE1$USE_LIBPCRE2" && test_set_prereq PCRE
+test -n "$USE_LIBPCRE1" && test_set_prereq LIBPCRE1
+test -n "$USE_LIBPCRE2" && test_set_prereq LIBPCRE2
+test -z "$NO_GETTEXT" && test_set_prereq GETTEXT
+
+# Can we rely on git's output in the C locale?
+if test -n "$GETTEXT_POISON"
+then
+       GIT_GETTEXT_POISON=YesPlease
+       export GIT_GETTEXT_POISON
+       test_set_prereq GETTEXT_POISON
+else
+       test_set_prereq C_LOCALE_OUTPUT
+fi
+
+test_lazy_prereq PIPE '
+       # test whether the filesystem supports FIFOs
+       test_have_prereq !MINGW,!CYGWIN &&
+       rm -f testfifo && mkfifo testfifo
+'
+
+test_lazy_prereq SYMLINKS '
+       # test whether the filesystem supports symbolic links
+       ln -s x y && test -h y
+'
+
+test_lazy_prereq FILEMODE '
+       test "$(git config --bool core.filemode)" = true
+'
+
+test_lazy_prereq CASE_INSENSITIVE_FS '
+       echo good >CamelCase &&
+       echo bad >camelcase &&
+       test "$(cat CamelCase)" != good
+'
+
+test_lazy_prereq UTF8_NFD_TO_NFC '
+       # check whether FS converts nfd unicode to nfc
+       auml=$(printf "\303\244")
+       aumlcdiar=$(printf "\141\314\210")
+       >"$auml" &&
+       case "$(echo *)" in
+       "$aumlcdiar")
+               true ;;
+       *)
+               false ;;
+       esac
+'
+
+test_lazy_prereq AUTOIDENT '
+       sane_unset GIT_AUTHOR_NAME &&
+       sane_unset GIT_AUTHOR_EMAIL &&
+       git var GIT_AUTHOR_IDENT
+'
+
+test_lazy_prereq EXPENSIVE '
+       test -n "$GIT_TEST_LONG"
+'
+
+test_lazy_prereq EXPENSIVE_ON_WINDOWS '
+       test_have_prereq EXPENSIVE || test_have_prereq !MINGW,!CYGWIN
+'
+
+test_lazy_prereq USR_BIN_TIME '
+       test -x /usr/bin/time
+'
+
+test_lazy_prereq NOT_ROOT '
+       uid=$(id -u) &&
+       test "$uid" != 0
+'
+
+test_lazy_prereq JGIT '
+       type jgit
+'
+
+# SANITY is about "can you correctly predict what the filesystem would
+# do by only looking at the permission bits of the files and
+# directories?"  A typical example of !SANITY is running the test
+# suite as root, where a test may expect "chmod -r file && cat file"
+# to fail because file is supposed to be unreadable after a successful
+# chmod.  In an environment (i.e. combination of what filesystem is
+# being used and who is running the tests) that lacks SANITY, you may
+# be able to delete or create a file when the containing directory
+# doesn't have write permissions, or access a file even if the
+# containing directory doesn't have read or execute permissions.
+
+test_lazy_prereq SANITY '
+       mkdir SANETESTD.1 SANETESTD.2 &&
+
+       chmod +w SANETESTD.1 SANETESTD.2 &&
+       >SANETESTD.1/x 2>SANETESTD.2/x &&
+       chmod -w SANETESTD.1 &&
+       chmod -r SANETESTD.1/x &&
+       chmod -rx SANETESTD.2 ||
+       error "bug in test sript: cannot prepare SANETESTD"
+
+       ! test -r SANETESTD.1/x &&
+       ! rm SANETESTD.1/x && ! test -f SANETESTD.2/x
+       status=$?
+
+       chmod +rwx SANETESTD.1 SANETESTD.2 &&
+       rm -rf SANETESTD.1 SANETESTD.2 ||
+       error "bug in test sript: cannot clean SANETESTD"
+       return $status
+'
+
+test FreeBSD != $uname_s || GIT_UNZIP=${GIT_UNZIP:-/usr/local/bin/unzip}
+GIT_UNZIP=${GIT_UNZIP:-unzip}
+test_lazy_prereq UNZIP '
+       "$GIT_UNZIP" -v
+       test $? -ne 127
+'
+
+run_with_limited_cmdline () {
+       (ulimit -s 128 && "$@")
+}
+
+test_lazy_prereq CMDLINE_LIMIT '
+       test_have_prereq !MINGW,!CYGWIN &&
+       run_with_limited_cmdline true
+'
+
+run_with_limited_stack () {
+       (ulimit -s 128 && "$@")
+}
+
+test_lazy_prereq ULIMIT_STACK_SIZE '
+       test_have_prereq !MINGW,!CYGWIN &&
+       run_with_limited_stack true
+'
+
+build_option () {
+       git version --build-options |
+       sed -ne "s/^$1: //p"
+}
+
+test_lazy_prereq LONG_IS_64BIT '
+       test 8 -le "$(build_option sizeof-long)"
+'
+
+test_lazy_prereq TIME_IS_64BIT 'test-tool date is64bit'
+test_lazy_prereq TIME_T_IS_64BIT 'test-tool date time_t-is64bit'
 
-# test whether the filesystem supports symbolic links
-ln -s x y 2>/dev/null && test -h y 2>/dev/null && test_set_prereq SYMLINKS
-rm -f y
+test_lazy_prereq CURL '
+       curl --version
+'