SZEDER Gábor <szeder.dev@xxxxxxxxx> writes: > +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" > +} This chaining is modelled after how $test_cleaup is built and maintained by test_when_finished. Use of eval_ret makes sense in that original context as eval_ret _is_ used to keep track of the result of 'test_eval_ "$1"' in test_run_ that executed the body of a single test_expect_$something, and $test_cleanup would want to keep the resulting status from that body when clean-up succeeds (or otherwise, make that failure to clean-up visible as $eval_ret). But does it make sense in the context of the whole test script to try maintaining $eval_ret? > # Most tests can use the created repository, but some may need to create more. > # Usage: test_create_repo <directory> > test_create_repo () { > diff --git a/t/test-lib.sh b/t/test-lib.sh > index db3875d1e4..b35881696f 100644 > --- a/t/test-lib.sh > +++ b/t/test-lib.sh > @@ -620,6 +620,10 @@ test_external_has_tap=0 > > 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 > @@ -1045,9 +1049,28 @@ write_junit_xml_testcase () { > 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. We are guaranteed to still have the trash directory when we are run in the exit handler after getting interrupted or test_failure_() exits under the "-i" option, and when test_done() calls us. What will cause us trouble is the exit handler at the end of a successful run after test_done() finishes. At that point, test_done would have already cleared the trash directory, so we may not have enough state to allow us to clean-up at exit. Clearing the exit trap in test_done after it calls us might be an alternative, but I think it is equivalent to clearing the test_atexit_cleanup variable, and it is cleaner, so I think I agree with the approach this patch uses. > + # 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 I think test_when_finished uses a special value in $test_cleanup in a similar way but it even skips when there is no point doing the test_eval_ of the "accumulated" scriptlet when it is empty. Shouldn't we be doing the same thing, i.e. if test -z "$test_atexit_cleanup" then return 0 fi ... do the heavy lifting ... test_atexit_cleanup= That will make the handler truly a no-op when there is no atexit defined. > + 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" || {