#!./perl
BEGIN {
chdir
't'
if
-d
't'
;
@INC
=
'../lib'
;
}
my
$torture
;
$::do_nothing = $::do_nothing = 1;
require
'./TEST'
;
our
$Valgrind_Log
;
my
$Verbose
= 0;
$Verbose
++
while
@ARGV
&&
$ARGV
[0] eq
'-v'
&&
shift
;
my
$htoolnm
;
my
$hgrind_ct
;
if
(
$ARGV
[0] &&
$ARGV
[0] eq
'-torture'
) {
shift
;
$torture
= 1;
}
$ENV
{PERL_CORE} = 1;
my
(
@tests
,
@re
,
@anti_re
);
@ARGV
=
grep
$_
&&
length
(
$_
) =>
@ARGV
;
sub
_extract_tests;
sub
_extract_tests {
my
@results
;
foreach
(
@_
) {
my
$ref
=
ref
$_
;
if
(
$ref
) {
if
(
$ref
eq
'ARRAY'
) {
push
@results
, _extract_tests
@$_
;
}
elsif
(
$ref
eq
'HASH'
) {
push
@results
, _extract_tests
values
%$_
;
}
else
{
die
"Unknown reference type $ref"
;
}
}
else
{
push
@results
,
glob
$_
;
}
}
@results
;
}
while
(
$ARGV
[0] &&
$ARGV
[0]=~/^-(n?)re/) {
my
$ary
= $1 ? \
@anti_re
: \
@re
;
if
(
$ARGV
[0] !~ /=/ ) {
shift
@ARGV
;
while
(
@ARGV
and
$ARGV
[0] !~ /^-/) {
push
@$ary
,
shift
@ARGV
;
}
}
else
{
push
@$ary
, (
split
/=/,
shift
@ARGV
)[1];
}
}
my
$jobs
=
$ENV
{TEST_JOBS};
my
(
$rules
,
$state
,
$color
);
if
(
$ENV
{HARNESS_OPTIONS}) {
for
my
$opt
(
split
/:/,
$ENV
{HARNESS_OPTIONS} ) {
if
(
$opt
=~ /^j(\d*)$/ ) {
$jobs
||= $1 || 9;
}
elsif
(
$opt
eq
'c'
) {
$color
= 1;
}
else
{
die
"Unknown HARNESS_OPTIONS item: $opt\n"
;
}
}
}
if
(
@ARGV
) {
@tests
=
map
(
glob
(
$_
),
@ARGV
);
1
if
$Config
{d_fork};
}
else
{
unless
(
@tests
) {
my
@seq
= <base/*.t>;
my
@last
;
my
@next
=
qw(comp run cmd)
;
my
$which
=
$ENV
{PERL_TEST_HARNESS_ASAP} ? \
@last
: \
@next
;
push
@$which
,
qw(io re opbasic op uni mro lib porting perf)
;
push
@$which
,
'japh'
if
$torture
;
push
@$which
,
'win32'
if
$^O eq
'MSWin32'
;
push
@$which
,
'benchmark'
if
$ENV
{PERL_BENCHMARK};
push
@$which
,
'bigmem'
if
$ENV
{PERL_TEST_MEMORY};
my
$next
= {
par
=> [
map
{
"$_/*.t"
}
@next
] };
@tests
= _extract_tests (
$next
);
my
$last
= {
par
=>
'{'
.
join
(
','
,
@last
) .
'}/*.t'
};
@last
= _extract_tests (
$last
);
if
(
$jobs
) {
$state
= App::Prove::State->new({
store
=>
'test_state'
});
$state
->apply_switch(
'slow'
,
'save'
);
my
%seen
;
@seen
{
@tests
} = ();
@tests
=
grep
{
exists
$seen
{
$_
} }
$state
->get_tests(0,
@tests
);
}
@tests
= (
@seq
,
@tests
);
push
@seq
,
$next
;
push
@last
,
_tests_from_manifest(
$Config
{extensions},
$Config
{known_extensions});
my
%times
;
if
(
$state
) {
foreach
(
$state
->results->tests()) {
$times
{
$_
->name} =
$_
->elapsed();
}
}
my
%dir
;
my
%total_time
;
my
%serials
;
my
%all_dirs
;
for
(
@last
) {
if
($^O eq
'MSWin32'
) {
s,\\,/,g;
};
if
( m! \A (?: \.\. / )?
( .*? )
/
( [^/]* \.t )
\z !x)
{
my
$path
= $1;
$all_dirs
{
$path
} = 1;
$serials
{
$path
} = 1
if
$2 =~ / \A 0 /x;
}
}
for
(
qw(cpan/IO-Zlib/t ext/File-Find/t)
) {
$serials
{
$_
} = 1;
}
my
@nonexistent_serials
=
grep
{ not
exists
$all_dirs
{
$_
} }
keys
%serials
;
if
(
@nonexistent_serials
) {
die
"These directories to be run serially don't exist."
.
" Check your spelling:\n"
.
join
"\n"
,
@nonexistent_serials
;
}
my
$non_serials
=
join
"|"
,
grep
{ not
exists
$serials
{
$_
} }
keys
%all_dirs
;
undef
%all_dirs
;
undef
%serials
;
for
(
@last
) {
m! \A ( (?: \.\. / )? (?:
$non_serials
)
/ [^/]+ \.t \z | .* [/] ) !x
or
die
"'$_'"
;
push
@{
$dir
{$1}},
$_
;
$total_time
{$1} +=
$times
{
$_
} || 0;
}
push
@tests
,
@last
;
push
@seq
, {
par
=> [
map
{ s!/$!/*!; {
seq
=>
$_
} }
sort
{
$total_time
{
$b
} <=>
$total_time
{
$a
} ||
lc
$a
cmp
lc
$b
}
keys
%dir
] };
$rules
= {
seq
=> \
@seq
};
}
}
if
($^O eq
'MSWin32'
) {
s,\\,/,g
for
@tests
;
}
if
(
@re
or
@anti_re
) {
my
@keepers
;
foreach
my
$test
(
@tests
) {
my
$keep
= 0;
if
(
@re
) {
foreach
my
$re
(
@re
) {
$keep
= 1
if
$test
=~/
$re
/;
}
}
else
{
$keep
= 1;
}
if
(
@anti_re
) {
foreach
my
$anti_re
(
@anti_re
) {
$keep
= 0
if
$test
=~/
$anti_re
/;
}
}
if
(
$keep
) {
push
@keepers
,
$test
;
}
}
@tests
=
@keepers
;
}
for
(
@tests
) {
if
(! -f
$_
&& !/^\.\./ && -f
"../$_"
) {
$_
=
"../$_"
;
s{^\.\./t/}{};
}
}
my
%options
;
my
$type
=
'perl'
;
my
$h
= TAP::Harness->new({
rules
=>
$rules
,
color
=>
$color
,
jobs
=>
$jobs
,
verbosity
=>
$Verbose
,
timer
=>
$ENV
{HARNESS_TIMER},
exec
=>
sub
{
my
(
$harness
,
$test
) =
@_
;
my
$options
=
$options
{
$test
};
if
(!
defined
$options
) {
$options
=
$options
{
$test
} = _scan_test(
$test
,
$type
);
}
(
local
$Valgrind_Log
=
"$test.valgrind-current"
) =~ s/^.*\///;
return
[
split
' '
, _cmd(
$options
,
$type
) ];
},
});
if
(
$ENV
{PERL_VALGRIND}) {
$h
->callback(
after_test
=>
sub
{
my
(
$job
) =
@_
;
my
$test
=
$job
->[0];
my
$vfile
=
"$test.valgrind-current"
;
$vfile
=~ s/^.*\///;
if
( (! -z
$vfile
) &&
open
(
my
$voutput
,
'<'
,
$vfile
)) {
print
"$test: Valgrind output:\n"
;
print
"$test: $_"
for
<
$voutput
>;
close
(
$voutput
);
}
(
local
$Valgrind_Log
=
"$test.valgrind-current"
) =~ s/^.*\///;
_check_valgrind(\
$htoolnm
, \
$hgrind_ct
, \
$test
);
}
);
}
if
(
$state
) {
$h
->callback(
after_test
=>
sub
{
$state
->observe_test(
@_
);
}
);
$h
->callback(
after_runtests
=>
sub
{
$state
->commit(
@_
);
}
);
}
$h
->callback(
parser_args
=>
sub
{
my
(
$args
,
$job
) =
@_
;
my
$test
=
$job
->[0];
_before_fork(
$options
{
$test
});
push
@{
$args
->{switches} },
"-I../../lib"
;
}
);
$h
->callback(
made_parser
=>
sub
{
my
(
$parser
,
$job
) =
@_
;
my
$test
=
$job
->[0];
my
$options
=
delete
$options
{
$test
};
_after_fork(
$options
);
}
);
my
$agg
=
$h
->runtests(
@tests
);
_cleanup_valgrind(\
$htoolnm
, \
$hgrind_ct
);
exit
$agg
->has_errors ? 1 : 0;