2
# Copyright (C) 2008, The Perl Foundation.
7
autounfudge - automatically write patches for unfudging spec tests
11
This tool runs the non-pure tests of the C<spectest> make target,
12
automatically creates files with less 'skip' fudge directives, runs them
14
modified tests succeeds, it adds a patch to C<autounfudge.patch> that, when
15
applied as C<< patch -p0 < autunfudge.patch >>, removes the superflous fudge
18
With the C<--untodo> option, C<todo> skip markers are also removed (where
19
appropriate), with the C<--unskip> option it tries to substitute C<skip>
20
markers by C<todo> markers.
24
Most common usage: C<perl tools/autounfudge.pl --auto>. For more options
25
please run this script without any options or command line parameters.
29
This tool assumes that all fudge directives are orthogonal,
30
which might not be the case in real world tests. So always make sure to
31
run C<make spectest> before commiting the changes.
33
Never blindly apply the automatically generated patch.
37
Fudge directives containing the words I<unspecced>, I<noauto> or I<unicode>
39
The latter is because Unicode related tests can succeed on platforms with icu
40
installed, and fail on other platforms.
42
By default some files are skipped (which can be overridden with the
43
C<--exclude> option) because certain tests loop (at the time of writing
44
C<t/spec/S04-statement-modifiers/while.t>), others because processing them
45
simply takes too long; C<t/spec/S05-mass/rx.t> contains more than 250
46
fudge lines and thus would take about three hours to autoumatically unfudge.
55
use File::Temp qw(tempfile tempdir);
57
use TAP::Parser::Aggregator;
68
our $out_filename = 'autounfudge.patch';
69
my $exclude = '(?:(?:radix|modifiers/(?:while|until)|rx|assign)\.t)$';
72
GetOptions 'impl=s' => \$impl,
74
'specfile=s' => \my $specfile,
76
'keep-env' => \my $keep_env,
77
'unskip' => \my $unskip,
78
'untodo' => \my $untodo,
79
'section=s' => \my $section,
80
'exclude' => \$exclude,
81
'jobs=i' => \$threads_num,
84
delete $ENV{PERL6LIB} unless $keep_env;
88
$specfile = 't/spectest.data' if $auto;
91
@files = read_specfile($specfile);
94
@files = @ARGV or usage();
98
my $s = ($section =~ m/^\d{1,2}$/)
99
? sprintf('S%02d', $section)
101
print "Only of section `$section'\n";
102
@files = grep { m{ spec [/\\] \Q$section\E }x } @files;
105
our $diff_lock :shared = 0;
106
open our $diff_fh, '>', $out_filename
107
or die "Can't open '$out_filename' for writing: $!";
114
our $tmp_dir = tempdir('RAKUDOXXXXXX', CLEANUP => 1);
116
if ($threads_num > 1) {
117
my $queue = Thread::Queue->new;
118
for (1..$threads_num) {
119
threads->create(sub {
120
while(my $file_name = $queue->dequeue) {
121
auto_unfudge_file($file_name);
126
$queue->enqueue($_) for @files;
127
$queue->enqueue(undef) for 1..$threads_num;
128
$_->join for threads->list;
132
auto_unfudge_file($_);
137
sub auto_unfudge_file {
138
my $file_name = shift;
140
return unless defined $file_name;
141
open my $f, '<:encoding(UTF-8)', $file_name
142
or die "Can't open '$file_name' for reading: $!";
143
print "Processing file '$file_name'\n";
146
push @fudge_lines, [$. , $_] if m/^\s*#\?$impl/ &&
147
!m/unspecced|unicode|utf-?8|noauto/i;
151
print "Found " . (scalar @fudge_lines) . " fudges...\n" if $debug;
154
print "No fudges found. Nothing to do\n" if $debug;
157
my $fudged = fudge($file_name);
158
print "Fudged: $fudged\n" if $debug;
159
if (!tests_ok($fudged)){
160
print "File '$file_name' doesn't even pass in its current state\n";
164
for my $to_unfudge (@fudge_lines){
165
print "trying line $to_unfudge->[0]...\n" if $debug;
166
next if $to_unfudge->[1] =~ m/\btodo\b/ && !$untodo;
167
$fudged = fudge(unfudge_some($file_name, [$to_unfudge->[0], '']));
168
if (tests_ok($fudged)){
169
print "WOOOOOT: Can remove fudge instruction on line $to_unfudge->[0]\n"
171
push @to_unfudge, [$to_unfudge->[0], ''],
173
elsif ($unskip && $to_unfudge->[1] =~ s/\bskip\b/todo/) {
174
# try to replace 'skip' with 'todo'-markers
175
$fudged = fudge(unfudge_some($file_name, $to_unfudge));
176
if (tests_ok($fudged)){
177
print "s/skip/todo/ successful\n" if $debug;
178
push @to_unfudge, $to_unfudge;
182
print "not successful\n"if $debug;
187
my $u = unfudge_some($file_name, @to_unfudge);
189
print $diff_fh diff($file_name, $u);
198
open my $p, '-|', 't/spec/fudge', '--keep-exit-code', $impl, $fn
199
or die "Can't launch fudge: $!";
212
--debug Enable debug output
213
--impl impl Specify a different implementation
214
--specfile file Specification file to read filenames from
215
--auto use t/spectest.data for --specfile
216
--keep-env Keep PERL6LIB environment variable.
217
--exclude regex Don't run the tests that match regex
218
--section number Run only on tests belonging to section <number>
219
--unskip Try to change 'skip' to 'todo' markers
220
--untodo Try to remove 'todo' markers
225
my ($file, @lines) = @_;
227
my ($fh, $tmp_filename) = tempfile(
232
open my $in, '<', $file
233
or die "Can't open file '$file' for reading: $!";
235
if ($. == $lines[0][0]){
236
print $fh $lines[0][1];
237
shift @lines if @lines > 1;
245
return $tmp_filename;
252
my $harness = get_harness();
253
my $agg = TAP::Parser::Aggregator->new();
255
$harness->aggregate_tests($agg, $fn);
257
return !$agg->has_errors;
261
return TAP::Harness->new({
272
open (my $f, '<', $fn) or die "Can't open file '$fn' for reading: $!";
277
m/(\S+)/ && push @res, "t/spec/$1";
283
close $diff_fh if $diff_fh;
284
File::Path::rmtree($tmp_dir);
289
# cperl-indent-level: 4
292
# vim: expandtab shiftwidth=4: