SubmittingPatches: document how to reference previous commits
[gitweb.git] / t / test-lib.sh
index f7e55b1001f21774b9bab0107b68b59e45a9d79e..39c70f03269c263fb4872efed88a4142aec25be1 100644 (file)
@@ -15,9 +15,6 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see http://www.gnu.org/licenses/ .
 
-# Keep the original TERM for say_color
-ORIGINAL_TERM=$TERM
-
 # 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"
@@ -68,12 +65,12 @@ done,*)
 esac
 
 # 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=:
 # 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
@@ -109,6 +106,10 @@ export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
 export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
 export EDITOR
 
+# 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"
@@ -136,6 +137,9 @@ else
        }
 fi
 
+: ${ASAN_OPTIONS=detect_leaks=0}
+export ASAN_OPTIONS
+
 # Protect ourselves from common misconfiguration to export
 # CDPATH into the environment
 unset CDPATH
@@ -145,10 +149,7 @@ 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 ! *"
+       GIT_TRACE=4
        ;;
 esac
 
@@ -165,7 +166,11 @@ _z40=0000000000000000000000000000000000000000
 LF='
 '
 
-export _x05 _x40 _z40 LF
+# UTF-8 ZERO WIDTH NON-JOINER, which HFS+ ignores
+# when case-folding filenames
+u200c=$(printf '\342\200\214')
+
+export _x05 _x40 _z40 LF u200c
 
 # Each test should start with something like this, after copyright notices:
 #
@@ -173,10 +178,8 @@ export _x05 _x40 _z40 LF
 # 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
@@ -192,12 +195,20 @@ 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=$(expr "z$1" : 'z[^=]*=\(.*\)')
+               verbose_only=${1#--*=}
                shift ;;
        -q|--q|--qu|--qui|--quie|--quiet)
                # Ignore --quiet under a TAP::Harness. Saying how many tests
@@ -211,15 +222,25 @@ do
                valgrind=memcheck
                shift ;;
        --valgrind=*)
-               valgrind=$(expr "z$1" : 'z[^=]*=\(.*\)')
+               valgrind=${1#--*=}
                shift ;;
        --valgrind-only=*)
-               valgrind_only=$(expr "z$1" : 'z[^=]*=\(.*\)')
+               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)
+               trace=t
+               verbose=t
                shift ;;
        *)
                echo "error: unknown test option '$1'" >&2; exit 1 ;;
@@ -237,29 +258,30 @@ 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 setaf 4;; # blue
-               warn)
-                       tput setaf 3;; # brown/yellow
-               pass)
-                       tput setaf 2;; # green
-               info)
-                       tput setaf 6;; # cyan
-               *)
-                       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() {
@@ -269,6 +291,9 @@ else
        }
 fi
 
+TERM=dumb
+export TERM
+
 error () {
        say_color error "error: $*"
        GIT_EXIT_OK=t
@@ -297,6 +322,19 @@ 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
@@ -318,6 +356,7 @@ die () {
 
 GIT_EXIT_OK=
 trap 'die' EXIT
+trap 'exit $?' INT
 
 # The user-facing functions are loaded from a separate file so that
 # test_perf subshells can have them too
@@ -367,6 +406,99 @@ match_pattern_list () {
        return 1
 }
 
+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
+               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
+
+       test -n "$include"
+}
+
 maybe_teardown_verbose () {
        test -z "$verbose_only" && return
        exec 4>/dev/null 3>/dev/null
@@ -412,21 +544,70 @@ maybe_setup_valgrind () {
        fi
 }
 
+want_trace () {
+       test "$trace" = t && test "$verbose" = 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_ () {
-       # This is a separate function because some tests use
-       # "return" to end a test_expect_success block early.
-       eval </dev/null >&3 2>&4 "$*"
+       # We run this block with stderr redirected to avoid extra cruft
+       # during a "-x" trace. 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.
+       #
+       # The test itself is run with stderr put back to &4 (so either to
+       # /dev/null, or to the original stderr if --verbose was used).
+       {
+               test_eval_inner_ "$@" </dev/null >&3 2>&4
+               test_eval_ret_=$?
+               if want_trace
+               then
+                       set +x
+                       if test "$test_eval_ret_" != 0
+                       then
+                               say_color error >&4 "error: last command exited with \$?=$test_eval_ret_"
+                       fi
+               fi
+       } 2>/dev/null
+       return $test_eval_ret_
 }
 
 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
+               test_eval_ "(exit 117) && $1"
+               if test "$?" != 117; then
+                       error "bug in the test script: broken &&-chain: $1"
+               fi
+               trace=$trace_tmp
+       fi
+
        setup_malloc_check
        test_eval_ "$1"
        eval_ret=$?
        teardown_malloc_check
 
-       if test -z "$immediate" || test $eval_ret = 0 || test -n "$expecting_failure"
+       if test -z "$immediate" || test $eval_ret = 0 ||
+          test -n "$expecting_failure" && test "$test_cleanup" != ":"
        then
                setup_malloc_check
                test_eval_ "$test_cleanup"
@@ -453,25 +634,35 @@ test_finish_ () {
 
 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
-       fi
-       case "$to_skip" in
-       t)
+
                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 (missing $missing_prereq${of_prereq})"
+               say_color skip "ok $test_count # skip $1 ($skipped_reason)"
                : true
                ;;
        *)
@@ -528,7 +719,7 @@ test_done () {
                then
                        error "Can't use skip_all after running some tests"
                fi
-               [ -z "$skip_all" ] || skip_all=" # SKIP $skip_all"
+               test -z "$skip_all" || skip_all=" # SKIP $skip_all"
 
                if test $test_external_has_tap -eq 0
                then
@@ -698,7 +889,8 @@ rm -fr "$TRASH_DIRECTORY" || {
 }
 
 HOME="$TRASH_DIRECTORY"
-export HOME
+GNUPGHOME="$HOME/gnupg-home-not-used"
+export HOME GNUPGHOME
 
 if test -z "$TEST_NO_CREATE_REPO"
 then
@@ -728,9 +920,11 @@ yes () {
                y="$*"
        fi
 
-       while echo "$y"
+       i=0
+       while test $i -lt 99
        do
-               :
+               echo "$y"
+               i=$(($i+1))
        done
 }
 
@@ -755,7 +949,7 @@ case $(uname -s) in
        # backslashes in pathspec are converted to '/'
        # exec does not inherit the PID
        test_set_prereq MINGW
-       test_set_prereq NOT_CYGWIN
+       test_set_prereq NATIVE_CRLF
        test_set_prereq SED_STRIPS_CR
        test_set_prereq GREP_STRIPS_CR
        GIT_TEST_CMP=mingw_test_cmp
@@ -763,7 +957,6 @@ case $(uname -s) in
 *CYGWIN*)
        test_set_prereq POSIXPERM
        test_set_prereq EXECKEEPSPID
-       test_set_prereq NOT_MINGW
        test_set_prereq CYGWIN
        test_set_prereq SED_STRIPS_CR
        test_set_prereq GREP_STRIPS_CR
@@ -772,8 +965,6 @@ case $(uname -s) in
        test_set_prereq POSIXPERM
        test_set_prereq BSLASHPSPEC
        test_set_prereq EXECKEEPSPID
-       test_set_prereq NOT_MINGW
-       test_set_prereq NOT_CYGWIN
        ;;
 esac
 
@@ -822,7 +1013,7 @@ test_i18ngrep () {
 test_lazy_prereq PIPE '
        # test whether the filesystem supports FIFOs
        case $(uname -s) in
-       CYGWIN*)
+       CYGWIN*|MINGW*)
                false
                ;;
        *)
@@ -865,12 +1056,58 @@ test_lazy_prereq AUTOIDENT '
        git var GIT_AUTHOR_IDENT
 '
 
-# 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
+test_lazy_prereq EXPENSIVE '
+       test -n "$GIT_TEST_LONG"
+'
+
+test_lazy_prereq USR_BIN_TIME '
+       test -x /usr/bin/time
+'
+
+test_lazy_prereq NOT_ROOT '
+       uid=$(id -u) &&
+       test "$uid" != 0
+'
+
+# 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
+'
 
 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 'run_with_limited_cmdline true'