'
}
+lf_to_nul () {
+ perl -pe 'y/\012/\000/'
+}
+
nul_to_q () {
perl -pe 'y/\000/Q/'
}
export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
}
-# Stop execution and start a shell. This is useful for debugging tests and
-# only makes sense together with "-v".
+# Stop execution and start a shell. This is useful for debugging tests.
#
# Be sure to remove all invocations of this command before submitting.
test_pause () {
- if test "$verbose" = t; then
- "$SHELL_PATH" <&6 >&3 2>&4
- else
- error >&5 "test_pause requires --verbose"
- fi
+ "$SHELL_PATH" <&6 >&5 2>&7
}
-# Call test_commit with the arguments "<message> [<file> [<contents> [<tag>]]]"
+# Wrap git in gdb. Adding this to a command can make it easier to
+# understand what is going on in a failing test.
+#
+# Example: "debug git checkout master".
+debug () {
+ GIT_TEST_GDB=1 "$@" <&6 >&5 2>&7
+}
+
+# Call test_commit with the arguments
+# [-C <directory>] <message> [<file> [<contents> [<tag>]]]"
#
# This will commit a file with the given contents and the given commit
# message, and tag the resulting commit with the given tag name.
#
# <file>, <contents>, and <tag> all default to <message>.
+#
+# If the first argument is "-C", the second argument is used as a path for
+# the git invocations.
test_commit () {
notick= &&
signoff= &&
+ indir= &&
while test $# != 0
do
case "$1" in
--signoff)
signoff="$1"
;;
+ -C)
+ indir="$2"
+ shift
+ ;;
*)
break
;;
esac
shift
done &&
+ indir=${indir:+"$indir"/} &&
file=${2:-"$1.t"} &&
- echo "${3-$1}" > "$file" &&
- git add "$file" &&
+ echo "${3-$1}" > "$indir$file" &&
+ git ${indir:+ -C "$indir"} add "$file" &&
if test -z "$notick"
then
test_tick
fi &&
- git commit $signoff -m "$1" &&
- git tag "${4:-$1}"
+ git ${indir:+ -C "$indir"} commit $signoff -m "$1" &&
+ git ${indir:+ -C "$indir"} tag "${4:-$1}"
}
# Call test_merge with the arguments "<message> <commit>", where <commit>
git update-index --add "--chmod=$@"
}
+# Get the modebits from a file.
+test_modebits () {
+ ls -l "$1" | sed -e 's|^\(..........\).*|\1|'
+}
+
# Unset a configuration variable, but don't fail if it doesn't exist.
test_unconfig () {
- git config --unset-all "$@"
+ config_dir=
+ if test "$1" = -C
+ then
+ shift
+ config_dir=$1
+ shift
+ fi
+ git ${config_dir:+-C "$config_dir"} config --unset-all "$@"
config_status=$?
case "$config_status" in
5) # ok, nothing to unset
# Set git config, automatically unsetting it after the test is over.
test_config () {
- test_when_finished "test_unconfig '$1'" &&
- git config "$@"
+ config_dir=
+ if test "$1" = -C
+ then
+ shift
+ config_dir=$1
+ shift
+ fi
+ test_when_finished "test_unconfig ${config_dir:+-C '$config_dir'} '$1'" &&
+ git ${config_dir:+-C "$config_dir"} config "$@"
}
test_config_global () {
fi
}
+# Returns success if a comma separated string of keywords ($1) contains a
+# given keyword ($2).
+# Examples:
+# `list_contains "foo,bar" bar` returns 0
+# `list_contains "foo" bar` returns 1
+
+list_contains () {
+ case ",$1," in
+ *,$2,*)
+ return 0
+ ;;
+ esac
+ return 1
+}
+
# 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:
#
# the failure could be due to a segv. We want a controlled failure.
test_must_fail () {
+ case "$1" in
+ ok=*)
+ _test_ok=${1#ok=}
+ shift
+ ;;
+ *)
+ _test_ok=
+ ;;
+ esac
"$@"
exit_code=$?
- if test $exit_code = 0; then
+ if test $exit_code -eq 0 && ! list_contains "$_test_ok" success
+ then
echo >&2 "test_must_fail: command succeeded: $*"
return 1
- elif test $exit_code -gt 129 && test $exit_code -le 192; then
- echo >&2 "test_must_fail: died by signal: $*"
+ elif test_match_signal 13 $exit_code && list_contains "$_test_ok" sigpipe
+ then
+ return 0
+ elif test $exit_code -gt 129 && test $exit_code -le 192
+ then
+ echo >&2 "test_must_fail: died by signal $(($exit_code - 128)): $*"
return 1
- elif test $exit_code = 127; then
+ elif test $exit_code -eq 127
+ then
echo >&2 "test_must_fail: command not found: $*"
return 1
- elif test $exit_code = 126; then
+ elif test $exit_code -eq 126
+ then
echo >&2 "test_must_fail: valgrind error: $*"
return 1
fi
# because we want to notice if it fails due to segv.
test_might_fail () {
- "$@"
- exit_code=$?
- if test $exit_code -gt 129 && test $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
+ test_must_fail ok=success "$@"
}
# Similar to test_must_fail and test_might_fail, but check that a
test_cmp expect.rev actual.rev
}
-# Print a sequence of numbers or letters in increasing order. This is
-# similar to GNU seq(1), but the latter might not be available
-# everywhere (and does not do letters). It may be used like:
+# Print a sequence of integers in increasing order, either with
+# two arguments (start and end):
+#
+# test_seq 1 5 -- outputs 1 2 3 4 5 one line at a time
#
-# for i in $(test_seq 100)
-# do
-# for j in $(test_seq 10 20)
-# do
-# for k in $(test_seq a z)
-# do
-# echo $i-$j-$k
-# done
-# done
-# done
+# or with one argument (end), in which case it starts counting
+# from 1.
test_seq () {
case $# in
2) ;;
*) error "bug in the test script: not 1 or 2 parameters to test_seq" ;;
esac
- perl -le 'print for $ARGV[0]..$ARGV[1]' -- "$@"
+ test_seq_counter__=$1
+ while test "$test_seq_counter__" -le "$2"
+ do
+ echo "$test_seq_counter__"
+ test_seq_counter__=$(( $test_seq_counter__ + 1 ))
+ done
}
# This function can be used to schedule some commands to be run
# what went wrong.
test_when_finished () {
+ # We cannot detect when we are in a subshell in general, but by
+ # doing so on Bash is better than nothing (the test will
+ # silently pass on other shells).
+ test "${BASH_SUBSHELL-0}" = 0 ||
+ error "bug in test script: test_when_finished does nothing in a subshell"
test_cleanup="{ $*
} && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
}
eval "$1=\$$1\$line"
done
}
+
+# Like "env FOO=BAR some-program", but run inside a subshell, which means
+# it also works for shell functions (though those functions cannot impact
+# the environment outside of the test_env invocation).
+test_env () {
+ (
+ while test $# -gt 0
+ do
+ case "$1" in
+ *=*)
+ eval "${1%%=*}=\${1#*=}"
+ eval "export ${1%%=*}"
+ shift
+ ;;
+ *)
+ "$@"
+ exit
+ ;;
+ esac
+ done
+ )
+}
+
+# Returns true if the numeric exit code in "$2" represents the expected signal
+# in "$1". Signals should be given numerically.
+test_match_signal () {
+ if test "$2" = "$((128 + $1))"
+ then
+ # POSIX
+ return 0
+ elif test "$2" = "$((256 + $1))"
+ then
+ # ksh
+ return 0
+ fi
+ return 1
+}
+
+# Read up to "$1" bytes (or to EOF) from stdin and write them to stdout.
+test_copy_bytes () {
+ perl -e '
+ my $len = $ARGV[1];
+ while ($len > 0) {
+ my $s;
+ my $nread = sysread(STDIN, $s, $len);
+ die "cannot read: $!" unless defined($nread);
+ print $s;
+ $len -= $nread;
+ }
+ ' - "$1"
+}
+
+# run "$@" inside a non-git directory
+nongit () {
+ test -d non-repo ||
+ mkdir non-repo ||
+ return 1
+
+ (
+ GIT_CEILING_DIRECTORIES=$(pwd) &&
+ export GIT_CEILING_DIRECTORIES &&
+ cd non-repo &&
+ "$@"
+ )
+}