t7004: fix embedded single-quotes
[gitweb.git] / t / test-lib.sh
index afa411e128d9778970b2b2d416605d3c7e86e21a..50b3d3f45dde25b63d1bff636056565be70a17ba 100644 (file)
@@ -91,6 +91,7 @@ unset VISUAL EMAIL LANGUAGE COLUMNS $("$PERL_PATH" -e '
                VALGRIND
                UNZIP
                PERF_
+               CURL_VERBOSE
        ));
        my @vars = grep(/^GIT_/ && !/^GIT_($ok)/o, @env);
        print join("\n", @vars);
@@ -108,6 +109,16 @@ 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"
+       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 ||
@@ -129,6 +140,9 @@ else
        }
 fi
 
+: ${ASAN_OPTIONS=detect_leaks=0}
+export ASAN_OPTIONS
+
 # Protect ourselves from common misconfiguration to export
 # CDPATH into the environment
 unset CDPATH
@@ -170,16 +184,8 @@ export _x05 _x40 _z40 LF u200c
 # This test checks if command xyzzy does the right thing...
 # '
 # . ./test-lib.sh
-[ "x$ORIGINAL_TERM" != "xdumb" ] && (
-               TERM=$ORIGINAL_TERM &&
-               export TERM &&
-               [ -t 1 ] &&
-               tput bold >/dev/null 2>&1 &&
-               tput setaf 1 >/dev/null 2>&1 &&
-               tput sgr0 >/dev/null 2>&1
-       ) &&
-       color=t
 
+unset color
 while test "$#" -ne 0
 do
        case "$1" in
@@ -189,6 +195,14 @@ 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=$(expr "z$1" : 'z[^=]*=\(.*\)'); shift ;;
        -h|--h|--he|--hel|--help)
                help=t; shift ;;
        -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
@@ -218,6 +232,16 @@ do
        --root=*)
                root=$(expr "z$1" : 'z[^=]*=\(.*\)')
                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 ;;
        esac
@@ -232,40 +256,6 @@ then
        verbose=t
 fi
 
-if test -n "$color"
-then
-       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
-               shift
-               printf "%s" "$*"
-               tput sgr0
-               echo
-               )
-       }
-else
-       say_color() {
-               test -z "$1" && test -n "$quiet" && return
-               shift
-               printf "%s\n" "$*"
-       }
-fi
-
 error () {
        say_color error "error: $*"
        GIT_EXIT_OK=t
@@ -364,6 +354,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
@@ -409,21 +492,61 @@ maybe_setup_valgrind () {
        fi
 }
 
+# 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 "
+               test \"$trace\" = t && 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 test "$trace" = t
+               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:-0}" != 0; then
+               # 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
+       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"
@@ -450,25 +573,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
                ;;
        *)
@@ -525,7 +658,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
@@ -653,7 +786,6 @@ else # normal case, use ../bin-wrappers only unless $with_dashes:
        fi
 fi
 GIT_TEMPLATE_DIR="$GIT_BUILD_DIR"/templates/blt
-unset GIT_CONFIG
 GIT_CONFIG_NOSYSTEM=1
 GIT_ATTR_NOSYSTEM=1
 export PATH GIT_EXEC_PATH GIT_TEMPLATE_DIR GIT_CONFIG_NOSYSTEM GIT_ATTR_NOSYSTEM
@@ -696,7 +828,54 @@ rm -fr "$TRASH_DIRECTORY" || {
 }
 
 HOME="$TRASH_DIRECTORY"
-export HOME
+GNUPGHOME="$HOME/gnupg-home-not-used"
+export HOME GNUPGHOME
+
+# run the tput tests *after* changing HOME (in case ncurses needs
+# ~/.terminfo for $TERM)
+test -n "${color+set}" || test "x$ORIGINAL_TERM" != "xdumb" && (
+               TERM=$ORIGINAL_TERM &&
+               export TERM &&
+               test -t 1 &&
+               tput bold >/dev/null 2>&1 &&
+               tput setaf 1 >/dev/null 2>&1 &&
+               tput sgr0 >/dev/null 2>&1
+       ) &&
+       color=t
+
+if test -n "$color"
+then
+       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
+               shift
+               printf "%s" "$*"
+               tput sgr0
+               echo
+               )
+       }
+else
+       say_color() {
+               test -z "$1" && test -n "$quiet" && return
+               shift
+               printf "%s\n" "$*"
+       }
+fi
 
 if test -z "$TEST_NO_CREATE_REPO"
 then
@@ -753,7 +932,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
@@ -761,7 +940,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
@@ -770,8 +948,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
 
@@ -863,9 +1039,41 @@ 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
+'
+
+# On a filesystem that lacks SANITY, a file can be deleted even if
+# the containing directory doesn't have write permissions, or a file
+# can be accessed even if the containing directory doesn't have read
+# or execute permissions, causing our tests that validate that Git
+# works sensibly in such situations.
+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 -rx SANETESTD.2 ||
+       error "bug in test sript: cannot prepare SANETESTD"
+
+       ! 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 '