Merge branch 'es/chainlint-output'
Teach chainlint.pl to annotate the original test definition instead of the token stream. * es/chainlint-output: chainlint: annotate original test definition rather than token stream chainlint: latch start/end position of each token chainlint: tighten accuracy when consuming input stream chainlint: add explanatory comments
This commit is contained in:
107
t/chainlint.pl
107
t/chainlint.pl
@ -75,7 +75,9 @@ sub scan_heredoc_tag {
|
|||||||
my $self = shift @_;
|
my $self = shift @_;
|
||||||
${$self->{buff}} =~ /\G(-?)/gc;
|
${$self->{buff}} =~ /\G(-?)/gc;
|
||||||
my $indented = $1;
|
my $indented = $1;
|
||||||
my $tag = $self->scan_token();
|
my $token = $self->scan_token();
|
||||||
|
return "<<$indented" unless $token;
|
||||||
|
my $tag = $token->[0];
|
||||||
$tag =~ s/['"\\]//g;
|
$tag =~ s/['"\\]//g;
|
||||||
push(@{$self->{heretags}}, $indented ? "\t$tag" : "$tag");
|
push(@{$self->{heretags}}, $indented ? "\t$tag" : "$tag");
|
||||||
return "<<$indented$tag";
|
return "<<$indented$tag";
|
||||||
@ -149,7 +151,7 @@ sub scan_dollar {
|
|||||||
my $self = shift @_;
|
my $self = shift @_;
|
||||||
my $b = $self->{buff};
|
my $b = $self->{buff};
|
||||||
return $self->scan_balanced('(', ')') if $$b =~ /\G\((?=\()/gc; # $((...))
|
return $self->scan_balanced('(', ')') if $$b =~ /\G\((?=\()/gc; # $((...))
|
||||||
return '(' . join(' ', $self->scan_subst()) . ')' if $$b =~ /\G\(/gc; # $(...)
|
return '(' . join(' ', map {$_->[0]} $self->scan_subst()) . ')' if $$b =~ /\G\(/gc; # $(...)
|
||||||
return $self->scan_balanced('{', '}') if $$b =~ /\G\{/gc; # ${...}
|
return $self->scan_balanced('{', '}') if $$b =~ /\G\{/gc; # ${...}
|
||||||
return $1 if $$b =~ /\G(\w+)/gc; # $var
|
return $1 if $$b =~ /\G(\w+)/gc; # $var
|
||||||
return $1 if $$b =~ /\G([@*#?$!0-9-])/gc; # $*, $1, $$, etc.
|
return $1 if $$b =~ /\G([@*#?$!0-9-])/gc; # $*, $1, $$, etc.
|
||||||
@ -170,16 +172,18 @@ sub scan_token {
|
|||||||
my $self = shift @_;
|
my $self = shift @_;
|
||||||
my $b = $self->{buff};
|
my $b = $self->{buff};
|
||||||
my $token = '';
|
my $token = '';
|
||||||
|
my $start;
|
||||||
RESTART:
|
RESTART:
|
||||||
$$b =~ /\G[ \t]+/gc; # skip whitespace (but not newline)
|
$$b =~ /\G[ \t]+/gc; # skip whitespace (but not newline)
|
||||||
return "\n" if $$b =~ /\G#[^\n]*(?:\n|\z)/gc; # comment
|
$start = pos($$b) || 0;
|
||||||
|
return ["\n", $start, pos($$b)] if $$b =~ /\G#[^\n]*(?:\n|\z)/gc; # comment
|
||||||
while (1) {
|
while (1) {
|
||||||
# slurp up non-special characters
|
# slurp up non-special characters
|
||||||
$token .= $1 if $$b =~ /\G([^\\;&|<>(){}'"\$\s]+)/gc;
|
$token .= $1 if $$b =~ /\G([^\\;&|<>(){}'"\$\s]+)/gc;
|
||||||
# handle special characters
|
# handle special characters
|
||||||
last unless $$b =~ /\G(.)/sgc;
|
last unless $$b =~ /\G(.)/sgc;
|
||||||
my $c = $1;
|
my $c = $1;
|
||||||
last if $c =~ /^[ \t]$/; # whitespace ends token
|
pos($$b)--, last if $c =~ /^[ \t]$/; # whitespace ends token
|
||||||
pos($$b)--, last if length($token) && $c =~ /^[;&|<>(){}\n]$/;
|
pos($$b)--, last if length($token) && $c =~ /^[;&|<>(){}\n]$/;
|
||||||
$token .= $self->scan_sqstring(), next if $c eq "'";
|
$token .= $self->scan_sqstring(), next if $c eq "'";
|
||||||
$token .= $self->scan_dqstring(), next if $c eq '"';
|
$token .= $self->scan_dqstring(), next if $c eq '"';
|
||||||
@ -197,7 +201,7 @@ RESTART:
|
|||||||
}
|
}
|
||||||
die("internal error scanning character '$c'\n");
|
die("internal error scanning character '$c'\n");
|
||||||
}
|
}
|
||||||
return length($token) ? $token : undef;
|
return length($token) ? [$token, $start, pos($$b)] : undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
# ShellParser parses POSIX shell scripts (with minor extensions for Bash). It
|
# ShellParser parses POSIX shell scripts (with minor extensions for Bash). It
|
||||||
@ -239,14 +243,14 @@ sub stop_at {
|
|||||||
my ($self, $token) = @_;
|
my ($self, $token) = @_;
|
||||||
return 1 unless defined($token);
|
return 1 unless defined($token);
|
||||||
my $stop = ${$self->{stop}}[-1] if @{$self->{stop}};
|
my $stop = ${$self->{stop}}[-1] if @{$self->{stop}};
|
||||||
return defined($stop) && $token =~ $stop;
|
return defined($stop) && $token->[0] =~ $stop;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub expect {
|
sub expect {
|
||||||
my ($self, $expect) = @_;
|
my ($self, $expect) = @_;
|
||||||
my $token = $self->next_token();
|
my $token = $self->next_token();
|
||||||
return $token if defined($token) && $token eq $expect;
|
return $token if defined($token) && $token->[0] eq $expect;
|
||||||
push(@{$self->{output}}, "?!ERR?! expected '$expect' but found '" . (defined($token) ? $token : "<end-of-input>") . "'\n");
|
push(@{$self->{output}}, "?!ERR?! expected '$expect' but found '" . (defined($token) ? $token->[0] : "<end-of-input>") . "'\n");
|
||||||
$self->untoken($token) if defined($token);
|
$self->untoken($token) if defined($token);
|
||||||
return ();
|
return ();
|
||||||
}
|
}
|
||||||
@ -255,7 +259,7 @@ sub optional_newlines {
|
|||||||
my $self = shift @_;
|
my $self = shift @_;
|
||||||
my @tokens;
|
my @tokens;
|
||||||
while (my $token = $self->peek()) {
|
while (my $token = $self->peek()) {
|
||||||
last unless $token eq "\n";
|
last unless $token->[0] eq "\n";
|
||||||
push(@tokens, $self->next_token());
|
push(@tokens, $self->next_token());
|
||||||
}
|
}
|
||||||
return @tokens;
|
return @tokens;
|
||||||
@ -278,7 +282,7 @@ sub parse_case_pattern {
|
|||||||
my @tokens;
|
my @tokens;
|
||||||
while (defined(my $token = $self->next_token())) {
|
while (defined(my $token = $self->next_token())) {
|
||||||
push(@tokens, $token);
|
push(@tokens, $token);
|
||||||
last if $token eq ')';
|
last if $token->[0] eq ')';
|
||||||
}
|
}
|
||||||
return @tokens;
|
return @tokens;
|
||||||
}
|
}
|
||||||
@ -293,13 +297,13 @@ sub parse_case {
|
|||||||
$self->optional_newlines());
|
$self->optional_newlines());
|
||||||
while (1) {
|
while (1) {
|
||||||
my $token = $self->peek();
|
my $token = $self->peek();
|
||||||
last unless defined($token) && $token ne 'esac';
|
last unless defined($token) && $token->[0] ne 'esac';
|
||||||
push(@tokens,
|
push(@tokens,
|
||||||
$self->parse_case_pattern(),
|
$self->parse_case_pattern(),
|
||||||
$self->optional_newlines(),
|
$self->optional_newlines(),
|
||||||
$self->parse(qr/^(?:;;|esac)$/)); # item body
|
$self->parse(qr/^(?:;;|esac)$/)); # item body
|
||||||
$token = $self->peek();
|
$token = $self->peek();
|
||||||
last unless defined($token) && $token ne 'esac';
|
last unless defined($token) && $token->[0] ne 'esac';
|
||||||
push(@tokens,
|
push(@tokens,
|
||||||
$self->expect(';;'),
|
$self->expect(';;'),
|
||||||
$self->optional_newlines());
|
$self->optional_newlines());
|
||||||
@ -315,7 +319,7 @@ sub parse_for {
|
|||||||
$self->next_token(), # variable
|
$self->next_token(), # variable
|
||||||
$self->optional_newlines());
|
$self->optional_newlines());
|
||||||
my $token = $self->peek();
|
my $token = $self->peek();
|
||||||
if (defined($token) && $token eq 'in') {
|
if (defined($token) && $token->[0] eq 'in') {
|
||||||
push(@tokens,
|
push(@tokens,
|
||||||
$self->expect('in'),
|
$self->expect('in'),
|
||||||
$self->optional_newlines());
|
$self->optional_newlines());
|
||||||
@ -339,11 +343,11 @@ sub parse_if {
|
|||||||
$self->optional_newlines(),
|
$self->optional_newlines(),
|
||||||
$self->parse(qr/^(?:elif|else|fi)$/)); # if/elif body
|
$self->parse(qr/^(?:elif|else|fi)$/)); # if/elif body
|
||||||
my $token = $self->peek();
|
my $token = $self->peek();
|
||||||
last unless defined($token) && $token eq 'elif';
|
last unless defined($token) && $token->[0] eq 'elif';
|
||||||
push(@tokens, $self->expect('elif'));
|
push(@tokens, $self->expect('elif'));
|
||||||
}
|
}
|
||||||
my $token = $self->peek();
|
my $token = $self->peek();
|
||||||
if (defined($token) && $token eq 'else') {
|
if (defined($token) && $token->[0] eq 'else') {
|
||||||
push(@tokens,
|
push(@tokens,
|
||||||
$self->expect('else'),
|
$self->expect('else'),
|
||||||
$self->optional_newlines(),
|
$self->optional_newlines(),
|
||||||
@ -380,7 +384,7 @@ sub parse_bash_array_assignment {
|
|||||||
my @tokens = $self->expect('(');
|
my @tokens = $self->expect('(');
|
||||||
while (defined(my $token = $self->next_token())) {
|
while (defined(my $token = $self->next_token())) {
|
||||||
push(@tokens, $token);
|
push(@tokens, $token);
|
||||||
last if $token eq ')';
|
last if $token->[0] eq ')';
|
||||||
}
|
}
|
||||||
return @tokens;
|
return @tokens;
|
||||||
}
|
}
|
||||||
@ -398,29 +402,31 @@ sub parse_cmd {
|
|||||||
my $self = shift @_;
|
my $self = shift @_;
|
||||||
my $cmd = $self->next_token();
|
my $cmd = $self->next_token();
|
||||||
return () unless defined($cmd);
|
return () unless defined($cmd);
|
||||||
return $cmd if $cmd eq "\n";
|
return $cmd if $cmd->[0] eq "\n";
|
||||||
|
|
||||||
my $token;
|
my $token;
|
||||||
my @tokens = $cmd;
|
my @tokens = $cmd;
|
||||||
if ($cmd eq '!') {
|
if ($cmd->[0] eq '!') {
|
||||||
push(@tokens, $self->parse_cmd());
|
push(@tokens, $self->parse_cmd());
|
||||||
return @tokens;
|
return @tokens;
|
||||||
} elsif (my $f = $compound{$cmd}) {
|
} elsif (my $f = $compound{$cmd->[0]}) {
|
||||||
push(@tokens, $self->$f());
|
push(@tokens, $self->$f());
|
||||||
} elsif (defined($token = $self->peek()) && $token eq '(') {
|
} elsif (defined($token = $self->peek()) && $token->[0] eq '(') {
|
||||||
if ($cmd !~ /\w=$/) {
|
if ($cmd->[0] !~ /\w=$/) {
|
||||||
push(@tokens, $self->parse_func());
|
push(@tokens, $self->parse_func());
|
||||||
return @tokens;
|
return @tokens;
|
||||||
}
|
}
|
||||||
$tokens[-1] .= join(' ', $self->parse_bash_array_assignment());
|
my @array = $self->parse_bash_array_assignment();
|
||||||
|
$tokens[-1]->[0] .= join(' ', map {$_->[0]} @array);
|
||||||
|
$tokens[-1]->[2] = $array[$#array][2] if @array;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (defined(my $token = $self->next_token())) {
|
while (defined(my $token = $self->next_token())) {
|
||||||
$self->untoken($token), last if $self->stop_at($token);
|
$self->untoken($token), last if $self->stop_at($token);
|
||||||
push(@tokens, $token);
|
push(@tokens, $token);
|
||||||
last if $token =~ /^(?:[;&\n|]|&&|\|\|)$/;
|
last if $token->[0] =~ /^(?:[;&\n|]|&&|\|\|)$/;
|
||||||
}
|
}
|
||||||
push(@tokens, $self->next_token()) if $tokens[-1] ne "\n" && defined($token = $self->peek()) && $token eq "\n";
|
push(@tokens, $self->next_token()) if $tokens[-1]->[0] ne "\n" && defined($token = $self->peek()) && $token->[0] eq "\n";
|
||||||
return @tokens;
|
return @tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -453,11 +459,18 @@ package TestParser;
|
|||||||
|
|
||||||
use base 'ShellParser';
|
use base 'ShellParser';
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my $class = shift @_;
|
||||||
|
my $self = $class->SUPER::new(@_);
|
||||||
|
$self->{problems} = [];
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
sub find_non_nl {
|
sub find_non_nl {
|
||||||
my $tokens = shift @_;
|
my $tokens = shift @_;
|
||||||
my $n = shift @_;
|
my $n = shift @_;
|
||||||
$n = $#$tokens if !defined($n);
|
$n = $#$tokens if !defined($n);
|
||||||
$n-- while $n >= 0 && $$tokens[$n] eq "\n";
|
$n-- while $n >= 0 && $$tokens[$n]->[0] eq "\n";
|
||||||
return $n;
|
return $n;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -467,7 +480,7 @@ sub ends_with {
|
|||||||
for my $needle (reverse(@$needles)) {
|
for my $needle (reverse(@$needles)) {
|
||||||
return undef if $n < 0;
|
return undef if $n < 0;
|
||||||
$n = find_non_nl($tokens, $n), next if $needle eq "\n";
|
$n = find_non_nl($tokens, $n), next if $needle eq "\n";
|
||||||
return undef if $$tokens[$n] !~ $needle;
|
return undef if $$tokens[$n]->[0] !~ $needle;
|
||||||
$n--;
|
$n--;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
@ -486,13 +499,13 @@ sub parse_loop_body {
|
|||||||
my $self = shift @_;
|
my $self = shift @_;
|
||||||
my @tokens = $self->SUPER::parse_loop_body(@_);
|
my @tokens = $self->SUPER::parse_loop_body(@_);
|
||||||
# did loop signal failure via "|| return" or "|| exit"?
|
# did loop signal failure via "|| return" or "|| exit"?
|
||||||
return @tokens if !@tokens || grep(/^(?:return|exit|\$\?)$/, @tokens);
|
return @tokens if !@tokens || grep {$_->[0] =~ /^(?:return|exit|\$\?)$/} @tokens;
|
||||||
# did loop upstream of a pipe signal failure via "|| echo 'impossible
|
# did loop upstream of a pipe signal failure via "|| echo 'impossible
|
||||||
# text'" as the final command in the loop body?
|
# text'" as the final command in the loop body?
|
||||||
return @tokens if ends_with(\@tokens, [qr/^\|\|$/, "\n", qr/^echo$/, qr/^.+$/]);
|
return @tokens if ends_with(\@tokens, [qr/^\|\|$/, "\n", qr/^echo$/, qr/^.+$/]);
|
||||||
# flag missing "return/exit" handling explicit failure in loop body
|
# flag missing "return/exit" handling explicit failure in loop body
|
||||||
my $n = find_non_nl(\@tokens);
|
my $n = find_non_nl(\@tokens);
|
||||||
splice(@tokens, $n + 1, 0, '?!LOOP?!');
|
push(@{$self->{problems}}, ['LOOP', $tokens[$n]]);
|
||||||
return @tokens;
|
return @tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -505,8 +518,13 @@ my @safe_endings = (
|
|||||||
|
|
||||||
sub accumulate {
|
sub accumulate {
|
||||||
my ($self, $tokens, $cmd) = @_;
|
my ($self, $tokens, $cmd) = @_;
|
||||||
|
my $problems = $self->{problems};
|
||||||
|
|
||||||
|
# no previous command to check for missing "&&"
|
||||||
goto DONE unless @$tokens;
|
goto DONE unless @$tokens;
|
||||||
goto DONE if @$cmd == 1 && $$cmd[0] eq "\n";
|
|
||||||
|
# new command is empty line; can't yet check if previous is missing "&&"
|
||||||
|
goto DONE if @$cmd == 1 && $$cmd[0]->[0] eq "\n";
|
||||||
|
|
||||||
# did previous command end with "&&", "|", "|| return" or similar?
|
# did previous command end with "&&", "|", "|| return" or similar?
|
||||||
goto DONE if match_ending($tokens, \@safe_endings);
|
goto DONE if match_ending($tokens, \@safe_endings);
|
||||||
@ -514,20 +532,20 @@ sub accumulate {
|
|||||||
# if this command handles "$?" specially, then okay for previous
|
# if this command handles "$?" specially, then okay for previous
|
||||||
# command to be missing "&&"
|
# command to be missing "&&"
|
||||||
for my $token (@$cmd) {
|
for my $token (@$cmd) {
|
||||||
goto DONE if $token =~ /\$\?/;
|
goto DONE if $token->[0] =~ /\$\?/;
|
||||||
}
|
}
|
||||||
|
|
||||||
# if this command is "false", "return 1", or "exit 1" (which signal
|
# if this command is "false", "return 1", or "exit 1" (which signal
|
||||||
# failure explicitly), then okay for all preceding commands to be
|
# failure explicitly), then okay for all preceding commands to be
|
||||||
# missing "&&"
|
# missing "&&"
|
||||||
if ($$cmd[0] =~ /^(?:false|return|exit)$/) {
|
if ($$cmd[0]->[0] =~ /^(?:false|return|exit)$/) {
|
||||||
@$tokens = grep(!/^\?!AMP\?!$/, @$tokens);
|
@$problems = grep {$_->[0] ne 'AMP'} @$problems;
|
||||||
goto DONE;
|
goto DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
# flag missing "&&" at end of previous command
|
# flag missing "&&" at end of previous command
|
||||||
my $n = find_non_nl($tokens);
|
my $n = find_non_nl($tokens);
|
||||||
splice(@$tokens, $n + 1, 0, '?!AMP?!') unless $n < 0;
|
push(@$problems, ['AMP', $tokens->[$n]]) unless $n < 0;
|
||||||
|
|
||||||
DONE:
|
DONE:
|
||||||
$self->SUPER::accumulate($tokens, $cmd);
|
$self->SUPER::accumulate($tokens, $cmd);
|
||||||
@ -553,7 +571,7 @@ sub new {
|
|||||||
# composition of multiple strings and non-string character runs; for instance,
|
# composition of multiple strings and non-string character runs; for instance,
|
||||||
# `"test body"` unwraps to `test body`; `word"a b"42'c d'` to `worda b42c d`
|
# `"test body"` unwraps to `test body`; `word"a b"42'c d'` to `worda b42c d`
|
||||||
sub unwrap {
|
sub unwrap {
|
||||||
my $token = @_ ? shift @_ : $_;
|
my $token = (@_ ? shift @_ : $_)->[0];
|
||||||
# simple case: 'sqstring' or "dqstring"
|
# simple case: 'sqstring' or "dqstring"
|
||||||
return $token if $token =~ s/^'([^']*)'$/$1/;
|
return $token if $token =~ s/^'([^']*)'$/$1/;
|
||||||
return $token if $token =~ s/^"([^"]*)"$/$1/;
|
return $token if $token =~ s/^"([^"]*)"$/$1/;
|
||||||
@ -584,12 +602,21 @@ sub check_test {
|
|||||||
$self->{ntests}++;
|
$self->{ntests}++;
|
||||||
my $parser = TestParser->new(\$body);
|
my $parser = TestParser->new(\$body);
|
||||||
my @tokens = $parser->parse();
|
my @tokens = $parser->parse();
|
||||||
return unless $emit_all || grep(/\?![^?]+\?!/, @tokens);
|
my $problems = $parser->{problems};
|
||||||
|
return unless $emit_all || @$problems;
|
||||||
my $c = main::fd_colors(1);
|
my $c = main::fd_colors(1);
|
||||||
my $checked = join(' ', @tokens);
|
my $start = 0;
|
||||||
|
my $checked = '';
|
||||||
|
for (sort {$a->[1]->[2] <=> $b->[1]->[2]} @$problems) {
|
||||||
|
my ($label, $token) = @$_;
|
||||||
|
my $pos = $token->[2];
|
||||||
|
$checked .= substr($body, $start, $pos - $start) . " ?!$label?! ";
|
||||||
|
$start = $pos;
|
||||||
|
}
|
||||||
|
$checked .= substr($body, $start);
|
||||||
$checked =~ s/^\n//;
|
$checked =~ s/^\n//;
|
||||||
$checked =~ s/^ //mg;
|
$checked =~ s/(\s) \?!/$1?!/mg;
|
||||||
$checked =~ s/ $//mg;
|
$checked =~ s/\?! (\s)/?!$1/mg;
|
||||||
$checked =~ s/(\?![^?]+\?!)/$c->{rev}$c->{red}$1$c->{reset}/mg;
|
$checked =~ s/(\?![^?]+\?!)/$c->{rev}$c->{red}$1$c->{reset}/mg;
|
||||||
$checked .= "\n" unless $checked =~ /\n$/;
|
$checked .= "\n" unless $checked =~ /\n$/;
|
||||||
push(@{$self->{output}}, "$c->{blue}# chainlint: $title$c->{reset}\n$checked");
|
push(@{$self->{output}}, "$c->{blue}# chainlint: $title$c->{reset}\n$checked");
|
||||||
@ -598,9 +625,9 @@ sub check_test {
|
|||||||
sub parse_cmd {
|
sub parse_cmd {
|
||||||
my $self = shift @_;
|
my $self = shift @_;
|
||||||
my @tokens = $self->SUPER::parse_cmd();
|
my @tokens = $self->SUPER::parse_cmd();
|
||||||
return @tokens unless @tokens && $tokens[0] =~ /^test_expect_(?:success|failure)$/;
|
return @tokens unless @tokens && $tokens[0]->[0] =~ /^test_expect_(?:success|failure)$/;
|
||||||
my $n = $#tokens;
|
my $n = $#tokens;
|
||||||
$n-- while $n >= 0 && $tokens[$n] =~ /^(?:[;&\n|]|&&|\|\|)$/;
|
$n-- while $n >= 0 && $tokens[$n]->[0] =~ /^(?:[;&\n|]|&&|\|\|)$/;
|
||||||
$self->check_test($tokens[1], $tokens[2]) if $n == 2; # title body
|
$self->check_test($tokens[1], $tokens[2]) if $n == 2; # title body
|
||||||
$self->check_test($tokens[2], $tokens[3]) if $n > 2; # prereq title body
|
$self->check_test($tokens[2], $tokens[3]) if $n > 2; # prereq title body
|
||||||
return @tokens;
|
return @tokens;
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
(
|
(
|
||||||
{
|
{
|
||||||
|
# show a
|
||||||
echo a &&
|
echo a &&
|
||||||
|
# show b
|
||||||
echo b
|
echo b
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
(
|
(
|
||||||
case "$x" in
|
case "$x" in
|
||||||
|
# found foo
|
||||||
x) foo ;;
|
x) foo ;;
|
||||||
|
# found other
|
||||||
*)
|
*)
|
||||||
|
# treat it as bar
|
||||||
bar
|
bar
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
@ -15,7 +15,8 @@
|
|||||||
) | wuzzle &&
|
) | wuzzle &&
|
||||||
(
|
(
|
||||||
bop
|
bop
|
||||||
) | fazz fozz &&
|
) | fazz \
|
||||||
|
fozz &&
|
||||||
(
|
(
|
||||||
bup
|
bup
|
||||||
) |
|
) |
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
(
|
(
|
||||||
|
# comment 1
|
||||||
nothing &&
|
nothing &&
|
||||||
|
# comment 2
|
||||||
something
|
something
|
||||||
|
# comment 3
|
||||||
|
# comment 4
|
||||||
)
|
)
|
||||||
|
@ -1,2 +1,12 @@
|
|||||||
run_sub_test_lib_test_err run-inv-range-start "--run invalid range start" --run="a-5" <<-EOF &&
|
run_sub_test_lib_test_err run-inv-range-start \
|
||||||
check_sub_test_lib_test_err run-inv-range-start <<-EOF_OUT 3 <<-EOF_ERR
|
"--run invalid range start" \
|
||||||
|
--run="a-5" <<-\EOF &&
|
||||||
|
test_expect_success "passing test #1" "true"
|
||||||
|
test_done
|
||||||
|
EOF
|
||||||
|
check_sub_test_lib_test_err run-inv-range-start \
|
||||||
|
<<-\EOF_OUT 3<<-EOF_ERR
|
||||||
|
> FATAL: Unexpected exit with code 1
|
||||||
|
EOF_OUT
|
||||||
|
> error: --run: invalid non-numeric in range start: ${SQ}a-5${SQ}
|
||||||
|
EOF_ERR
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
git ls-tree $tree path > current &&
|
git ls-tree $tree path > current &&
|
||||||
cat > expected <<EOF &&
|
cat > expected <<\EOF &&
|
||||||
|
EOF
|
||||||
test_output
|
test_output
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
for i in a b c
|
for i in a b c
|
||||||
do
|
do
|
||||||
echo $i ?!AMP?!
|
echo $i ?!AMP?!
|
||||||
cat <<-EOF ?!LOOP?!
|
cat <<-\EOF ?!LOOP?!
|
||||||
|
bar
|
||||||
|
EOF
|
||||||
done ?!AMP?!
|
done ?!AMP?!
|
||||||
for i in a b c; do
|
for i in a b c; do
|
||||||
echo $i &&
|
echo $i &&
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
(
|
(
|
||||||
cat <<-INPUT)
|
cat <<-\INPUT)
|
||||||
|
fizz
|
||||||
|
INPUT
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
cat > expect <<-EOF &&
|
cat >expect <<- EOF &&
|
||||||
|
header: 43475048 1 $(test_oid oid_version) $NUM_CHUNKS 0
|
||||||
|
num_commits: $1
|
||||||
|
chunks: oid_fanout oid_lookup commit_metadata generation_data bloom_indexes bloom_data
|
||||||
|
EOF
|
||||||
|
|
||||||
cat > expect <<-EOF ?!AMP?!
|
cat >expect << -EOF ?!AMP?!
|
||||||
|
this is not indented
|
||||||
|
-EOF
|
||||||
|
|
||||||
cleanup
|
cleanup
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
(
|
(
|
||||||
x=$(bobble <<-END &&
|
x=$(bobble <<-\END &&
|
||||||
|
fossil
|
||||||
|
vegetable
|
||||||
|
END
|
||||||
wiffle) ?!AMP?!
|
wiffle) ?!AMP?!
|
||||||
echo $x
|
echo $x
|
||||||
)
|
)
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
(
|
(
|
||||||
cat <<-TXT && echo "multi-line
|
cat <<-\TXT && echo "multi-line
|
||||||
string" ?!AMP?!
|
string" ?!AMP?!
|
||||||
|
fizzle
|
||||||
|
TXT
|
||||||
bap
|
bap
|
||||||
)
|
)
|
||||||
|
@ -1,7 +1,25 @@
|
|||||||
boodle wobba gorgo snoot wafta snurb <<EOF &&
|
boodle wobba \
|
||||||
|
gorgo snoot \
|
||||||
|
wafta snurb <<EOF &&
|
||||||
|
quoth the raven,
|
||||||
|
nevermore...
|
||||||
|
EOF
|
||||||
|
|
||||||
cat <<-Arbitrary_Tag_42 >foo &&
|
cat <<-Arbitrary_Tag_42 >foo &&
|
||||||
|
snoz
|
||||||
|
boz
|
||||||
|
woz
|
||||||
|
Arbitrary_Tag_42
|
||||||
|
|
||||||
cat <<zump >boo &&
|
cat <<"zump" >boo &&
|
||||||
|
snoz
|
||||||
|
boz
|
||||||
|
woz
|
||||||
|
zump
|
||||||
|
|
||||||
horticulture <<EOF
|
horticulture <<\EOF
|
||||||
|
gomez
|
||||||
|
morticia
|
||||||
|
wednesday
|
||||||
|
pugsly
|
||||||
|
EOF
|
||||||
|
@ -8,7 +8,9 @@
|
|||||||
echo foo
|
echo foo
|
||||||
else
|
else
|
||||||
echo foo &&
|
echo foo &&
|
||||||
cat <<-EOF
|
cat <<-\EOF
|
||||||
|
bar
|
||||||
|
EOF
|
||||||
fi ?!AMP?!
|
fi ?!AMP?!
|
||||||
echo poodle
|
echo poodle
|
||||||
) &&
|
) &&
|
||||||
|
@ -1,4 +1,10 @@
|
|||||||
line 1 line 2 line 3 line 4 &&
|
line 1 \
|
||||||
|
line 2 \
|
||||||
|
line 3 \
|
||||||
|
line 4 &&
|
||||||
(
|
(
|
||||||
line 5 line 6 line 7 line 8
|
line 5 \
|
||||||
|
line 6 \
|
||||||
|
line 7 \
|
||||||
|
line 8
|
||||||
)
|
)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
(
|
(
|
||||||
foobar &&
|
foobar && # comment 1
|
||||||
barfoo ?!AMP?!
|
barfoo ?!AMP?! # wrong position for &&
|
||||||
flibble "not a # comment"
|
flibble "not a # comment"
|
||||||
) &&
|
) &&
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
do
|
do
|
||||||
printf "Generating blob $i/$blobcount\r" >& 2 &&
|
printf "Generating blob $i/$blobcount\r" >& 2 &&
|
||||||
printf "blob\nmark :$i\ndata $blobsize\n" &&
|
printf "blob\nmark :$i\ndata $blobsize\n" &&
|
||||||
|
#test-tool genrandom $i $blobsize &&
|
||||||
printf "%-${blobsize}s" $i &&
|
printf "%-${blobsize}s" $i &&
|
||||||
echo "M 100644 :$i $i" >> commit &&
|
echo "M 100644 :$i $i" >> commit &&
|
||||||
i=$(($i+1)) ||
|
i=$(($i+1)) ||
|
||||||
|
@ -1,7 +1,30 @@
|
|||||||
cat <<ARBITRARY >foop &&
|
cat <<ARBITRARY >foop &&
|
||||||
|
naddle
|
||||||
|
fub <<EOF
|
||||||
|
nozzle
|
||||||
|
noodle
|
||||||
|
EOF
|
||||||
|
formp
|
||||||
|
ARBITRARY
|
||||||
|
|
||||||
(
|
(
|
||||||
cat <<-INPUT_END &&
|
cat <<-\INPUT_END &&
|
||||||
cat <<-EOT ?!AMP?!
|
fish are mice
|
||||||
|
but geese go slow
|
||||||
|
data <<EOF
|
||||||
|
perl is lerp
|
||||||
|
and nothing else
|
||||||
|
EOF
|
||||||
|
toink
|
||||||
|
INPUT_END
|
||||||
|
|
||||||
|
cat <<-\EOT ?!AMP?!
|
||||||
|
text goes here
|
||||||
|
data <<EOF
|
||||||
|
data goes here
|
||||||
|
EOF
|
||||||
|
more test here
|
||||||
|
EOT
|
||||||
|
|
||||||
foobar
|
foobar
|
||||||
)
|
)
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
foo &&
|
foo &&
|
||||||
(
|
(
|
||||||
bar &&
|
bar &&
|
||||||
|
# bottles wobble while fiddles gobble
|
||||||
|
# minor numbers of cows (or do they?)
|
||||||
baz &&
|
baz &&
|
||||||
snaff
|
snaff
|
||||||
) ?!AMP?!
|
) ?!AMP?!
|
||||||
|
@ -1,10 +1,30 @@
|
|||||||
(
|
(
|
||||||
echo wobba gorgo snoot wafta snurb <<-EOF &&
|
echo wobba \
|
||||||
|
gorgo snoot \
|
||||||
|
wafta snurb <<-EOF &&
|
||||||
|
quoth the raven,
|
||||||
|
nevermore...
|
||||||
|
EOF
|
||||||
|
|
||||||
cat <<EOF >bip ?!AMP?!
|
cat <<EOF >bip ?!AMP?!
|
||||||
echo <<-EOF >bop
|
fish fly high
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo <<-\EOF >bop
|
||||||
|
gomez
|
||||||
|
morticia
|
||||||
|
wednesday
|
||||||
|
pugsly
|
||||||
|
EOF
|
||||||
) &&
|
) &&
|
||||||
(
|
(
|
||||||
cat <<-ARBITRARY >bup &&
|
cat <<-\ARBITRARY >bup &&
|
||||||
cat <<-ARBITRARY3 >bup3 &&
|
glink
|
||||||
|
FIZZ
|
||||||
|
ARBITRARY
|
||||||
|
cat <<-"ARBITRARY3" >bup3 &&
|
||||||
|
glink
|
||||||
|
FIZZ
|
||||||
|
ARBITRARY3
|
||||||
meep
|
meep
|
||||||
)
|
)
|
||||||
|
@ -4,12 +4,16 @@ sub2
|
|||||||
sub3
|
sub3
|
||||||
sub4" &&
|
sub4" &&
|
||||||
chks_sub=$(cat <<TXT | sed "s,^,sub dir/,"
|
chks_sub=$(cat <<TXT | sed "s,^,sub dir/,"
|
||||||
|
$chks
|
||||||
|
TXT
|
||||||
) &&
|
) &&
|
||||||
chkms="main-sub1
|
chkms="main-sub1
|
||||||
main-sub2
|
main-sub2
|
||||||
main-sub3
|
main-sub3
|
||||||
main-sub4" &&
|
main-sub4" &&
|
||||||
chkms_sub=$(cat <<TXT | sed "s,^,sub dir/,"
|
chkms_sub=$(cat <<TXT | sed "s,^,sub dir/,"
|
||||||
|
$chkms
|
||||||
|
TXT
|
||||||
) &&
|
) &&
|
||||||
subfiles=$(git ls-files) &&
|
subfiles=$(git ls-files) &&
|
||||||
check_equal "$subfiles" "$chkms
|
check_equal "$subfiles" "$chkms
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
while true
|
while true
|
||||||
do
|
do
|
||||||
echo foo ?!AMP?!
|
echo foo ?!AMP?!
|
||||||
cat <<-EOF ?!LOOP?!
|
cat <<-\EOF ?!LOOP?!
|
||||||
|
bar
|
||||||
|
EOF
|
||||||
done ?!AMP?!
|
done ?!AMP?!
|
||||||
while true; do
|
while true; do
|
||||||
echo foo &&
|
echo foo &&
|
||||||
|
Reference in New Issue
Block a user