t/test-lib: teach --chain-lint to detect broken &&-chains in subshells
authorEric Sunshine <sunshine@sunshineco.com>
Wed, 11 Jul 2018 06:46:33 +0000 (02:46 -0400)
committerJunio C Hamano <gitster@pobox.com>
Tue, 17 Jul 2018 16:15:14 +0000 (09:15 -0700)
commit878f9883507492fe1734561ea8ddc3c4e9de3c1d
tree3d1b20bca3e348287f0c486e3816e090fe9bcd82
parent95005262844c470652538d53e11cd68d23a0fb62
t/test-lib: teach --chain-lint to detect broken &&-chains in subshells

The --chain-lint option detects broken &&-chains by forcing the test to
exit early (as the very first step) with a sentinel value. If that
sentinel is the test's overall exit code, then the &&-chain is intact;
if not, then the chain is broken. Unfortunately, this detection does not
extend to &&-chains within subshells even when the subshell itself is
properly linked into the outer &&-chain.

Address this shortcoming by feeding the body of the test to a
lightweight "linter" which can peer inside subshells and identify broken
&&-chains by pure textual inspection. Although the linter does not
actually parse shell scripts, it has enough knowledge of shell syntax to
reliably deal with formatting style variations (as evolved over the
years) and to avoid being fooled by non-shell content (such as inside
here-docs and multi-line strings). It recognizes modern subshell
formatting:

    statement1 &&
    (
        statement2 &&
        statement3
    ) &&
    statement4

as well as old-style:

    statement1 &&
    (statement2 &&
     statement3) &&
    statement4

Heuristics are employed to properly identify the extent of a subshell
formatted in the old-style since a number of legitimate constructs may
superficially appear to close the subshell even though they don't. For
example, it understands that neither "x=$(command)" nor "case $x in *)"
end a subshell, despite the ")" at the end of line.

Due to limitations of the tool used ('sed') and its inherent
line-by-line processing, only subshells one level deep are handled, as
well as one-liner subshells one level below that. Subshells deeper than
that or multi-line subshells at level two are passed through as-is, thus
&&-chains in their bodies are not checked.

Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
t/chainlint.sed [new file with mode: 0644]
t/test-lib.sh