1#!./perl 2 3# We suppose that perl _mostly_ works at this moment, so may use 4# sophisticated testing. 5 6BEGIN { 7 chdir 't' if -d 't'; 8 @INC = '../lib'; # pick up only this build's lib 9} 10 11my $torture; # torture testing? 12 13use TAP::Harness 3.13; 14use strict; 15use Config; 16 17$::do_nothing = $::do_nothing = 1; 18require './TEST'; 19 20my $Verbose = 0; 21$Verbose++ while @ARGV && $ARGV[0] eq '-v' && shift; 22 23if ($ARGV[0] && $ARGV[0] eq '-torture') { 24 shift; 25 $torture = 1; 26} 27 28# Let tests know they're running in the perl core. Useful for modules 29# which live dual lives on CPAN. 30$ENV{PERL_CORE} = 1; 31 32#fudge DATA for now. 33my %datahandle = qw( 34 lib/bigint.t 1 35 lib/bigintpm.t 1 36 lib/bigfloat.t 1 37 lib/bigfloatpm.t 1 38 op/gv.t 1 39 lib/complex.t 1 40 lib/ph.t 1 41 lib/soundex.t 1 42 op/misc.t 1 43 op/runlevel.t 1 44 op/tie.t 1 45 op/lex_assign.t 1 46 ); 47 48foreach (keys %datahandle) { 49 unlink "$_.t"; 50} 51 52my (@tests, $re); 53 54# [.VMS]TEST.COM calls harness with empty arguments, so clean-up @ARGV 55@ARGV = grep $_ && length( $_ ) => @ARGV; 56 57sub _extract_tests; 58sub _extract_tests { 59 # This can probably be done more tersely with a map, but I doubt that it 60 # would be as clear 61 my @results; 62 foreach (@_) { 63 my $ref = ref $_; 64 if ($ref) { 65 if ($ref eq 'ARRAY') { 66 push @results, _extract_tests @$_; 67 } elsif ($ref eq 'HASH') { 68 push @results, _extract_tests values %$_; 69 } else { 70 die "Unknown reference type $ref"; 71 } 72 } else { 73 push @results, glob $_; 74 } 75 } 76 @results; 77} 78 79if ($ARGV[0] && $ARGV[0]=~/^-re/) { 80 if ($ARGV[0]!~/=/) { 81 shift; 82 $re=join "|",@ARGV; 83 @ARGV=(); 84 } else { 85 (undef,$re)=split/=/,shift; 86 } 87} 88 89my $jobs = $ENV{TEST_JOBS}; 90my ($rules, $state, $color); 91if ($ENV{HARNESS_OPTIONS}) { 92 for my $opt ( split /:/, $ENV{HARNESS_OPTIONS} ) { 93 if ( $opt =~ /^j(\d*)$/ ) { 94 $jobs ||= $1 || 9; 95 } 96 elsif ( $opt eq 'c' ) { 97 $color = 1; 98 } 99 else { 100 die "Unknown HARNESS_OPTIONS item: $opt\n"; 101 } 102 } 103} 104 105if (@ARGV) { 106 # If you want these run in speed order, just use prove 107 if ($^O eq 'MSWin32') { 108 @tests = map(glob($_),@ARGV); 109 } 110 else { 111 @tests = @ARGV; 112 } 113 # This is a hack to force config_heavy.pl to be loaded, before the 114 # prep work for running a test changes directory. 115 1 if $Config{d_fork}; 116} else { 117 # Ideally we'd get somewhere close to Tux's Oslo rules 118 # my $rules = { 119 # par => [ 120 # { seq => '../ext/DB_File/t/*' }, 121 # { seq => '../ext/IO_Compress_Zlib/t/*' }, 122 # { seq => '../lib/ExtUtils/t/*' }, 123 # '*' 124 # ] 125 # }; 126 127 # but for now, run all directories in sequence. 128 129 unless (@tests) { 130 my @seq = <base/*.t>; 131 132 my @next = qw(comp run cmd io re opbasic op uni mro lib porting); 133 push @next, 'japh' if $torture; 134 push @next, 'win32' if $^O eq 'MSWin32'; 135 push @next, 'benchmark' if $ENV{PERL_BENCHMARK}; 136 push @next, 'bigmem' if $ENV{PERL_TEST_MEMORY}; 137 # Hopefully TAP::Parser::Scheduler will support this syntax soon. 138 # my $next = { par => '{' . join (',', @next) . '}/*.t' }; 139 my $next = { par => [ 140 map { "$_/*.t" } @next 141 ] }; 142 @tests = _extract_tests ($next); 143 144 # This is a bit of a game, because we only want to sort these tests in 145 # speed order. base/*.t wants to run first, and ext,lib etc last and in 146 # MANIFEST order 147 if ($jobs) { 148 require App::Prove::State; 149 $state = App::Prove::State->new({ store => 'test_state' }); 150 $state->apply_switch('slow', 'save'); 151 # For some reason get_tests returns *all* the tests previously run, 152 # (in the right order), not simply the selection in @tests 153 # (in the right order). Not sure if this is a bug or a feature. 154 # Whatever, *we* are only interested in the ones that are in @tests 155 my %seen; 156 @seen{@tests} = (); 157 @tests = grep {exists $seen{$_} } $state->get_tests(0, @tests); 158 } 159 @tests = (@seq, @tests); 160 push @seq, $next; 161 162 my @last; 163 push @last, sort { lc $a cmp lc $b } 164 _tests_from_manifest($Config{extensions}, $Config{known_extensions}); 165 push @last, <x2p/*.t>; 166 167 my %times; 168 if ($state) { 169 # Where known, collate the elapsed times by test name 170 foreach ($state->results->tests()) { 171 $times{$_->name} = $_->elapsed(); 172 } 173 } 174 175 my %dir; 176 my %total_time; 177 178 for (@last) { 179 if ($^O eq 'MSWin32') { 180 s,\\,/,g; # canonicalize path 181 }; 182 # Treat every file matching lib/*.t as a "directory" 183 m!\A(\.\./lib/[^/]+\.t\z|.*[/])! or die "'$_'"; 184 push @{$dir{$1}}, $_; 185 $total_time{$1} += $times{$_} || 0; 186 } 187 188 push @tests, @last; 189 190 # Generate T::H schedule rules that run the contents of each directory 191 # sequentially. 192 push @seq, { par => [ map { s!/$!/*!; { seq => $_ } } sort { 193 # Directories, ordered by total time descending then name ascending 194 $total_time{$b} <=> $total_time{$a} || $a cmp $b 195 } keys %dir ] }; 196 197 $rules = { seq => \@seq }; 198 } 199} 200if ($^O eq 'MSWin32') { 201 s,\\,/,g for @tests; 202} 203@tests=grep /$re/, @tests 204 if $re; 205 206my %options; 207 208my $type = 'perl'; 209 210# Load TAP::Parser now as otherwise it could be required in the short time span 211# in which the harness process chdirs into ext/Dist 212require TAP::Parser; 213 214my $h = TAP::Harness->new({ 215 rules => $rules, 216 color => $color, 217 jobs => $jobs, 218 verbosity => $Verbose, 219 exec => sub { 220 my ($harness, $test) = @_; 221 222 my $options = $options{$test}; 223 if (!defined $options) { 224 $options = $options{$test} = _scan_test($test, $type); 225 } 226 227 return [ split ' ', _cmd($options, $type) ]; 228 }, 229}); 230 231if ($state) { 232 $h->callback( 233 after_test => sub { 234 $state->observe_test(@_); 235 } 236 ); 237 $h->callback( 238 after_runtests => sub { 239 $state->commit(@_); 240 } 241 ); 242} 243 244$h->callback( 245 parser_args => sub { 246 my ($args, $job) = @_; 247 my $test = $job->[0]; 248 _before_fork($options{$test}); 249 push @{ $args->{switches} }, "-I../../lib"; 250 } 251 ); 252 253$h->callback( 254 made_parser => sub { 255 my ($parser, $job) = @_; 256 my $test = $job->[0]; 257 my $options = delete $options{$test}; 258 _after_fork($options); 259 } 260 ); 261 262my $agg = $h->runtests(@tests); 263exit $agg->has_errors ? 1 : 0; 264