...
'
+ - test_atexit <script>
+
+ Prepend <script> to a list of commands to run unconditionally to
+ clean up before the test script exits, e.g. to stop a daemon:
+
+ test_expect_success 'test git daemon' '
+ git daemon &
+ daemon_pid=$! &&
+ test_atexit 'kill $daemon_pid' &&
+ hello world
+ '
+
+ The commands will be executed before the trash directory is removed,
+ i.e. the atexit commands will still be able to access any pidfiles or
+ socket files.
+
+ Note that these commands will be run even when a test script run
+ with '--immediate' fails. Be careful with your atexit commands to
+ minimize any changes to the failed state.
+
- test_write_lines <lines>
Write <lines> on standard output, one line per argument.
test_cmp expect actual
'
-stop_git_daemon
test_done
#
# test_expect_success ...
#
-# stop_git_daemon
# test_done
test_tristate GIT_TEST_GIT_DAEMON
test_set_port LIB_GIT_DAEMON_PORT
GIT_DAEMON_PID=
+GIT_DAEMON_PIDFILE="$PWD"/daemon.pid
GIT_DAEMON_DOCUMENT_ROOT_PATH="$PWD"/repo
GIT_DAEMON_HOST_PORT=127.0.0.1:$LIB_GIT_DAEMON_PORT
GIT_DAEMON_URL=git://$GIT_DAEMON_HOST_PORT
+registered_stop_git_daemon_atexit_handler=
start_git_daemon() {
if test -n "$GIT_DAEMON_PID"
then
mkdir -p "$GIT_DAEMON_DOCUMENT_ROOT_PATH"
- trap 'code=$?; stop_git_daemon; (exit $code); die' EXIT
+ # One of the test scripts stops and then re-starts 'git daemon'.
+ # Don't register and then run the same atexit handlers several times.
+ if test -z "$registered_stop_git_daemon_atexit_handler"
+ then
+ test_atexit 'stop_git_daemon'
+ registered_stop_git_daemon_atexit_handler=AlreadyDone
+ fi
say >&3 "Starting git daemon ..."
mkfifo git_daemon_output
${LIB_GIT_DAEMON_COMMAND:-git daemon} \
--listen=127.0.0.1 --port="$LIB_GIT_DAEMON_PORT" \
- --reuseaddr --verbose \
+ --reuseaddr --verbose --pid-file="$GIT_DAEMON_PIDFILE" \
--base-path="$GIT_DAEMON_DOCUMENT_ROOT_PATH" \
"$@" "$GIT_DAEMON_DOCUMENT_ROOT_PATH" \
>&3 2>git_daemon_output &
then
kill "$GIT_DAEMON_PID"
wait "$GIT_DAEMON_PID"
- trap 'die' EXIT
+ unset GIT_DAEMON_PID
test_skip_or_die $GIT_TEST_GIT_DAEMON \
"git daemon failed to start"
fi
return
fi
- trap 'die' EXIT
-
# kill git-daemon child of git
say >&3 "Stopping git daemon ..."
kill "$GIT_DAEMON_PID"
then
error "git daemon exited with status: $ret"
fi
+ kill "$(cat "$GIT_DAEMON_PIDFILE")" 2>/dev/null
GIT_DAEMON_PID=
- rm -f git_daemon_output
+ rm -f git_daemon_output "$GIT_DAEMON_PIDFILE"
}
# A stripped-down version of a netcat client, that connects to a "host:port"
echo "$path"
}
-# On Solaris the 'date +%s' function is not supported and therefore we
-# need this replacement.
-# Attention: This function is not safe again against time offset updates
-# at runtime (e.g. via NTP). The 'clock_gettime(CLOCK_MONOTONIC)'
-# function could fix that but it is not in Python until 3.3.
-time_in_seconds () {
- (cd / && "$PYTHON_PATH" -c 'import time; print(int(time.time()))')
-}
-
test_set_port P4DPORT
P4PORT=localhost:$P4DPORT
git="$TRASH_DIRECTORY/git"
pidfile="$TRASH_DIRECTORY/p4d.pid"
-# Sometimes "prove" seems to hang on exit because p4d is still running
-cleanup () {
- if test -f "$pidfile"
- then
- kill -9 $(cat "$pidfile") 2>/dev/null && exit 255
- fi
+stop_p4d_and_watchdog () {
+ kill -9 $p4d_pid $watchdog_pid
}
-trap cleanup EXIT
# git p4 submit generates a temp file, which will
# not get cleaned up if the submission fails. Don't
TMPDIR="$TRASH_DIRECTORY"
export TMPDIR
+registered_stop_p4d_atexit_handler=
start_p4d () {
+ # One of the test scripts stops and then re-starts p4d.
+ # Don't register and then run the same atexit handlers several times.
+ if test -z "$registered_stop_p4d_atexit_handler"
+ then
+ test_atexit 'stop_p4d_and_watchdog'
+ registered_stop_p4d_atexit_handler=AlreadyDone
+ fi
+
mkdir -p "$db" "$cli" "$git" &&
rm -f "$pidfile" &&
(
echo $! >"$pidfile"
}
) &&
+ p4d_pid=$(cat "$pidfile")
# This gives p4d a long time to start up, as it can be
# quite slow depending on the machine. Set this environment
# an automated test setup. If the p4d process dies, that
# will be caught with the "kill -0" check below.
i=${P4D_START_PATIENCE:-300}
- pid=$(cat "$pidfile")
- timeout=$(($(time_in_seconds) + $P4D_TIMEOUT))
+ nr_tries_left=$P4D_TIMEOUT
while true
do
- if test $(time_in_seconds) -gt $timeout
+ if test $nr_tries_left -eq 0
then
- kill -9 $pid
+ kill -9 $p4d_pid
exit 1
fi
sleep 1
- done &
+ nr_tries_left=$(($nr_tries_left - 1))
+ done 2>/dev/null 4>&2 &
watchdog_pid=$!
ready=
break
fi
# fail if p4d died
- kill -0 $pid 2>/dev/null || break
+ kill -0 $p4d_pid 2>/dev/null || break
echo waiting for p4d to start
sleep 1
i=$(( $i - 1 ))
}
retry_until_success () {
- timeout=$(($(time_in_seconds) + $RETRY_TIMEOUT))
- until "$@" 2>/dev/null || test $(time_in_seconds) -gt $timeout
- do
- sleep 1
- done
-}
-
-retry_until_fail () {
- timeout=$(($(time_in_seconds) + $RETRY_TIMEOUT))
- until ! "$@" 2>/dev/null || test $(time_in_seconds) -gt $timeout
+ nr_tries_left=$RETRY_TIMEOUT
+ until "$@" 2>/dev/null || test $nr_tries_left -eq 0
do
sleep 1
+ nr_tries_left=$(($nr_tries_left - 1))
done
}
-kill_p4d () {
- pid=$(cat "$pidfile")
- retry_until_fail kill $pid
- retry_until_fail kill -9 $pid
- # complain if it would not die
- test_must_fail kill $pid >/dev/null 2>&1 &&
- rm -rf "$db" "$cli" "$pidfile" &&
- retry_until_fail kill -9 $watchdog_pid
+stop_and_cleanup_p4d () {
+ kill -9 $p4d_pid $watchdog_pid
+ wait $p4d_pid
+ rm -rf "$db" "$cli" "$pidfile"
}
cleanup_git () {
LIB_HTTPD_SVN="$loc"
start_httpd
;;
- *)
- stop_httpd () {
- : noop
- }
- ;;
esac
}
#
# test_expect_success ...
#
-# stop_httpd
# test_done
#
# Can be configured using the following variables.
start_httpd() {
prepare_httpd >&3 2>&4
- trap 'code=$?; stop_httpd; (exit $code); die' EXIT
+ test_atexit stop_httpd
"$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
-f "$TEST_PATH/apache.conf" $HTTPD_PARA \
>&3 2>&4
if test $? -ne 0
then
- trap 'die' EXIT
cat "$HTTPD_ROOT_PATH"/error.log >&4 2>/dev/null
test_skip_or_die $GIT_TEST_HTTPD "web server setup failed"
fi
}
stop_httpd() {
- trap 'die' EXIT
-
"$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
-f "$TEST_PATH/apache.conf" $HTTPD_PARA -k stop
}
EOF
"
+test_expect_success 'test_atexit is run' "
+ test_must_fail run_sub_test_lib_test \
+ atexit-cleanup 'Run atexit commands' -i <<-\\EOF &&
+ test_expect_success 'tests clean up even after a failure' '
+ > ../../clean-atexit &&
+ test_atexit rm ../../clean-atexit &&
+ > ../../also-clean-atexit &&
+ test_atexit rm ../../also-clean-atexit &&
+ > ../../dont-clean-atexit &&
+ (exit 1)
+ '
+ test_done
+ EOF
+ test_path_is_file dont-clean-atexit &&
+ test_path_is_missing clean-atexit &&
+ test_path_is_missing also-clean-atexit
+"
+
test_expect_success 'test_oid setup' '
test_oid_init
'
}
# don't leave a stale daemon running
-trap 'code=$?; git credential-cache exit; (exit $code); die' EXIT
+test_atexit 'git credential-cache exit'
# test that the daemon works with no special setup
helper_test cache
helper_test_timeout cache --timeout=1
-# we can't rely on our "trap" above working after test_done,
-# as test_done will delete the trash directory containing
-# our socket, leaving us with no way to access the daemon.
-git credential-cache exit
-
test_done
git verify-pack --verbose "$IDX" | grep "$HASH"
'
-stop_httpd
-
test_done
fetch_filter_blob_limit_zero "$HTTPD_DOCUMENT_ROOT_PATH/server" "$HTTPD_URL/smart/server"
'
-stop_httpd
-
-
test_done
check_negotiation_tip
'
-stop_httpd
-
test_done
git -C client fsck
'
-stop_httpd
-
test_done
)
'
-stop_httpd
test_done
test_cmp expect actual
'
-stop_httpd
-
test_done
test_i18ngrep ! "^hint: " decoded
'
-stop_httpd
test_done
)
'
-stop_httpd
test_done
test_cmp expect "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git/hooks/pre-receive.push_options
'
-stop_httpd
-
test_done
git -c http.followredirects=true clone "$HTTPD_URL/dumb/alt-child.git"
'
-stop_httpd
test_done
grep "server-side error" actual
'
-stop_httpd
test_done
check_access_log exp
'
-stop_httpd
test_done
test_cmp expect actual
'
-stop_git_daemon
test_done
grep "< HTTP/1.1 500 Intentional Breakage" curl_log
'
-stop_httpd
-
test_done
partial_clone "$HTTPD_DOCUMENT_ROOT_PATH/server" "$HTTPD_URL/smart/server"
'
-stop_httpd
-
test_done
! test -e "$HTTPD_ROOT_PATH/one-time-sed"
'
-stop_httpd
-
test_done
grep "git< version 1" log
'
-stop_httpd
-
test_done
test_i18ngrep "expected no other sections to be sent after no .ready." err
'
-stop_httpd
-
test_done
test_i18ngrep "fatal: remote error: unknown ref refs/heads/raster" err
'
-stop_httpd
-
REPO="$(pwd)/repo"
LOCAL_PRISTINE="$(pwd)/local_pristine"
clone "$HTTPD_URL/smart-redir-perm/repo.git" redir.git
'
-stop_httpd
test_done
git svn dcommit
'
-stop_httpd
-
test_done
)
'
-stop_httpd
-
test_done
)
'
-stop_httpd
-
test_done
( cd g && git rev-parse --symbolic --verify HEAD )
'
-stop_httpd
-
test_done
)
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
'
test_expect_success 'restart p4d' '
- kill_p4d &&
+ stop_and_cleanup_p4d &&
start_p4d
'
'
test_expect_success 'restart p4d' '
- kill_p4d &&
+ stop_and_cleanup_p4d &&
start_p4d
'
)
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
)
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
)
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
)
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
)
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
test_path_is_file "$git"/cli_file2.t
'
-
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
)
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
)
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
git_verify "cdir 1/file11" "cdir 1/file12"
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
)
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
(
cd "$cli" &&
p4 sync ... &&
- !(p4 labels | grep GIT_TAG_ON_A_BRANCH)
+ ! p4 labels | grep GIT_TAG_ON_A_BRANCH
)
'
)
'
-
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
)
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
)
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
)
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
)
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
)
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
)
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
test_line_count \> 10 log
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
test_must_fail git p4 clone //depot/uc/...
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
)
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
)
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
)
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
)
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
)
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
git p4 clone --dest="$git" //depot
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
)
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
)
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
)
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
)
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
)
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
test_done
test_path_is_file file_to_shelve
)
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
test_done
)
'
-test_expect_success 'kill p4d' '
- kill_p4d
-'
-
-
test_done
} && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
}
+# This function can be used to schedule some commands to be run
+# unconditionally at the end of the test script, e.g. to stop a daemon:
+#
+# test_expect_success 'test git daemon' '
+# git daemon &
+# daemon_pid=$! &&
+# test_atexit 'kill $daemon_pid' &&
+# hello world
+# '
+#
+# The commands will be executed before the trash directory is removed,
+# i.e. the atexit commands will still be able to access any pidfiles or
+# socket files.
+#
+# Note that these commands will be run even when a test script run
+# with '--immediate' fails. Be careful with your atexit commands to
+# minimize any changes to the failed state.
+
+test_atexit () {
+ # 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_atexit does nothing in a subshell"
+ test_atexit_cleanup="{ $*
+ } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_atexit_cleanup"
+}
+
# Most tests can use the created repository, but some may need to create more.
# Usage: test_create_repo <directory>
test_create_repo () {
die () {
code=$?
+ # This is responsible for running the atexit commands even when a
+ # test script run with '--immediate' fails, or when the user hits
+ # ctrl-C, i.e. when 'test_done' is not invoked at all.
+ test_atexit_handler || code=$?
if test -n "$GIT_EXIT_OK"
then
exit $code
GIT_EXIT_OK=
trap 'die' EXIT
-trap 'exit $?' INT TERM HUP
+# Disable '-x' tracing, because with some shells, notably dash, it
+# prevents running the cleanup commands when a test script run with
+# '--verbose-log -x' is interrupted.
+trap '{ code=$?; set +x; } 2>/dev/null; exit $code' INT TERM HUP
# The user-facing functions are loaded from a separate file so that
# test_perf subshells can have them too
junit_have_testcase=t
}
+test_atexit_cleanup=:
+test_atexit_handler () {
+ # In a succeeding test script 'test_atexit_handler' is invoked
+ # twice: first from 'test_done', then from 'die' in the trap on
+ # EXIT.
+ # This condition and resetting 'test_atexit_cleanup' below makes
+ # sure that the registered cleanup commands are run only once.
+ test : != "$test_atexit_cleanup" || return 0
+
+ setup_malloc_check
+ test_eval_ "$test_atexit_cleanup"
+ test_atexit_cleanup=:
+ teardown_malloc_check
+}
+
test_done () {
GIT_EXIT_OK=t
+ # Run the atexit commands _before_ the trash directory is
+ # removed, so the commands can access pidfiles and socket files.
+ test_atexit_handler
+
if test -n "$write_junit_xml" && test -n "$junit_xml_path"
then
test -n "$junit_have_testcase" || {