Skip to content

Commit 6d932e9

Browse files
sunshinecogitster
authored andcommitted
chainlint.pl: add parser to validate tests
Continue fleshing out chainlint.pl by adding TestParser, a parser with special knowledge about how Git tests should be written; for instance, it knows that commands within a test body should be chained together with `&&`. An upcoming parser which plucks test definitions from test scripts will invoke TestParser for each test body it encounters. Signed-off-by: Eric Sunshine <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 6594554 commit 6d932e9

File tree

1 file changed

+46
-0
lines changed

1 file changed

+46
-0
lines changed

t/chainlint.pl

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,52 @@ sub parse {
441441
return @tokens;
442442
}
443443

444+
# TestParser is a subclass of ShellParser which, beyond parsing shell script
445+
# code, is also imbued with semantic knowledge of test construction, and checks
446+
# tests for common problems (such as broken &&-chains) which might hide bugs in
447+
# the tests themselves or in behaviors being exercised by the tests. As such,
448+
# TestParser is only called upon to parse test bodies, not the top-level
449+
# scripts in which the tests are defined.
450+
package TestParser;
451+
452+
use base 'ShellParser';
453+
454+
sub find_non_nl {
455+
my $tokens = shift @_;
456+
my $n = shift @_;
457+
$n = $#$tokens if !defined($n);
458+
$n-- while $n >= 0 && $$tokens[$n] eq "\n";
459+
return $n;
460+
}
461+
462+
sub ends_with {
463+
my ($tokens, $needles) = @_;
464+
my $n = find_non_nl($tokens);
465+
for my $needle (reverse(@$needles)) {
466+
return undef if $n < 0;
467+
$n = find_non_nl($tokens, $n), next if $needle eq "\n";
468+
return undef if $$tokens[$n] !~ $needle;
469+
$n--;
470+
}
471+
return 1;
472+
}
473+
474+
sub accumulate {
475+
my ($self, $tokens, $cmd) = @_;
476+
goto DONE unless @$tokens;
477+
goto DONE if @$cmd == 1 && $$cmd[0] eq "\n";
478+
479+
# did previous command end with "&&", "||", "|"?
480+
goto DONE if ends_with($tokens, [qr/^(?:&&|\|\||\|)$/]);
481+
482+
# flag missing "&&" at end of previous command
483+
my $n = find_non_nl($tokens);
484+
splice(@$tokens, $n + 1, 0, '?!AMP?!') unless $n < 0;
485+
486+
DONE:
487+
$self->SUPER::accumulate($tokens, $cmd);
488+
}
489+
444490
package ScriptParser;
445491

446492
sub new {

0 commit comments

Comments
 (0)