Code

Documentation/gitweb: trivial English fixes
[git.git] / t / test-lib.sh
index afd3053f96b789a73274217b384d245583500c04..bdd9513b84301275330d3dd7e49af05081ef9cd7 100644 (file)
@@ -2,6 +2,18 @@
 #
 # 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/ .
 
 # if --tee was passed, write the output not only to the terminal, but
 # additionally to the file test-results/$BASENAME.out, too.
@@ -31,40 +43,37 @@ TERM=dumb
 export LANG LC_ALL PAGER TERM 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
+unset $(perl -e '
+       my @env = keys %ENV;
+       my $ok = join("|", qw(
+               TRACE
+               DEBUG
+               USE_LOOKUP
+               TEST
+               .*_TEST
+               PROVE
+               VALGRIND
+       ));
+       my @vars = grep(/^GIT_/ && !/^GIT_($ok)/o, @env);
+       print join("\n", @vars);
+')
 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
 export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
 export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
 export EDITOR
-GIT_TEST_CMP=${GIT_TEST_CMP:-diff -u}
 
 # Protect ourselves from common misconfiguration to export
 # CDPATH into the environment
 unset CDPATH
 
+unset GREP_OPTIONS
+
 case $(echo $GIT_TRACE |tr "[A-Z]" "[a-z]") in
        1|2|true)
                echo "* warning: Some tests will not work if GIT_TRACE" \
@@ -80,6 +89,13 @@ esac
 _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"
 
+# Zero SHA-1
+_z40=0000000000000000000000000000000000000000
+
+# Line feed
+LF='
+'
+
 # Each test should start with something like this, after copyright notices:
 #
 # test_description='Description of this test...
@@ -110,14 +126,13 @@ do
        -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
                verbose=t; 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 ;;
        --tee)
@@ -143,7 +158,7 @@ if test -n "$color"; then
                        *) test -n "$quiet" && return;;
                esac
                shift
-               printf "%s" "$*"
+               printf "%s" "$*"
                tput sgr0
                echo
                )
@@ -152,7 +167,7 @@ else
        say_color() {
                test -z "$1" && test -n "$quiet" && return
                shift
-               echo "$*"
+               echo "$*"
        }
 fi
 
@@ -189,6 +204,8 @@ test_fixed=0
 test_broken=0
 test_success=0
 
+test_external_has_tap=0
+
 die () {
        code=$?
        if test -n "$GIT_EXIT_OK"
@@ -220,14 +237,51 @@ test_set_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'
+       awk '
+               function name(n) {
+                       if (n == 0) return "RESET";
+                       if (n == 1) return "BOLD";
+                       if (n == 30) return "BLACK";
+                       if (n == 31) return "RED";
+                       if (n == 32) return "GREEN";
+                       if (n == 33) return "YELLOW";
+                       if (n == 34) return "BLUE";
+                       if (n == 35) return "MAGENTA";
+                       if (n == 36) return "CYAN";
+                       if (n == 37) return "WHITE";
+                       if (n == 40) return "BLACK";
+                       if (n == 41) return "BRED";
+                       if (n == 42) return "BGREEN";
+                       if (n == 43) return "BYELLOW";
+                       if (n == 44) return "BBLUE";
+                       if (n == 45) return "BMAGENTA";
+                       if (n == 46) return "BCYAN";
+                       if (n == 47) return "BWHITE";
+               }
+               {
+                       while (match($0, /\033\[[0-9;]*m/) != 0) {
+                               printf "%s<", substr($0, 1, RSTART-1);
+                               codes = substr($0, RSTART+2, RLENGTH-3);
+                               if (length(codes) == 0)
+                                       printf "%s", name(0)
+                               else {
+                                       n = split(codes, ary, ";");
+                                       sep = "";
+                                       for (i = 1; i <= n; i++) {
+                                               printf "%s%s", sep, name(ary[i]);
+                                               sep = ";"
+                                       }
+                               }
+                               printf ">";
+                               $0 = substr($0, RSTART + RLENGTH, length($0) - RSTART - RLENGTH + 1);
+                       }
+                       print
+               }
+       '
+}
+
+nul_to_q () {
+       perl -pe 'y/\000/Q/'
 }
 
 q_to_nul () {
@@ -238,6 +292,10 @@ q_to_cr () {
        tr Q '\015'
 }
 
+q_to_tab () {
+       tr Q '\011'
+}
+
 append_cr () {
        sed -e 's/$/Q/' | tr Q '\015'
 }
@@ -246,6 +304,17 @@ remove_cr () {
        tr '\015' Q | sed -e 's/Q$//'
 }
 
+# In some bourne shell implementations, the "unset" builtin returns
+# nonzero status when a variable to be unset was not set in the first
+# place.
+#
+# Use sane_unset when that should not be considered an error.
+
+sane_unset () {
+       unset "$@"
+       return 0
+}
+
 test_tick () {
        if test -z "${test_tick+set}"
        then
@@ -292,6 +361,24 @@ test_chmod () {
        git update-index --add "--chmod=$@"
 }
 
+# Unset a configuration variable, but don't fail if it doesn't exist.
+test_unconfig () {
+       git config --unset-all "$@"
+       config_status=$?
+       case "$config_status" in
+       5) # ok, nothing to unset
+               config_status=0
+               ;;
+       esac
+       return $config_status
+}
+
+# Set git config, automatically unsetting it after the test is over.
+test_config () {
+       test_when_finished "test_unconfig '$1'" &&
+       git config "$@"
+}
+
 # Use test_set_prereq to tell that a particular prerequisite is available.
 # The prerequisite can later be checked for in two ways:
 #
@@ -309,12 +396,44 @@ test_set_prereq () {
 satisfied=" "
 
 test_have_prereq () {
-       case $satisfied in
-       *" $1 "*)
-               : yes, have it ;;
-       *)
-               ! : nope ;;
+       # prerequisites can be concatenated with ','
+       save_IFS=$IFS
+       IFS=,
+       set -- $*
+       IFS=$save_IFS
+
+       total_prereq=0
+       ok_prereq=0
+       missing_prereq=
+
+       for prerequisite
+       do
+               total_prereq=$(($total_prereq + 1))
+               case $satisfied in
+               *" $prerequisite "*)
+                       ok_prereq=$(($ok_prereq + 1))
+                       ;;
+               *)
+                       # Keep a list of missing prerequisites
+                       if test -z "$missing_prereq"
+                       then
+                               missing_prereq=$prerequisite
+                       else
+                               missing_prereq="$prerequisite,$missing_prereq"
+                       fi
+               esac
+       done
+
+       test $total_prereq = $ok_prereq
+}
+
+test_declared_prereq () {
+       case ",$test_prereq," in
+       *,$1,*)
+               return 0
+               ;;
        esac
+       return 1
 }
 
 # You are not expected to call test_ok_ and test_failure_ directly, use
@@ -322,35 +441,51 @@ test_have_prereq () {
 
 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/^/        /'
+       echo "$@" | 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 "" "ok $test_count - $@ # TODO known breakage"
 }
 
 test_known_broken_failure_ () {
        test_broken=$(($test_broken+1))
-       say_color skip "  still broken $test_count: $@"
+       say_color skip "not ok $test_count - $@ # TODO known breakage"
 }
 
 test_debug () {
        test "$debug" = "" || eval "$1"
 }
 
+test_eval_ () {
+       # This is a separate function because some tests use
+       # "return" to end a test_expect_success block early.
+       eval >&3 2>&4 "$*"
+}
+
 test_run_ () {
-       eval >&3 2>&4 "$1"
-       eval_ret="$?"
-       return 0
+       test_cleanup=:
+       expecting_failure=$2
+       test_eval_ "$1"
+       eval_ret=$?
+
+       if test -z "$immediate" || test $eval_ret = 0 || test -n "$expecting_failure"
+       then
+               test_eval_ "$test_cleanup"
+       fi
+       if test "$verbose" = "t" && test -n "$HARNESS_ACTIVE"; then
+               echo ""
+       fi
+       return "$eval_ret"
 }
 
 test_skip () {
@@ -361,17 +496,24 @@ test_skip () {
                case $this_test.$test_count in
                $skp)
                        to_skip=t
+                       break
                esac
        done
-       if test -z "$to_skip" && test -n "$prereq" &&
-          ! test_have_prereq "$prereq"
+       if test -z "$to_skip" && test -n "$test_prereq" &&
+          ! test_have_prereq "$test_prereq"
        then
                to_skip=t
        fi
        case "$to_skip" in
        t)
+               of_prereq=
+               if test "$missing_prereq" != "$test_prereq"
+               then
+                       of_prereq=" of $test_prereq"
+               fi
+
                say_color skip >&3 "skipping test: $@"
-               say_color skip "skip $test_count: $1"
+               say_color skip "ok $test_count # skip $1 (missing $missing_prereq${of_prereq})"
                : true
                ;;
        *)
@@ -381,14 +523,14 @@ test_skip () {
 }
 
 test_expect_failure () {
-       test "$#" = 3 && { prereq=$1; shift; } || prereq=
+       test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
        test "$#" = 2 ||
        error "bug in the test script: not 2 or 3 parameters to test-expect-failure"
+       export test_prereq
        if ! test_skip "$@"
        then
                say >&3 "checking known breakage: $2"
-               test_run_ "$2"
-               if [ "$?" = 0 -a "$eval_ret" = 0 ]
+               if test_run_ "$2" expecting_failure
                then
                        test_known_broken_ok_ "$1"
                else
@@ -399,14 +541,14 @@ test_expect_failure () {
 }
 
 test_expect_success () {
-       test "$#" = 3 && { prereq=$1; shift; } || prereq=
+       test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
        test "$#" = 2 ||
        error "bug in the test script: not 2 or 3 parameters to test-expect-success"
+       export test_prereq
        if ! test_skip "$@"
        then
                say >&3 "expecting success: $2"
-               test_run_ "$2"
-               if [ "$?" = 0 -a "$eval_ret" = 0 ]
+               if test_run_ "$2"
                then
                        test_ok_ "$1"
                else
@@ -416,52 +558,48 @@ test_expect_success () {
        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" ]
-               then
-                       test_ok_ "$2"
-               else
-                       test_failure_ "$@"
-               fi
-       fi
-       echo >&3 ""
-}
-
 # 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
+# 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 "$#" = 4 && { test_prereq=$1; shift; } || test_prereq=
        test "$#" = 3 ||
        error >&5 "bug in the test script: not 3 or 4 parameters to test_external"
        descr="$1"
        shift
+       export test_prereq
        if ! test_skip "$descr" "$@"
        then
                # Announce the script to reduce confusion about the
                # test output that follows.
-               say_color "" " run $test_count: $descr ($*)"
+               say_color "" "# run $test_count: $descr ($*)"
+               # Export TEST_DIRECTORY, TRASH_DIRECTORY and GIT_TEST_LONG
+               # to be able to use them in script
+               export TEST_DIRECTORY TRASH_DIRECTORY GIT_TEST_LONG
                # 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 ]
                then
-                       test_ok_ "$descr"
+                       if test $test_external_has_tap -eq 0; then
+                               test_ok_ "$descr"
+                       else
+                               say_color "" "# test_external test $descr was ok"
+                               test_success=$(($test_success + 1))
+                       fi
                else
-                       test_failure_ "$descr" "$@"
+                       if test $test_external_has_tap -eq 0; then
+                               test_failure_ "$descr" "$@"
+                       else
+                               say_color error "# test_external test $descr failed: $@"
+                               test_failure=$(($test_failure + 1))
+                       fi
                fi
        fi
 }
@@ -471,25 +609,90 @@ test_external () {
 test_external_without_stderr () {
        # The temporary file has no (and must have no) security
        # implications.
-       tmp="$TMPDIR"; if [ -z "$tmp" ]; then tmp=/tmp; fi
+       tmp=${TMPDIR:-/tmp}
        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"
+       say >&3 "expecting no stderr from previous command"
        if [ ! -s "$stderr" ]; then
                rm "$stderr"
-               test_ok_ "$descr"
+
+               if test $test_external_has_tap -eq 0; then
+                       test_ok_ "$descr"
+               else
+                       say_color "" "# test_external_without_stderr test $descr was ok"
+                       test_success=$(($test_success + 1))
+               fi
        else
                if [ "$verbose" = t ]; then
-                       output=`echo; echo Stderr is:; cat "$stderr"`
+                       output=`echo; echo "# Stderr is:"; cat "$stderr"`
                else
                        output=
                fi
                # rm first in case test_failure exits.
                rm "$stderr"
-               test_failure_ "$descr" "$@" "$output"
+               if test $test_external_has_tap -eq 0; then
+                       test_failure_ "$descr" "$@" "$output"
+               else
+                       say_color error "# test_external_without_stderr test $descr failed: $@: $output"
+                       test_failure=$(($test_failure + 1))
+               fi
+       fi
+}
+
+# debugging-friendly alternatives to "test [-f|-d|-e]"
+# The commands test the existence or non-existence of $1. $2 can be
+# given to provide a more precise diagnosis.
+test_path_is_file () {
+       if ! [ -f "$1" ]
+       then
+               echo "File $1 doesn't exist. $*"
+               false
+       fi
+}
+
+test_path_is_dir () {
+       if ! [ -d "$1" ]
+       then
+               echo "Directory $1 doesn't exist. $*"
+               false
+       fi
+}
+
+test_path_is_missing () {
+       if [ -e "$1" ]
+       then
+               echo "Path exists:"
+               ls -ld "$1"
+               if [ $# -ge 1 ]; then
+                       echo "$*"
+               fi
+               false
+       fi
+}
+
+# test_line_count checks that a file has the number of lines it
+# ought to. For example:
+#
+#      test_expect_success 'produce exactly one line of output' '
+#              do something >output &&
+#              test_line_count = 1 output
+#      '
+#
+# is like "test $(wc -l <output) = 1" except that it passes the
+# output through when the number of lines is wrong.
+
+test_line_count () {
+       if test $# != 3
+       then
+               error "bug in the test script: not 3 parameters to test_line_count"
+       elif ! test $(wc -l <"$3") "$1" "$2"
+       then
+               echo "test_line_count: line count for $3 !$1 $2"
+               cat "$3"
+               return 1
        fi
 }
 
@@ -507,7 +710,63 @@ test_external_without_stderr () {
 
 test_must_fail () {
        "$@"
-       test $? -gt 0 -a $? -le 129 -o $? -gt 192
+       exit_code=$?
+       if test $exit_code = 0; then
+               echo >&2 "test_must_fail: command succeeded: $*"
+               return 1
+       elif test $exit_code -gt 129 -a $exit_code -le 192; then
+               echo >&2 "test_must_fail: died by signal: $*"
+               return 1
+       elif test $exit_code = 127; then
+               echo >&2 "test_must_fail: command not found: $*"
+               return 1
+       fi
+       return 0
+}
+
+# Similar to test_must_fail, but tolerates success, too.  This is
+# meant to be used in contexts like:
+#
+#      test_expect_success 'some command works without configuration' '
+#              test_might_fail git config --unset all.configuration &&
+#              do something
+#      '
+#
+# Writing "git config --unset all.configuration || :" would be wrong,
+# because we want to notice if it fails due to segv.
+
+test_might_fail () {
+       "$@"
+       exit_code=$?
+       if test $exit_code -gt 129 -a $exit_code -le 192; then
+               echo >&2 "test_might_fail: died by signal: $*"
+               return 1
+       elif test $exit_code = 127; then
+               echo >&2 "test_might_fail: command not found: $*"
+               return 1
+       fi
+       return 0
+}
+
+# Similar to test_must_fail and test_might_fail, but check that a
+# given command exited with a given exit code. Meant to be used as:
+#
+#      test_expect_success 'Merge with d/f conflicts' '
+#              test_expect_code 1 git merge "merge msg" B master
+#      '
+
+test_expect_code () {
+       want_code=$1
+       shift
+       "$@"
+       exit_code=$?
+       if test $exit_code = $want_code
+       then
+               return 0
+       fi
+
+       echo >&2 "test_expect_code: command exited with $exit_code, we wanted $want_code $*"
+       return 1
 }
 
 # test_cmp is a helper function to compare actual and expected output.
@@ -527,48 +786,87 @@ test_cmp() {
        $GIT_TEST_CMP "$@"
 }
 
+# This function can be used to schedule some commands to be run
+# unconditionally at the end of the test to restore sanity:
+#
+#      test_expect_success 'test core.capslock' '
+#              git config core.capslock true &&
+#              test_when_finished "git config --unset core.capslock" &&
+#              hello world
+#      '
+#
+# That would be roughly equivalent to
+#
+#      test_expect_success 'test core.capslock' '
+#              git config core.capslock true &&
+#              hello world
+#              git config --unset core.capslock
+#      '
+#
+# except that the greeting and config --unset must both succeed for
+# the test to pass.
+#
+# Note that under --immediate mode, no clean-up is done to help diagnose
+# what went wrong.
+
+test_when_finished () {
+       test_cleanup="{ $*
+               } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
+}
+
 # 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"
+       (
+               cd "$repo" || error "Cannot setup test environment"
+               "$GIT_EXEC_PATH/git-init" "--template=$GIT_BUILD_DIR/templates/blt/" >&3 2>&4 ||
+               error "cannot run git init -- have you built things yet?"
+               mv .git/hooks .git/hooks-disabled
+       ) || exit
 }
 
 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_DIRECTORY/test-results"
+               mkdir -p "$test_results_dir"
+               test_results_path="$test_results_dir/${0%.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 pass "fixed $test_fixed known breakage(s)"
        fi
        if test "$test_broken" != 0
        then
-               say_color error "still have $test_broken known breakage(s)"
+               say_color error "still have $test_broken known breakage(s)"
                msg="remaining $(($test_count-$test_broken)) test(s)"
        else
                msg="$test_count test(s)"
        fi
        case "$test_failure" in
        0)
-               say_color pass "passed all $msg"
+               # Maybe print SKIP message
+               [ -z "$skip_all" ] || skip_all=" # SKIP $skip_all"
+
+               if test $test_external_has_tap -eq 0; then
+                       say_color pass "# passed all $msg"
+                       say "1..$test_count$skip_all"
+               fi
 
                test -d "$remove_trash" &&
                cd "$(dirname "$remove_trash")" &&
@@ -577,7 +875,11 @@ test_done () {
                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
@@ -585,7 +887,15 @@ test_done () {
 
 # 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 -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)
+fi
+GIT_BUILD_DIR="$TEST_DIRECTORY"/..
+
 if test -n "$valgrind"
 then
        make_symlink () {
@@ -608,11 +918,16 @@ 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.  We will just use "#!" as a
+               # guess for a shell-script, since we have no idea what the user
+               # may have configured as the shell path.
+               test -x "$1" ||
+               test "#!" = "$(head -c 2 <"$1")" ||
+               return;
 
                base=$(basename "$1")
-               symlink_target=$TEST_DIRECTORY/../$base
+               symlink_target=$GIT_BUILD_DIR/$base
                # do not override scripts
                if test -x "$symlink_target" &&
                    test ! -d "$symlink_target" &&
@@ -631,10 +946,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/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
@@ -652,10 +969,10 @@ then
 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"
+       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"
@@ -663,35 +980,45 @@ else # normal case, use ../bin-wrappers only unless $with_dashes:
                with_dashes=t
        fi
        PATH="$git_bin_dir:$PATH"
-       GIT_EXEC_PATH=$TEST_DIRECTORY/..
+       GIT_EXEC_PATH=$GIT_BUILD_DIR
        if test -n "$with_dashes" ; then
-               PATH="$TEST_DIRECTORY/..:$PATH"
+               PATH="$GIT_BUILD_DIR:$PATH"
        fi
 fi
-GIT_TEMPLATE_DIR=$(pwd)/../templates/blt
+GIT_TEMPLATE_DIR="$GIT_BUILD_DIR"/templates/blt
 unset GIT_CONFIG
 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
+. "$GIT_BUILD_DIR"/GIT-BUILD-OPTIONS
 
-GITPERLLIB=$(pwd)/../perl/blib/lib:$(pwd)/../perl/blib/arch/auto/Git
+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="$GIT_BUILD_DIR"/perl/blib/lib:"$GIT_BUILD_DIR"/perl/blib/arch/auto/Git
 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"
 then
-       GITPYTHONLIB="$(pwd)/../git_remote_helpers/build/lib"
+       GITPYTHONLIB="$GIT_BUILD_DIR/git_remote_helpers/build/lib"
        export GITPYTHONLIB
-       test -d ../git_remote_helpers/build || {
+       test -d "$GIT_BUILD_DIR"/git_remote_helpers/build || {
                error "You haven't built git_remote_helpers yet, have you?"
        }
 fi
 
-if ! test -x ../test-chmtime; then
+if ! test -x "$GIT_BUILD_DIR"/test-chmtime; then
        echo >&2 'You need to build test-chmtime:'
        echo >&2 'Run "make test-chmtime" in the source (toplevel) directory'
        exit 1
@@ -711,6 +1038,9 @@ rm -fr "$test" || {
        exit 1
 }
 
+HOME="$TRASH_DIRECTORY"
+export HOME
+
 test_create_repo "$test"
 # Use -P to resolve symlinks in our working directory so that the cwd
 # in subprocesses like git equals our $PWD (for pathname comparisons).
@@ -720,18 +1050,10 @@ 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)
+       case "$this_test" in
+       $skp)
                say_color skip >&3 "skipping test $this_test altogether"
-               say_color skip "skip all tests in $this_test"
+               skip_all="skip all tests in $this_test"
                test_done
        esac
 done
@@ -771,17 +1093,66 @@ 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 SED_STRIPS_CR
+       ;;
+*CYGWIN*)
+       test_set_prereq POSIXPERM
+       test_set_prereq EXECKEEPSPID
+       test_set_prereq NOT_MINGW
+       test_set_prereq SED_STRIPS_CR
        ;;
 *)
        test_set_prereq POSIXPERM
        test_set_prereq BSLASHPSPEC
        test_set_prereq EXECKEEPSPID
+       test_set_prereq NOT_MINGW
        ;;
 esac
 
 test -z "$NO_PERL" && test_set_prereq PERL
 test -z "$NO_PYTHON" && test_set_prereq PYTHON
+test -n "$USE_LIBPCRE" && test_set_prereq LIBPCRE
+
+# 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
+else
+       test_set_prereq C_LOCALE_OUTPUT
+fi
+
+# Use this instead of test_cmp to compare files that contain expected and
+# actual output from git commands that can be translated.  When running
+# under GETTEXT_POISON this pretends that the command produced expected
+# results.
+test_i18ncmp () {
+       test -n "$GETTEXT_POISON" || test_cmp "$@"
+}
+
+# Use this instead of "grep expected-string actual" to see if the
+# output from a git command that can be translated either contains an
+# expected string, or does not contain an unwanted one.  When running
+# under GETTEXT_POISON this pretends that the command produced expected
+# results.
+test_i18ngrep () {
+       if test -n "$GETTEXT_POISON"
+       then
+           : # pretend success
+       elif test "x!" = "x$1"
+       then
+               shift
+               ! grep "$@"
+       else
+               grep "$@"
+       fi
+}
 
 # 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
+
+# When the tests are run as root, permission tests will report that
+# things are writable when they shouldn't be.
+test -w / || test_set_prereq SANITY