-#!./perl -w
+#!./perl
# This is written in a peculiar style, since we're trying to avoid
# most of the constructs we'll be testing for. (This comment is
# In which case, we need to stop t/TEST actually running tests, as all
# t/harness needs are its subroutines.
+# Measure the elapsed wallclock time.
+my $t0 = time();
+
# If we're doing deparse tests, ignore failures for these
my $deparse_failures;
# And skip even running these
my $deparse_skips;
+my $deparse_skip_file = '../Porting/deparse-skips.txt';
+
# directories with special sets of test switches
my %dir_to_switch =
(base => '',
'../cpan/Archive-Tar' => 1,
'../cpan/AutoLoader' => 1,
'../cpan/CPAN' => 1,
- '../cpan/Devel-PPPort' => 1,
'../cpan/Encode' => 1,
- '../cpan/ExtUtils-Command' => 1,
'../cpan/ExtUtils-Constant' => 1,
'../cpan/ExtUtils-Install' => 1,
'../cpan/ExtUtils-MakeMaker' => 1,
'../cpan/File-Fetch' => 1,
'../cpan/IPC-Cmd' => 1,
'../cpan/IPC-SysV' => 1,
- '../cpan/Locale-Codes' => 1,
'../cpan/Module-Load' => 1,
'../cpan/Module-Load-Conditional' => 1,
- '../cpan/Parse-CPAN-Meta' => 1,
'../cpan/Pod-Simple' => 1,
'../cpan/Test-Simple' => 1,
'../cpan/podlators' => 1,
'../dist/Cwd' => 1,
+ '../dist/Devel-PPPort' => 1,
'../dist/ExtUtils-ParseXS' => 1,
'../dist/Tie-File' => 1,
);
-my %temp_no_core =
- ('../cpan/B-Debug' => 1,
+my %temp_no_core = (
'../cpan/Compress-Raw-Bzip2' => 1,
'../cpan/Compress-Raw-Zlib' => 1,
'../cpan/Devel-PPPort' => 1,
'../cpan/IO-Compress' => 1,
'../cpan/MIME-Base64' => 1,
'../cpan/parent' => 1,
- '../cpan/Parse-CPAN-Meta' => 1,
'../cpan/Pod-Simple' => 1,
'../cpan/podlators' => 1,
'../cpan/Test-Simple' => 1,
'../cpan/Tie-RefHash' => 1,
'../cpan/Unicode-Collate' => 1,
- '../cpan/Unicode-Normalize' => 1,
+ '../dist/Unicode-Normalize' => 1,
);
# delete env vars that may influence the results
# but allow override via *_TEST env var if wanted
# (e.g. PERL5OPT_TEST=-d:NYTProf)
my @bad_env_vars = qw(
- PERL5LIB PERLLIB PERL5OPT
+ PERL5LIB PERLLIB PERL5OPT PERL_UNICODE
PERL_YAML_BACKEND PERL_JSON_BACKEND
);
'.svn' => 1,
);
+
+if ($::do_nothing) {
+ return 1;
+}
+
$| = 1;
# for testing TEST only
#BEGIN { require '../lib/strict.pm'; "strict"->import() };
#BEGIN { require '../lib/warnings.pm'; "warnings"->import() };
-# allow setting -options from env
-unshift @ARGV, split /;/, $ENV{TEST_OPTS} if $ENV{TEST_OPTS};
+my $OS = $ENV{FAKE_OS} || $^O;
+
+my $is_vms = $OS eq "VMS";
+my $is_win32 = $OS eq "MSWin32";
+my $is_os2 = $OS eq "os2";
# remove empty elements due to insertion of empty symbols via "''p1'" syntax
-@ARGV = grep($_,@ARGV) if $^O eq 'VMS';
-our $show_elapsed_time = $ENV{HARNESS_TIMER} || 0;
+@ARGV = grep($_,@ARGV) if $is_vms;
-{ # create timestamp for any reports written, so theyre corelatable
- my @dt = localtime;
- $dt[5] += 1900; $dt[4] += 1; # fix year, month
- $::tstamp = sprintf("%d-%.2d%.2d-%.2d%.2d-%.2d-$$", @dt[5,4,3,2,1,0]);
-}
+our $show_elapsed_time = $ENV{HARNESS_TIMER} || 0;
-# wrapper template for linux perf
-our %cmdwrap = (
- tool => "perf", # path
- cmd => "stat", # others possible, none tried
- rptdir => $ENV{PWD}, # typically ./t, better if absolute
- name => "", # $::tstamp prefixed implicitly
- # reps => 1 #
- opts => "", # pass-thru to perf stat: 'opts=-x,' gives CSV
- );
+my $dump_tests = 0;
# Cheesy version of Getopt::Std. We can't replace it with that, because we
# can't rely on require working.
{
+ my %opt_vars = (
+ benchmark => \$::benchmark,
+ core => \$::core,
+ v => \$::verbose,
+ torture => \$::torture,
+ utf8 => \$::with_utf8,
+ utf16 => \$::with_utf16,
+ taintwarn => \$::taintwarn,
+ dumptests => \$dump_tests,
+ );
+
my @argv = ();
foreach my $idx (0..$#ARGV) {
- push( @argv, $ARGV[$idx] ), next unless $ARGV[$idx] =~ /^-(\S+?)(?:=(.+))?$/;
- $::benchmark = 1 if $1 eq 'benchmark';
- $::core = 1 if $1 eq 'core';
- $::verbose = 1 if $1 eq 'v';
- $::torture = 1 if $1 eq 'torture';
- $::with_utf8 = 1 if $1 eq 'utf8';
- $::with_utf16 = 1 if $1 eq 'utf16';
- $::taintwarn = 1 if $1 eq 'taintwarn';
- if ($1 =~ /^deparse(,.+)?$/) {
+ my $opt;
+ if ($ARGV[$idx] =~ /^-?-(\S+)$/) {
+ $opt = $1;
+ } else {
+ push @argv, $ARGV[$idx];
+ next;
+ }
+ if (my $ref = $opt_vars{$opt}) {
+ $$ref = 1;
+ }
+ elsif ($opt =~ /^deparse(,.+)?$/) {
$::deparse = 1;
$::deparse_opts = $1;
_process_deparse_config();
}
- if ($1 eq 'lxperf') {
- die "perf only available on linux" unless $^O eq 'linux';
- $::perf = 1;
- next unless $2;
- # $2 is either Reps or : delimited $k=$v pairs
- my (@v) = split /:/, $2;
- $cmdwrap{reps} = $v[0] if @v == 1 and $v[0] =~ /^\d+$/;
- # parse as $k=$v to override %cmdwrap defaults
- m/(.+?)=(.+)/ and $cmdwrap{$1} = $2 for @v;
+ else {
+ die "Unknown option '$opt'\n";
}
}
@ARGV = @argv;
}
-if ($::do_nothing || $::do_nothing) { # set by harness b4 requiring us
- return 1;
-}
-
chdir 't' if -f 't/TEST';
if (-f 'TEST' && -f 'harness' && -d '../lib') {
@INC = '../lib';
}
-die "You need to run \"make test\" first to set things up.\n"
+die "You need to run \"make test_prep\" first to set things up.\n"
unless -e 'perl' or -e 'perl.exe' or -e 'perl.pm';
+if ($is_win32) {
+ # String eval to avoid loading File::Glob on non-miniperl.
+ # (Windows only uses this script for miniperl.)
+ my @argv;
+ if (eval '@argv = map glob, @ARGV; 1') {
+ @ARGV = @argv;
+ } else {
+ die "Failed to glob \@ARGV: $@";
+ }
+}
+
# check leakage for embedders
$ENV{PERL_DESTRUCT_LEVEL} = 2 unless exists $ENV{PERL_DESTRUCT_LEVEL};
# check existence of all symbols
# Roll your own File::Find!
our @found;
-sub _find_tests { @found = (); push @ARGV, _find_files('\.t$', $_[0]) }
+sub _find_tests { @found=(); push @ARGV, _find_files('\.t$', $_[0]) }
sub _find_files {
my($patt, @dirs) = @_;
for my $dir (@dirs) {
opendir DIR, $dir or die "Trouble opening $dir: $!";
foreach my $f (sort { $a cmp $b } readdir DIR) {
next if $skip{$f};
-
+ $dir =~ s/(?<!\^)\.dir(;1)?$//i if $is_vms; # trim .DIR extension
my $fullpath = "$dir/$f";
-
if (-d $fullpath) {
_find_files($patt, $fullpath);
} elsif ($f =~ /$patt/) {
close $script;
- my $perl = './perl';
+ my $perl = $is_win32 ? '.\perl' : './perl';
my $lib = '../lib';
my $run_dir;
my $return_dir;
}
elsif ($type eq 'perl') {
my $perl = $options->{perl};
- my $redir = $^O eq 'VMS' ? '2>&1' : '';
+ my $redir = $is_vms ? '2>&1' : '';
if ($ENV{PERL_VALGRIND}) {
my $perl_supp = $options->{return_dir} ? "$options->{return_dir}/perl.supp" : "perl.supp";
my $valgrind_exe = $ENV{VALGRIND} // 'valgrind';
if ($options->{run_dir}) {
- $Valgrind_Log = "$options->{run_dir}/$Valgrind_Log";
+ require Cwd;
+ $Valgrind_Log = Cwd::abs_path("$options->{run_dir}/$Valgrind_Log");
}
my $vg_opts = $ENV{VG_OPTS}
// "--log-file=$Valgrind_Log "
}
$perl = "$valgrind_exe $vg_opts $perl";
}
- if ($::perf) {
- my $ofile = "$cmdwrap{rptdir}/$::tstamp$cmdwrap{name}.perf";
- my $wrapper = "$cmdwrap{tool} $cmdwrap{cmd} "
- . ( defined $cmdwrap{reps} && $cmdwrap{reps} > 0
- ? "--repeat $cmdwrap{reps}" : "" )
- . " --append -o $ofile $cmdwrap{opts} -- ";
- $perl = "$wrapper $perl";
- }
my $args = "$options->{testswitch} $options->{switch} $options->{utf8}";
$cmd = $perl . _quote_args($args) . " $test $redir";
foreach (split(/\s+/,$args)) {
# In VMS protect with doublequotes because otherwise
# DCL will lowercase -- unless already doublequoted.
- $_ = q(").$_.q(") if ($^O eq 'VMS') && !/^\"/ && length($_) > 0;
+ $_ = q(").$_.q(") if $is_vms && !/^\"/ && length($_) > 0;
$argstring = $argstring . ' ' . $_;
}
return $argstring;
}
sub _tests_from_manifest {
- my ($extensions, $known_extensions) = @_;
+ my ($extensions, $known_extensions, $all) = @_;
+ s/\bCwd\b/PathTools/, s!\bList/Util\b!Scalar/List/Utils!
+ for $extensions, $known_extensions;
my %skip;
my %extensions = _populate_hash($extensions);
my %known_extensions = _populate_hash($known_extensions);
+ my %printed_skip_warning;
foreach (keys %known_extensions) {
$skip{$_} = 1 unless $extensions{$_};
}
my @results;
+ my %non_ext;
+ push @results, \%non_ext if $all;
my $mani = '../MANIFEST';
if (open(MANI, $mani)) {
while (<MANI>) {
- if (m!^((?:cpan|dist|ext)/(\S+)/+(?:[^/\s]+\.t|test\.pl)|lib/\S+?(?:\.t|test\.pl))\s!) {
+ chomp;
+ my ($file)= split /\t/, $_;
+ if ($file =~ m!^((?:cpan|dist|ext)/(\S+)/+(?:[^/\s]+\.t|test\.pl)|lib/\S+?(?:\.t|test\.pl))\z!) {
my $t = $1;
my $extension = $2;
+
+ if ( ord "A" != 65
+ && defined $extension
+ && $extension =~ m! \b (?:
+ Archive-Tar/
+ | Config-Perl-V/
+ | CPAN-Meta/
+ | CPAN-Meta-YAML/
+ | Digest-SHA/
+ | ExtUtils-MakeMaker/
+ | HTTP-Tiny/
+ | IO-Compress/
+ | JSON-PP/
+ | libnet/
+ | MIME-Base64/
+ | podlators/
+ | Pod-Simple/
+ | Pod-Checker/
+ | Digest-MD5/
+ | Test-Harness/
+ | IPC-Cmd/
+ | Encode/
+ | Socket/
+ | ExtUtils-Manifest/
+ | Module-Metadata/
+ | PerlIO-via-QuotedPrint/
+ )
+ !x)
+ {
+ print STDERR "Skipping testing of $extension on EBCDIC\n"
+ unless $printed_skip_warning{$extension}++;
+ next;
+ }
+
if (!$::core || $t =~ m!^lib/[a-z]!) {
if (defined $extension) {
$extension =~ s!/t(:?/\S+)*$!!;
$::path_to_name{$path} = $t;
}
}
+ elsif ($file=~m!/(?:test\.pl|[^/\s]+\.t)\z! and $file ne "t/test.pl") {
+ my $munged = $file;
+ next if $munged=~m!^(?:t/)?os2/! and !$is_os2;
+ next if $munged=~m!^(?:t/)?win32/! and !$is_win32;
+ next if $munged=~m!^(?:t/)?japh/! and !($::torture or $ENV{PERL_TORTURE_TEST});
+ next if $munged=~m!^(?:t/)?benchmark/! and !($::benchmark or $ENV{PERL_BENCHMARK});
+ next if $munged=~m!^(?:t/)?bigmem/! and !$ENV{PERL_TEST_MEMORY};
+ $munged =~ s!t/!! or $munged = "../$munged";
+
+ $non_ext{$munged}++;
+ }
}
close MANI;
} else {
return @results;
}
+sub dump_tests {
+ my ($ary) = @_;
+ for my $test (sort @$ary) {
+ # convert it to a path from the root of the repo
+ $test=~s!^\.\./!! or $test=~s!^!t/!;
+ print "$test\n";
+ }
+ exit(0);
+}
+
+sub filter_taint_tests {
+ my $tests = shift;
+ require Config;
+ return unless $Config::Config{taint_disabled} eq "define";
+
+ # These are test files which are known to fail with -DNO_TAINT_SUPPORT
+ # but which do not have "taint" in their name, nor have shebang lines
+ # with -t or -T in them. So we exclude them specifically instead.
+ my %known_tainter = map { $_ => 0 } (
+ '../cpan/Test-Harness/t/regression.t',
+ '../cpan/Test-Harness/t/source_handler.t',
+ '../cpan/Test-Harness/t/compat/inc-propagation.t',
+ );
+ @$tests = grep {
+ my $file = $_;
+ open my $ifh, "<", $file
+ or die "Failed to read: '$file': $!";
+ my $line = <$ifh>;
+ my $keep = $file=~/taint/ ? 0 : ($known_tainter{$file} // 1);
+ if ($line=~/^#!.*perl\s+-(\w+)/) {
+ my $switch = $1;
+ if ($switch =~ s/[Tt]//) {
+ $keep = 0;
+ }
+ }
+ $keep
+ } @$tests;
+}
+
+
unless (@ARGV) {
# base first, as TEST bails out if that can't run
# then comp, to validate that require works
# then run, to validate that -M works
# then we know we can -MTestInit for everything else, making life simpler
- foreach my $dir (qw(base comp run cmd io re opbasic op uni mro perf)) {
+
+ # NOTE that _find_tests() is recursive, unlike what test_harness uses.
+ foreach my $dir (qw(base comp run cmd io re opbasic op uni mro class perf test_pl)) {
_find_tests($dir);
}
unless ($::core) {
_find_tests('porting');
- _find_tests("lib");
+ _find_tests("lib");
}
+ _find_tests('win32') if $is_win32;
+ _find_tests('os2') if $is_os2;
# Config.pm may be broken for make minitest. And this is only a refinement
# for skipping tests on non-default builds, so it is allowed to fail.
- # What we want to to is make a list of extensions which we did not build.
+ # What we want to do is make a list of extensions which we did not build.
my $configsh = '../config.sh';
my ($extensions, $known_extensions);
if (-f $configsh) {
# something is that badly wrong.
push @ARGV, _tests_from_manifest($extensions, $known_extensions);
unless ($::core) {
- _find_tests('japh') if $::torture;
+ _find_tests('japh') if $::torture or $ENV{PERL_TORTURE_TEST};
_find_tests('benchmark') if $::benchmark or $ENV{PERL_BENCHMARK};
_find_tests('bigmem') if $ENV{PERL_TEST_MEMORY};
}
}
+@ARGV= do {
+ my @order= (
+ "test_pl",
+ "base",
+ "comp",
+ "run",
+ "cmd",
+ "io",
+ "re",
+ "opbasic",
+ "op",
+ "op/hook",
+ "uni",
+ "mro",
+ "class",
+ "lib",
+ "ext",
+ "dist",
+ "cpan",
+ "perf",
+ "porting",
+ );
+ my %order= map { $order[$_] => 1+$_ } 0..$#order;
+ my $idx= 0;
+ map {
+ $_->[0]
+ } sort {
+ $a->[3] <=> $b->[3] ||
+ $a->[1] <=> $b->[1]
+ } map {
+ my $root= /(\w+)/ ? $1 : "";
+ [ $_, $idx++, $root, $order{$root}||=0 ]
+ } @ARGV;
+};
+
+dump_tests(\@ARGV) if $dump_tests;
+
+filter_taint_tests(\@ARGV);
if ($::deparse) {
_testprogs('deparse', '', @ARGV);
my $grind_ct = 0; # count of non-empty valgrind reports
my $total_files = @tests;
my $good_files = 0;
- my $tested_files = 0;
+ my $tested_files = 0;
my $totmax = 0;
my %failed_tests;
+ my @unexpected_pass; # files where deparse-skips.txt says fail but passed
my $toolnm; # valgrind, cachegrind, perf
while (my $test = shift @tests) {
my $te = $::path_to_name{$test} . '.'
x ($dotdotdot - length($::path_to_name{$test})) .' ';
- if ($^O ne 'VMS') { # defer printing on VMS due to piping bug
+ if (!$is_vms) { # defer printing on VMS due to piping bug
print $te;
$te = '';
}
my $results = _run_test($test, $type);
- my $failure = "";
- my $next = 0; # ++ after each ok M
- my $seen_leader = 0; # set after "1..N"
- my $seen_ok = 0; # set after "ok M"
- my $trailing_leader = 0; # set after "1..N" if $seen_ok
- my $max; # set to N after "1..N"
+ my $failure;
+ my $next = 0;
+ my $seen_leader = 0;
+ my $seen_ok = 0;
+ my $trailing_leader = 0;
+ my $max;
my %todo;
while (<$results>) {
next if /^\s*$/; # skip blank lines
- if (/^1..$/ && ($^O eq 'VMS')) {
+ if (/^1..$/ && $is_vms) {
# VMS pipe bug inserts blank lines.
my $l2 = <$results>;
if ($l2 =~ /^\s*$/) {
}
}
}
+ my @junk = <$results>; # dump remaining output to prevent SIGPIPE
+ # (so far happens only on os390)
close $results;
+ undef @junk;
- if (not $failure) {
+ if (not defined $failure) {
$failure = 'FAILED--no leader found' unless $seen_leader;
}
if ($type eq 'deparse' && !$ENV{KEEP_DEPARSE_FILES}) {
unlink "./$test.dp";
}
- if (not $failure and $next != $max) {
+ if (not defined $failure and $next != $max) {
$failure="FAILED--expected $max tests, saw $next";
}
- if (not $failure and $? ) { # don't mask a test failure
+ if( !defined $failure # don't mask a test failure
+ and $? )
+ {
$failure = "FAILED--non-zero wait status: $?";
}
# Deparse? Should it have passed or failed?
if ($type eq 'deparse' && $test =~ $deparse_failures) {
if (!$failure) {
- # Wait, it didn't fail? Great news! Tell someone!
- $failure = "FAILED--all tests passed but test should have failed";
+ # Wait, it didn't fail? Great news!
+ push @unexpected_pass, $test;
} else {
# Bah, still failing. Mask it.
print "${te}skipped\n";
}
}
- if ($failure) {
+ if (defined $failure) {
print "${te}$failure\n";
$::bad_files = $::bad_files + 1;
if ($test =~ /^base/ && ! defined &DynaLoader::boot_DynaLoader) {
for my $test ( sort keys %failed_tests ) {
print "\t$test\n";
}
+
+ if (@unexpected_pass) {
+ print <<EOF;
+
+The following scripts were expected to fail under -deparse (at least
+according to $deparse_skip_file), but unexpectedly succeeded:
+EOF
+ print "\t$_\n" for sort @unexpected_pass;
+ print "\n";
+ }
+
warn <<'SHRDLU_1';
### Since not all tests were successful, you may want to run some of
### them individually and examine any diagnostic messages they produce.
### ./perl harness
### in the 't' directory since most (>=80%) of the tests succeeded.
SHRDLU_2
- if (eval {require Config; import Config; 1}) {
+ if (eval {require Config; Config->import; 1}) {
if ($::Config{usedl} && (my $p = $::Config{ldlibpthname})) {
warn <<SHRDLU_3;
### You may have to set your dynamic library search path,
}
}
}
+ printf "Elapsed: %d sec\n", time() - $t0;
my ($user,$sys,$cuser,$csys) = times;
my $tot = sprintf("u=%.2f s=%.2f cu=%.2f cs=%.2f scripts=%d tests=%d",
$user,$sys,$cuser,$csys,$tested_files,$totmax);
# relative path must account for that, ie ../../perf
# points to dir next to source tree.
require Storable;
- my $fn = "$show_elapsed_time/$::tstamp.ttimes";
+ my @dt = localtime;
+ $dt[5] += 1900; $dt[4] += 1; # fix year, month
+ my $fn = "$show_elapsed_time/".join('-', @dt[5,4,3,2,1]).".ttimes";
Storable::store({ perf => \%timings,
gather_conf_platform_info(),
total => $tot,
@platform = grep /name|cpu/, <$fh>;
chomp $_ for @platform;
}
- unshift @platform, $^O;
+ unshift @platform, $OS;
return (
conf => \%conf,
unlink _find_files('cachegrind.out.\d+$',
qw ( ../t ../cpan ../ext ../dist/ ));
}
+ elsif ($$toolnm eq 'valgrind') {
+ # Remove empty, hence non-error, output files
+ unlink grep { -z } _find_files('valgrind-current',
+ qw ( ../t ../cpan ../ext ../dist/ ));
+ }
}
# Generate regexps of known bad filenames / skips from Porting/deparse-skips.txt
-my $in;
sub _process_deparse_config {
my @deparse_failures;
my @deparse_skips;
- my $f = '../Porting/deparse-skips.txt';
+ my $f = $deparse_skip_file;
my $skips;
if (!open($skips, '<', $f)) {
return;
}
+ my $in;
while(<$skips>) {
if (/__DEPARSE_FAILURES__/) {
$in = \@deparse_failures; next;
next unless $_;
push @$in, $_;
+ warn "WARNING: $f:$.: excluded file doesn't exist: $_\n" unless -f $_;
}
for my $f (@deparse_failures, @deparse_skips) {