Re: [PATCH 03/11] test-lib: introduce 'test_atexit'
- From
SZEDER Gábor <szeder.dev@gmail.com>
- Date
- Mar 14, 2019, 17:40 UTC
- Message-ID
- <20190314174024.GH28939@szeder.dev>
- In-Reply-To
- <xmqqh8c6b0pv.fsf@gitster-ct.c.googlers.com>
On Thu, Mar 14, 2019 at 12:21:00PM +0900, Junio C Hamano wrote:
Show 22 quoted lines
> SZEDER Gábor <szeder.dev@gmail.com> 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?Right, it doesn't, as 'die' preserves the last seen exit code, and any exit codes from the atexit commands are ignored anyway.
Show 47 quoted lines
> > # 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 wayThat's right.
> but it even skips when there is no point doing the > test_eval_ of the "accumulated" scriptlet when it is empty.
But this is not, because $test_cleanup is initialized to this special value and it can never be empty, and indeed 'test_eval_' uses this condition:
if test -z "$immediate" || test $eval_ret = 0 ||
test -n "$expecting_failure" && test "$test_cleanup" != ":"and it never checks $test_cleanup's emptiness.
Show 11 quoted lines
> 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.
$test_atexit_cleanup is used the same way as $test_cleanup; it's initialized to the same special value and can never be empty, so there is no need to check for its emptiness either.
Show 16 quoted lines
> > + 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" || {