Merge branch 'ps/test-set-e-clean'

The test suite harness and many individual test scripts have been
updated to work correctly when 'set -e' is in effect, which helps
detect misspelled test commands.

* ps/test-set-e-clean:
  t: detect errors outside of test cases
  t9902: fix use of `read` with `set -e`
  t6002: fix use of `expr` with `set -e`
  t1301: don't fail in case setfacl(1) doesn't exist or fails
  t0008: silence error in subshell when using `grep -v`
  t: prepare `test_when_finished ()`/`test_atexit()` for `set -e`
  t: prepare execution of potentially failing commands for `set -e`
  t: prepare conditional test execution for `set -e`
  t: prepare `git config --unset` calls for `set -e`
  t: prepare `stop_git_daemon ()` for `set -e`
  t: prepare `test_must_fail ()` for `set -e`
  t: prepare `test_match_signal ()` calls for `set -e`
main
Junio C Hamano 2026-05-11 10:05:54 +09:00
commit 6a5621b604
23 changed files with 108 additions and 68 deletions

View File

@ -10,7 +10,14 @@ export TEST_CONTRIB_TOO=yes
case "$jobname" in
linux-musl-meson)
MESONFLAGS="$MESONFLAGS -Drust=disabled"
export GIT_TEST_USE_SET_E=yes
;;
almalinux-*|debian-*|fedora-*|linux-*)
export GIT_TEST_USE_SET_E=yes
;;
esac

case "$jobname" in
fedora-breaking-changes-musl|linux-breaking-changes)
export WITH_BREAKING_CHANGES=YesPlease
MESONFLAGS="$MESONFLAGS -Dbreaking_changes=true"

View File

@ -85,14 +85,16 @@ stop_git_daemon() {

# kill git-daemon child of git
say >&3 "Stopping git daemon ..."

kill "$GIT_DAEMON_PID"
wait "$GIT_DAEMON_PID" >&3 2>&4
ret=$?
ret=0; wait "$GIT_DAEMON_PID" >&3 2>&4 || ret=$?

if ! test_match_signal 15 $ret
then
error "git daemon exited with status: $ret"
fi
kill "$(cat "$GIT_DAEMON_PIDFILE")" 2>/dev/null

kill "$(cat "$GIT_DAEMON_PIDFILE")" 2>/dev/null || :
GIT_DAEMON_PID=
rm -f git_daemon_output "$GIT_DAEMON_PIDFILE"
}

View File

@ -15,8 +15,7 @@ GIT_SVN_DIR=$GIT_DIR/svn/refs/remotes/git-svn
SVN_TREE=$GIT_SVN_DIR/svn-tree
test_set_port SVNSERVE_PORT

svn >/dev/null 2>&1
if test $? -ne 1
if ! svn help >/dev/null 2>&1
then
skip_all='skipping git svn tests, svn not found'
test_done
@ -27,13 +26,13 @@ export svnrepo
svnconf=$PWD/svnconf
export svnconf

x=0
perl -w -e "
use SVN::Core;
use SVN::Repos;
\$SVN::Core::VERSION gt '1.1.0' or exit(42);
system(qw/svnadmin create --fs-type fsfs/, \$ENV{svnrepo}) == 0 or exit(41);
" >&3 2>&4
x=$?
" >&3 2>&4 || x=$?
if test $x -ne 0
then
if test $x -eq 42; then

View File

@ -235,11 +235,10 @@ start_httpd() {

test_atexit stop_httpd

"$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
if ! "$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
-f "$TEST_PATH/apache.conf" $HTTPD_PARA \
-c "Listen 127.0.0.1:$LIB_HTTPD_PORT" -k start \
>&3 2>&4
if test $? -ne 0
then
cat "$HTTPD_ROOT_PATH"/error.log >&4 2>/dev/null
test_skip_or_die GIT_TEST_HTTPD "web server setup failed"

View File

@ -42,12 +42,12 @@ test_expect_success 'create blob' '
'

test_expect_success !MINGW 'a constipated git dies with SIGPIPE' '
OUT=$( ((large_git; echo $? 1>&3) | :) 3>&1 ) &&
OUT=$( ((large_git && echo 0 1>&3 || echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT"
'

test_expect_success !MINGW 'a constipated git dies with SIGPIPE even if parent ignores it' '
OUT=$( ((trap "" PIPE && large_git; echo $? 1>&3) | :) 3>&1 ) &&
OUT=$( ((trap "" PIPE && large_git && echo 0 1>&3 || echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT"
'


View File

@ -122,8 +122,8 @@ test_expect_success_multiple () {
fi
testname="$1" expect_all="$2" code="$3"

expect_verbose=$( echo "$expect_all" | grep -v '^:: ' )
expect=$( echo "$expect_verbose" | sed -e 's/.* //' )
expect_verbose=$(echo "$expect_all" | grep -v '^:: ' || :)
expect=$(echo "$expect_verbose" | sed -e 's/.* //')

test_expect_success $prereq "$testname${no_index_opt:+ with $no_index_opt}" '
expect "$expect" &&

View File

@ -12,7 +12,7 @@ TEST_CREATE_REPO_NO_TEMPLATE=1
. ./test-lib.sh

# Remove a default ACL from the test dir if possible.
setfacl -k . 2>/dev/null
setfacl -k . 2>/dev/null || :

# User must have read permissions to the repo -> failure on --shared=0400
test_expect_success 'shared = 0400 (faulty permission u-w)' '

View File

@ -260,7 +260,7 @@ test_expect_success 'choking "git rm" should not let it die with cruft (induce S

test_expect_success !MINGW 'choking "git rm" should not let it die with cruft (induce and check SIGPIPE)' '
choke_git_rm_setup &&
OUT=$( ((trap "" PIPE && git rm -n "some-file-*"; echo $? 1>&3) | :) 3>&1 ) &&
OUT=$( ((trap "" PIPE && git rm -n "some-file-*" && echo 0 1>&3 || echo $? 1>&3) | :) 3>&1 ) &&
test_match_signal 13 "$OUT" &&
test_path_is_missing .git/index.lock
'

View File

@ -28,7 +28,8 @@ check_encoding () {
8859)
grep "^encoding ISO8859-1" ;;
*)
grep "^encoding ISO8859-1"; test "$?" != 0 ;;
ret=0; grep "^encoding ISO8859-1" || ret=$?
test "$ret" != 0 ;;
esac || return 1
j=$i
i=$(($i+1))

View File

@ -17,7 +17,7 @@ f() {

t() {
use_config=
git config --unset diff.interHunkContext
git config --unset diff.interHunkContext || :

case $# in
4) hunks=$4; cmd="diff -U$3";;
@ -40,11 +40,13 @@ t() {
test $(git $cmd $file | grep '^@@ ' | wc -l) = $hunks
"

test -f $expected &&
if test -f $expected
then
test_expect_success "$label: check output" "
git $cmd $file | grep -v '^index ' >actual &&
test_cmp $expected actual
"
fi
}

cat <<EOF >expected.f1.0.1 || exit 1

View File

@ -503,8 +503,8 @@ test_expect_success LONG_IS_64BIT 'set up repository with huge blob' '
# would generate the whole 64GB).
test_expect_success LONG_IS_64BIT 'generate tar with huge size' '
{
git archive HEAD
echo $? >exit-code
{ ret=0 && git archive HEAD || ret=$?; } &&
echo "$ret" >exit-code
} | test_copy_bytes 4096 >huge.tar &&
echo 141 >expect &&
test_cmp expect exit-code

View File

@ -27,13 +27,16 @@ test_bisection_diff()
# Test if bisection size is close to half of list size within
# tolerance.
#
_bisect_err=$(expr $_list_size - $_bisection_size \* 2)
test "$_bisect_err" -lt 0 && _bisect_err=$(expr 0 - $_bisect_err)
_bisect_err=$(expr $_bisect_err / 2) ; # floor
_bisect_err=$(($_list_size - $_bisection_size * 2))
if test "$_bisect_err" -lt 0
then
_bisect_err=$((0 - $_bisect_err))
fi
_bisect_err=$(($_bisect_err / 2)) ; # floor

test_expect_success \
"bisection diff $_bisect_option $_head $* <= $_max_diff" \
'test $_bisect_err -le $_max_diff'
test_expect_success "bisection diff $_bisect_option $_head $* <= $_max_diff" '
test $_bisect_err -le $_max_diff
'
}

date >path0

View File

@ -198,7 +198,7 @@ test_expect_success !MINGW 'git submodule status --recursive propagates SIGPIPE'
(
cd repo &&
GIT_ALLOW_PROTOCOL=file git submodule add "$(pwd)"/../submodule &&
{ git submodule status --recursive 2>err; echo $?>status; } |
{ { ret=0 && git submodule status --recursive 2>err || ret=$?; } && echo $ret >status; } |
grep -q recursive-submodule-path-1 &&
test_must_be_empty err &&
test_match_signal 13 "$(cat status)"

View File

@ -220,7 +220,8 @@ check_dotx_symlink () {
)
'

test -n "$refuse_index" &&
if test -n "$refuse_index"
then
test_expect_success "refuse to load symlinked $name into index ($type)" '
test_must_fail \
git -C $dir \
@ -231,6 +232,7 @@ check_dotx_symlink () {
git -C $dir ls-files -s >out &&
test_must_be_empty out
'
fi
}

check_dotx_symlink gitmodules vanilla .gitmodules

View File

@ -773,8 +773,8 @@ test_expect_success TTY 'status --porcelain ignores color.status' '
'

# recover unconditionally from color tests
git config --unset color.status
git config --unset color.ui
git config --unset color.status || :
git config --unset color.ui || :

test_expect_success 'status --porcelain respects -b' '


View File

@ -68,8 +68,8 @@ test_expect_success 'authors-file overrode authors-prog' '
)
'

git --git-dir=x/.git config --unset svn.authorsfile
git --git-dir=x/.git config --unset svn.authorsprog
git --git-dir=x/.git config --unset svn.authorsfile || :
git --git-dir=x/.git config --unset svn.authorsprog || :

test_expect_success 'authors-prog imported user without email' '
svn mkdir -m gg --username gg-hermit "$svnrepo"/gg &&

View File

@ -11,8 +11,7 @@ if ! test_have_prereq PERL; then
test_done
fi

cvs >/dev/null 2>&1
if test $? -ne 1
if ! cvs version >/dev/null 2>&1
then
skip_all='skipping git cvsexportcommit tests, cvs not found'
test_done

View File

@ -17,12 +17,13 @@ if ! test_have_prereq PERL; then
skip_all='skipping git cvsserver tests, perl not available'
test_done
fi
cvs >/dev/null 2>&1
if test $? -ne 1

if ! cvs version >/dev/null 2>&1
then
skip_all='skipping git-cvsserver tests, cvs not found'
test_done
fi

perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
skip_all='skipping git-cvsserver tests, Perl SQLite interface unavailable'
test_done

View File

@ -60,12 +60,12 @@ check_status_options() {
return $stat
}

cvs >/dev/null 2>&1
if test $? -ne 1
if ! cvs version >/dev/null 2>&1
then
skip_all='skipping git-cvsserver tests, cvs not found'
test_done
fi

if ! test_have_prereq PERL
then
skip_all='skipping git-cvsserver tests, perl not available'

View File

@ -68,12 +68,12 @@ check_diff() {

#########

cvs >/dev/null 2>&1
if test $? -ne 1
if ! cvs version >/dev/null 2>&1
then
skip_all='skipping git-cvsserver tests, cvs not found'
test_done
fi

if ! test_have_prereq PERL
then
skip_all='skipping git-cvsserver tests, perl not available'

View File

@ -590,12 +590,10 @@ test_expect_success '__gitcomp - doesnt fail because of invalid variable name' '
__gitcomp "$invalid_variable_name"
'

read -r -d "" refs <<-\EOF
main
refs='main
maint
next
seen
EOF
seen'

test_expect_success '__gitcomp_nl - trailing space' '
test_gitcomp_nl "m" "$refs" <<-EOF

View File

@ -1195,8 +1195,9 @@ test_must_fail () {
echo >&7 "test_must_fail: only 'git' is allowed: $*"
return 1
fi
"$@" 2>&7
exit_code=$?

exit_code=0; "$@" 2>&7 || exit_code=$?

if test $exit_code -eq 0 && ! list_contains "$_test_ok" success
then
echo >&4 "test_must_fail: command succeeded: $*"
@ -1247,8 +1248,7 @@ test_might_fail () {
test_expect_code () {
want_code=$1
shift
"$@" 2>&7
exit_code=$?
exit_code=0; "$@" 2>&7 || exit_code=$?
if test $exit_code = $want_code
then
return 0
@ -1512,7 +1512,7 @@ test_when_finished () {
test "${BASH_SUBSHELL-0}" = 0 ||
BUG "test_when_finished does nothing in a subshell"
test_cleanup="{ $*
} && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
} || eval_ret=\$?; $test_cleanup"
}

# This function can be used to schedule some commands to be run
@ -1540,7 +1540,7 @@ test_atexit () {
test "${BASH_SUBSHELL-0}" = 0 ||
BUG "test_atexit does nothing in a subshell"
test_atexit_cleanup="{ $*
} && (exit \"\$eval_ret\"); eval_ret=\$?; $test_atexit_cleanup"
} || eval_ret=\$?; $test_atexit_cleanup"
}

# Deprecated wrapper for "git init", use "git init" directly instead

View File

@ -15,6 +15,31 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see https://www.gnu.org/licenses/ .

# Enable the use of errexit so that any unexpected failures will cause us to
# abort tests, even when outside of a specific test case.
#
# Note that we only enable this on Bash 5 and newer, or when explicitly
# requested by the user via `GIT_TEST_USE_SET_E=true`. This ib secause `set -e`
# has wildly different behaviour across shells. The list of default-enabled
# shells may be extended going forward.
if test -z "$GIT_TEST_USE_SET_E" && test "${BASH_VERSINFO:=0}" -ge 5
then
GIT_TEST_USE_SET_E=true
fi

# We cannot use `test-tool env-helper` here, as it's not yet available.
case "${GIT_TEST_USE_SET_E:-false}" in
1|on|true|yes)
set -e
;;
0|off|false|no)
;;
*)
echo "GIT_TEST_USE_SET_E requires a boolean" >&2
exit 1
;;
esac

# 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"
@ -143,8 +168,8 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
################################################################
# It appears that people try to run tests without building...
GIT_BINARY="${GIT_TEST_INSTALLED:-$GIT_BUILD_DIR}/git$X"
"$GIT_BINARY" >/dev/null
if test $? != 1

if ! "$GIT_BINARY" version >/dev/null
then
if test -n "$GIT_TEST_INSTALLED"
then
@ -454,8 +479,10 @@ then
# from any previous runs.
>"$GIT_TEST_TEE_OUTPUT_FILE"

(GIT_TEST_TEE_STARTED=done ${TEST_SHELL_PATH} "$0" "$@" 2>&1;
echo $? >"$TEST_RESULTS_BASE.exit") | tee -a "$GIT_TEST_TEE_OUTPUT_FILE"
(
ret=0 && GIT_TEST_TEE_STARTED=done ${TEST_SHELL_PATH} "$0" "$@" 2>&1 || ret=$?
echo "$ret" >"$TEST_RESULTS_BASE.exit"
) | tee -a "$GIT_TEST_TEE_OUTPUT_FILE"
test "$(cat "$TEST_RESULTS_BASE.exit")" = 0
exit
fi