| 1 | #!/usr/bin/perl |
|---|
| 2 | |
|---|
| 3 | use strict; |
|---|
| 4 | use warnings; |
|---|
| 5 | |
|---|
| 6 | my %OPTS; |
|---|
| 7 | while( $_ = $ARGV[0], /^-/ ) { |
|---|
| 8 | shift; |
|---|
| 9 | last if /^--$/; |
|---|
| 10 | $OPTS{$_} = $_; |
|---|
| 11 | } |
|---|
| 12 | |
|---|
| 13 | my $ME = shift; |
|---|
| 14 | my $IN = shift; |
|---|
| 15 | my $OUT = shift; |
|---|
| 16 | |
|---|
| 17 | if (!$OUT and $IN) { |
|---|
| 18 | ($OUT = $IN) =~ s/\.t$/.$ME/ or $OUT .= ".$ME"; |
|---|
| 19 | } |
|---|
| 20 | unless ($ME and $IN and -e $IN and $OUT) { |
|---|
| 21 | |
|---|
| 22 | die <<"USAGE"; |
|---|
| 23 | Usage: $0 [options] implname testfilename [fudgedtestfilename] |
|---|
| 24 | |
|---|
| 25 | Options: |
|---|
| 26 | --keep-exit-code |
|---|
| 27 | by default, fudge modifies the exit code for fudged test files to 1. |
|---|
| 28 | supplying this option will suppress that behavior. |
|---|
| 29 | |
|---|
| 30 | Verbs: |
|---|
| 31 | #?implname [num] skip 'reason' |
|---|
| 32 | comment out num tests or blocks and call skip(num) |
|---|
| 33 | |
|---|
| 34 | #?implname [num] eval 'reason' |
|---|
| 35 | eval num tests or blocks and skip(num) on parsefail |
|---|
| 36 | |
|---|
| 37 | #?implname [num] try 'reason' |
|---|
| 38 | try num tests or blocks and fail on exception |
|---|
| 39 | |
|---|
| 40 | #?implname [num] todo 'reason', :by<1.2.3> |
|---|
| 41 | run num tests or blocks with todo() preset |
|---|
| 42 | |
|---|
| 43 | #?implname emit your_ad_here(); |
|---|
| 44 | just pass through your_ad_here(); |
|---|
| 45 | |
|---|
| 46 | #?DOES count |
|---|
| 47 | for all implementations, the following thing does count tests |
|---|
| 48 | (disables any attempt to autocount tests within the construct) |
|---|
| 49 | when construct is a sub, registers the sub name as tester |
|---|
| 50 | (and multiplies calls to tester sub by count tests) |
|---|
| 51 | |
|---|
| 52 | where |
|---|
| 53 | implname is the lc name of your implementation, e.g. pugs or rakudo |
|---|
| 54 | num is the number of statements or blocks to preprocess, defaults to 1 |
|---|
| 55 | count is how many tests the following construct counts as |
|---|
| 56 | |
|---|
| 57 | |
|---|
| 58 | USAGE |
|---|
| 59 | } |
|---|
| 60 | |
|---|
| 61 | if (-e $OUT) { |
|---|
| 62 | if (-M $IN >= -M $OUT and -M $0 >= -M $OUT) { |
|---|
| 63 | print "$OUT\n"; # unchanged, so no need to refudge |
|---|
| 64 | exit(0); |
|---|
| 65 | } |
|---|
| 66 | else { |
|---|
| 67 | unlink $OUT; # old fudged version, may or may not regenerate... |
|---|
| 68 | } |
|---|
| 69 | } |
|---|
| 70 | |
|---|
| 71 | my $REALLY_FUDGED = 0; |
|---|
| 72 | my $OUTPUT = ""; |
|---|
| 73 | my $FUDGE = ""; |
|---|
| 74 | our $PENDING = 0; |
|---|
| 75 | my $ARGS = ''; |
|---|
| 76 | my $IS = '\\b(?:is|ok|nok|is_deeply|is_approx|isnt|like|unlike|eval_dies_ok|cmp_ok|isa_ok|use_ok|throws_ok|dies_ok|lives_ok|eval_lives_ok|pass|flunk)(?:\\b|_)'; |
|---|
| 77 | my %DOES; |
|---|
| 78 | my $DOES = 0; |
|---|
| 79 | my $EXIT = $OPTS{'--keep-exit-code'} ? '' : 'exit(1);'; |
|---|
| 80 | |
|---|
| 81 | @ARGV = ($IN); |
|---|
| 82 | fudgeblock(); |
|---|
| 83 | |
|---|
| 84 | if ($REALLY_FUDGED) { |
|---|
| 85 | open OUT, ">", $OUT or die "Can't create $OUT: $!"; |
|---|
| 86 | print OUT $OUTPUT; |
|---|
| 87 | print OUT <<"END"; |
|---|
| 88 | |
|---|
| 89 | say "# FUDGED!"; |
|---|
| 90 | $EXIT |
|---|
| 91 | END |
|---|
| 92 | close OUT; |
|---|
| 93 | print "$OUT\n"; # pick the output file to run |
|---|
| 94 | } |
|---|
| 95 | else { |
|---|
| 96 | print "$IN\n"; # pick the input file to run |
|---|
| 97 | } |
|---|
| 98 | |
|---|
| 99 | sub fudgeblock { |
|---|
| 100 | while (<>) { |
|---|
| 101 | if (/^\s*\#\?DOES[:\s] \s* (.*)/x) { |
|---|
| 102 | $DOES = $1; |
|---|
| 103 | next; |
|---|
| 104 | } |
|---|
| 105 | if (/^\s*\#\? (\w+)[:\s] \s* (.*)/x and $1 eq $ME) { |
|---|
| 106 | $REALLY_FUDGED = 1; |
|---|
| 107 | $ARGS = $2; |
|---|
| 108 | if ($ARGS =~ s/^emit\s*//) { |
|---|
| 109 | $_ = $ARGS; |
|---|
| 110 | next; |
|---|
| 111 | } |
|---|
| 112 | if ($ARGS =~ s/^(\d+)\s*//) { |
|---|
| 113 | $PENDING = $1; |
|---|
| 114 | } |
|---|
| 115 | else { |
|---|
| 116 | $PENDING = 1; |
|---|
| 117 | } |
|---|
| 118 | $ARGS =~ s/^(\w+)\s*//; |
|---|
| 119 | $FUDGE = $1; |
|---|
| 120 | } |
|---|
| 121 | |
|---|
| 122 | next if /^\s*#/; |
|---|
| 123 | next if /^\s*$/; |
|---|
| 124 | |
|---|
| 125 | if ($DOES) { |
|---|
| 126 | if (/^\s*(sub|multi|proto)\b/) { |
|---|
| 127 | my $tmp = $_; |
|---|
| 128 | $tmp =~ s/^\s*proto\s+//; |
|---|
| 129 | $tmp =~ s/^\s*multi\s+//; |
|---|
| 130 | $tmp =~ s/^\s*sub\s+//; |
|---|
| 131 | $tmp =~ /^(\w+)/; |
|---|
| 132 | $DOES{$1} = $DOES; |
|---|
| 133 | $DOES = 0; |
|---|
| 134 | next; |
|---|
| 135 | } |
|---|
| 136 | } |
|---|
| 137 | |
|---|
| 138 | next unless $PENDING > 0; |
|---|
| 139 | |
|---|
| 140 | if (/^\{/) { |
|---|
| 141 | $PENDING--; |
|---|
| 142 | if ($FUDGE eq 'todo') { |
|---|
| 143 | local $PENDING = 999999; # do all in block as one action |
|---|
| 144 | $OUTPUT .= $_; |
|---|
| 145 | $DOES = 0; # XXX ignore? |
|---|
| 146 | fudgeblock(); |
|---|
| 147 | $_ = ''; |
|---|
| 148 | } |
|---|
| 149 | else { |
|---|
| 150 | my $more; |
|---|
| 151 | while (defined($more = <>)) { |
|---|
| 152 | $_ .= $more; |
|---|
| 153 | last if $more =~ /^\}/; |
|---|
| 154 | } |
|---|
| 155 | my $numtests = $DOES || do { |
|---|
| 156 | my $tmp = $_; |
|---|
| 157 | my $nt = 0; |
|---|
| 158 | $nt += $1 while $tmp =~ s/^#\?DOES[:\s]\s*(\d+).*\n.*\n//m; |
|---|
| 159 | if (%DOES) { |
|---|
| 160 | my $does = join('|',keys(%DOES)); |
|---|
| 161 | $nt += $DOES{$1} while $tmp =~ s/^\s*($does)\b//mx; |
|---|
| 162 | } |
|---|
| 163 | $nt += () = $tmp =~ m/^(\s*$IS)/mgx; |
|---|
| 164 | $nt; |
|---|
| 165 | }; |
|---|
| 166 | if ($FUDGE eq 'skip') { |
|---|
| 167 | s/^/# /mg; |
|---|
| 168 | $_ = "skip($numtests, $ARGS);" . $_; |
|---|
| 169 | } |
|---|
| 170 | elsif ($FUDGE eq 'try') { |
|---|
| 171 | chomp; |
|---|
| 172 | $_ = "(try $_) // flunk($ARGS);\n"; |
|---|
| 173 | } |
|---|
| 174 | elsif ($FUDGE eq 'eval') { |
|---|
| 175 | chomp; |
|---|
| 176 | s/(['\\])/\\$1/g; |
|---|
| 177 | $_ = "eval('$_') // skip($numtests, $ARGS);\n"; |
|---|
| 178 | } |
|---|
| 179 | else { |
|---|
| 180 | warn "Don't know how to mark block for $FUDGE!\n"; |
|---|
| 181 | } |
|---|
| 182 | } |
|---|
| 183 | } |
|---|
| 184 | else { |
|---|
| 185 | if ($FUDGE eq 'todo') { |
|---|
| 186 | $DOES = 0; # XXX ignore? |
|---|
| 187 | $PENDING -= s/^(\s*)/${1}todo($ARGS); / if /^\s*$IS/; |
|---|
| 188 | } |
|---|
| 189 | else { |
|---|
| 190 | while ($_ !~ /;[ \t]*(#.*)?$/) { |
|---|
| 191 | my $more = <>; |
|---|
| 192 | last unless $more; |
|---|
| 193 | $_ .= $more; |
|---|
| 194 | } |
|---|
| 195 | my ($keyword) = /^\s*(\w+)/ || ''; |
|---|
| 196 | my $numtests; |
|---|
| 197 | if ($DOES{$keyword}) { |
|---|
| 198 | $numtests = $DOES{$keyword}; |
|---|
| 199 | } |
|---|
| 200 | elsif ($DOES) { |
|---|
| 201 | $numtests = $DOES; |
|---|
| 202 | } |
|---|
| 203 | else { |
|---|
| 204 | next unless /^\s*$IS/; |
|---|
| 205 | $numtests = 1; |
|---|
| 206 | } |
|---|
| 207 | $PENDING--; |
|---|
| 208 | $_ = "{ " . $_ . " }"; |
|---|
| 209 | if ($FUDGE eq 'skip') { |
|---|
| 210 | s/^/# /mg; |
|---|
| 211 | $_ = "skip($numtests,$ARGS); $_\n"; |
|---|
| 212 | } |
|---|
| 213 | elsif ($FUDGE eq 'try') { |
|---|
| 214 | $_ = "(try $_) // flunk($ARGS);\n"; |
|---|
| 215 | } |
|---|
| 216 | elsif ($FUDGE eq 'eval') { |
|---|
| 217 | s/(['\\])/\\$1/g; |
|---|
| 218 | $_ = "eval('$_') // skip($numtests,$ARGS);\n"; |
|---|
| 219 | } |
|---|
| 220 | else { |
|---|
| 221 | warn "Don't know how to mark statement for $FUDGE!\n"; |
|---|
| 222 | } |
|---|
| 223 | } |
|---|
| 224 | } |
|---|
| 225 | } |
|---|
| 226 | continue { |
|---|
| 227 | $OUTPUT .= $_; |
|---|
| 228 | return if /^\}/ and $PENDING > 0; |
|---|
| 229 | } |
|---|
| 230 | } |
|---|