xref: /openbsd-src/gnu/usr.bin/perl/Porting/test-dist-modules.pl (revision f2a19305cfc49ea4d1a5feb55cd6c283c6f1e031)
1#!perl
2# this should be perl 5.8 compatible, since it will be used
3# with old perls while testing dist modules on those perls
4use strict;
5use warnings;
6use File::Temp "tempdir";
7use ExtUtils::Manifest "maniread";
8use Cwd "getcwd";
9use Getopt::Long;
10use Config;
11
12my $continue;
13my $separate;
14GetOptions("c|continue" => \$continue,
15           "s|separate" => \$separate,
16           "h|help"     => \&usage)
17  or die "Unknown options\n";
18
19$|++;
20
21-f "Configure"
22  or die "Expected to be run from a perl checkout";
23
24my $github_ci = $ENV{'GITHUB_SHA'} ? 1 : 0;
25
26my $manifest = maniread();
27my @failures = ();
28
29my @config;
30my $install_path;
31if ($separate) {
32    # require EU::MM 6.31 or later
33    my $install_base = tempdir( CLEANUP => 1 );
34    push @config, "INSTALL_BASE=$install_base";
35    $ENV{PERL5LIB} .= $Config{path_sep} if $ENV{PERL5LIB};
36    $ENV{PERL5LIB} .= join $Config{path_sep},
37      "$install_base/lib/perl5/$Config{archname}",
38      "$install_base/lib/perl5";
39}
40
41my %dist_config = (
42    # these are defined by the modules as distributed on CPAN
43    # I don't know why their Makefile.PLs aren't in core
44    "threads"        => [ "DEFINE=-DHAS_PPPORT_H" ],
45    "threads-shared" => [ "DEFINE=-DHAS_PPPORT_H" ],
46   );
47
48my $start = getcwd()
49  or die "Cannot fetch current directory: $!\n";
50
51# get ppport.h
52my $pppdir = test_dist("Devel-PPPort");
53
54if (@failures) {
55    if ($github_ci) {
56        # GitHub may show STDERR before STDOUT.. despite autoflush
57        # being enabled.. Make sure it detects the 'endgroup' before
58        # the `die` statement.
59        print STDERR "::endgroup::\n";
60    }
61    die "Devel-PPPort failed, aborting other tests.\n";
62}
63
64my $pppfile = "$pppdir/ppport.h";
65
66-f $pppfile
67  or die "No ppport.h found in $pppdir\n";
68
69# Devel-PPPort is manually processed before anything else to ensure we
70# have an up to date ppport.h
71my @dists = @ARGV;
72if (@dists) {
73    for my $dist (@dists) {
74        -d "dist/$dist" or die "dist/$dist not a directory\n";
75    }
76}
77else {
78    opendir my $distdir, "dist"
79      or die "Cannot opendir 'dist': $!\n";
80    @dists = sort { lc $a cmp lc $b } grep { /^\w/ && $_ ne "Devel-PPPort" } readdir $distdir;
81    closedir $distdir;
82}
83
84# These may end up being included if their problems are resolved
85{
86    # https://github.com/Perl/version.pm claims CPAN is upstream
87    @dists = grep { $_ ne "version" } @dists;
88
89    # Safe is tied pretty heavily to core
90    # in any case it didn't seem simple to fix
91    @dists = grep { $_ ne "Safe" } @dists;
92}
93
94for my $dist (@dists) {
95    test_dist($dist);
96}
97
98if (@failures) {
99    if ($github_ci) {
100        # GitHub may show STDERR before STDOUT.. despite autoflush
101        # being enabled.. Make sure it detects the 'endgroup' before
102        # the `die` statement.
103        print STDERR "::endgroup::\n";
104    }
105    my $msg = join("\n", map { "\t'$_->[0]' failed at $_->[1]" } @failures);
106    die "Following dists had failures:\n$msg\n";
107}
108
109sub test_dist {
110    my ($name) = @_;
111
112    print "::group::Testing $name\n" if $github_ci;
113    print "*** Testing $name ***\n";
114    my $dir = tempdir( CLEANUP => 1);
115    run("cp", "-a", "dist/$name/.", "$dir/.")
116      or die "Cannot copy dist files to working directory\n";
117    chdir $dir
118      or die "Cannot chdir to dist working directory '$dir': $!\n";
119    if ($pppfile) {
120        run("cp", $pppfile, ".")
121          or die "Cannot copy $pppfile to .\n";
122    }
123    if ($name eq "IO" || $name eq "threads" || $name eq "threads-shared") {
124        write_testpl();
125    }
126    if ($name eq "threads" || $name eq "threads-shared") {
127        write_threads_h();
128    }
129    if ($name eq "threads-shared") {
130        write_shared_h();
131    }
132    unless (-f "Makefile.PL") {
133        print "  Creating Makefile.PL for $name\n";
134        my $key = "ABSTRACT_FROM";
135        my @parts = split /-/, $name;
136        my $last = $parts[-1];
137        my $module = join "::", @parts;
138        my $fromname;
139        for my $check ("$last.pm", join("/", "lib", @parts) . ".pm") {
140            if (-f $check) {
141                $fromname = $check;
142                last;
143            }
144        }
145        $fromname
146          or die "Cannot find ABSTRACT_FROM for $name\n";
147        my $value = $fromname;
148        open my $fh, ">", "Makefile.PL"
149          or die "Cannot create Makefile.PL: $!\n";
150        # adapted from make_ext.pl
151        printf $fh <<'EOM', $module, $fromname, $key, $value;
152use strict;
153use ExtUtils::MakeMaker;
154
155# This is what the .PL extracts to. Not the ultimate file that is installed.
156# (ie Win32 runs pl2bat after this)
157
158# Doing this here avoids all sort of quoting issues that would come from
159# attempting to write out perl source with literals to generate the arrays and
160# hash.
161my @temps = 'Makefile.PL';
162foreach (glob('scripts/pod*.PL')) {
163    # The various pod*.PL extractors change directory. Doing that with relative
164    # paths in @INC breaks. It seems the lesser of two evils to copy (to avoid)
165    # the chdir doing anything, than to attempt to convert lib paths to
166    # absolute, and potentially run into problems with quoting special
167    # characters in the path to our build dir (such as spaces)
168    require File::Copy;
169
170    my $temp = $_;
171    $temp =~ s!scripts/!!;
172    File::Copy::copy($_, $temp) or die "Can't copy $temp to $_: $!";
173    push @temps, $temp;
174}
175
176my $script_ext = $^O eq 'VMS' ? '.com' : '';
177my %%pod_scripts;
178foreach (glob('pod*.PL')) {
179    my $script = $_;
180    s/.PL$/$script_ext/i;
181    $pod_scripts{$script} = $_;
182}
183my @exe_files = values %%pod_scripts;
184
185WriteMakefile(
186    NAME          => '%s',
187    VERSION_FROM  => '%s',
188    %-13s => '%s',
189    realclean     => { FILES => "@temps" },
190    (%%pod_scripts ? (
191        PL_FILES  => \%%pod_scripts,
192        EXE_FILES => \@exe_files,
193        clean     => { FILES => "@exe_files" },
194    ) : ()),
195);
196
197EOM
198        close $fh;
199    }
200
201    my $verbose = $github_ci && $ENV{'RUNNER_DEBUG'} ? 1 : 0;
202    my $failed = "";
203    my @my_config = @config;
204    if (my $cfg = $dist_config{$name}) {
205        push @my_config, @$cfg;
206    }
207    if (!run($^X, "Makefile.PL", @my_config)) {
208        $failed = "Makefile.PL";
209        die "$name: Makefile.PL failed\n" unless $continue;
210    }
211    elsif (!run("make", "test", "TEST_VERBOSE=$verbose")) {
212        $failed = "make test";
213        die "$name: make test failed\n" unless $continue;
214    }
215    elsif (!run("make", "install")) {
216        $failed = "make install";
217        die "$name: make install failed\n" unless $continue;
218    }
219
220    chdir $start
221      or die "Cannot return to $start: $!\n";
222
223    if ($github_ci) {
224        print "::endgroup::\n";
225    }
226    if ($continue && $failed) {
227        print "::error ::$name failed at $failed\n" if $github_ci;
228        push @failures, [ $name, $failed ];
229    }
230
231    $dir;
232}
233
234# IO, threads and threads-shared use the blead t/test.pl when tested in core
235# and bundle their own test.pl when distributed on CPAN.
236# The test.pl source below is from the IO distribution but so far seems sufficient
237# for threads and threads-shared.
238sub write_testpl {
239    _write_from_data("t/test.pl");
240}
241
242# threads and threads-shared bundle this file, which isn't needed in core
243sub write_threads_h {
244    _write_from_data("threads.h");
245}
246
247# threads-shared bundles this file, which isn't needed in core
248sub write_shared_h {
249    _write_from_data("shared.h");
250}
251
252# file data read from <DATA>
253my %file_data;
254
255sub _write_from_data {
256    my ($want_name) = @_;
257
258    unless (keys %file_data) {
259        my $name;
260        while (<DATA>) {
261            if (/^-- (\S+) --/) {
262                $name = $1;
263            }
264            else {
265                $file_data{$name} .= $_;
266            }
267        }
268        close DATA;
269    }
270
271    my $data = $file_data{$want_name} or die "No data found for $want_name";
272    open my $fh, ">", $want_name
273      or die "Cannot create $want_name: $!\n";
274    print $fh $data;
275    close $fh
276      or die "Cannot close $want_name: $!\n";
277}
278
279sub run {
280    my (@cmd) = @_;
281
282    print "\$ @cmd\n";
283    my $result = system(@cmd);
284    if ($result < 0) {
285        print "Failed: $!\n";
286    }
287    elsif ($result) {
288        printf "Failed: %d (%#x)\n", $result, $?;
289    }
290    return $result == 0;
291}
292
293sub usage {
294    print <<EOS;
295Usage: $^X $0 [options] [distnames]
296 -c | -continue
297     Continue processing after failures
298     Devel::PPPort must successfully build to continue.
299 -s | -separate
300     Install to a work path, not to perl's site_perl.
301 -h | -help
302     Display this message.
303
304Optional distnames should be names of the distributions under dist/ to
305test.  If omitted all of the distributions under dist/ are tested.
306Devel-PPPort is always tested.
307
308Test all of the distributions, stop on the first failure:
309
310   $^X $0 -s
311
312Test the various threads distributions, continue on failure:
313
314   $^X $0 -s -c threads threads-shared Thread-Queue Thread-Semaphore
315EOS
316}
317
318__DATA__
319-- t/test.pl --
320#
321# t/test.pl - most of Test::More functionality without the fuss
322
323
324# NOTE:
325#
326# Increment ($x++) has a certain amount of cleverness for things like
327#
328#   $x = 'zz';
329#   $x++; # $x eq 'aaa';
330#
331# stands more chance of breaking than just a simple
332#
333#   $x = $x + 1
334#
335# In this file, we use the latter "Baby Perl" approach, and increment
336# will be worked over by t/op/inc.t
337
338$Level = 1;
339my $test = 1;
340my $planned;
341my $noplan;
342my $Perl;       # Safer version of $^X set by which_perl()
343
344$TODO = 0;
345$NO_ENDING = 0;
346
347# Use this instead of print to avoid interference while testing globals.
348sub _print {
349    local($\, $", $,) = (undef, ' ', '');
350    print STDOUT @_;
351}
352
353sub _print_stderr {
354    local($\, $", $,) = (undef, ' ', '');
355    print STDERR @_;
356}
357
358sub plan {
359    my $n;
360    if (@_ == 1) {
361        $n = shift;
362        if ($n eq 'no_plan') {
363          undef $n;
364          $noplan = 1;
365        }
366    } else {
367        my %plan = @_;
368        $n = $plan{tests};
369    }
370    _print "1..$n\n" unless $noplan;
371    $planned = $n;
372}
373
374END {
375    my $ran = $test - 1;
376    if (!$NO_ENDING) {
377        if (defined $planned && $planned != $ran) {
378            _print_stderr
379                "# Looks like you planned $planned tests but ran $ran.\n";
380        } elsif ($noplan) {
381            _print "1..$ran\n";
382        }
383    }
384}
385
386# Use this instead of "print STDERR" when outputing failure diagnostic
387# messages
388sub _diag {
389    return unless @_;
390    my @mess = map { /^#/ ? "$_\n" : "# $_\n" }
391               map { split /\n/ } @_;
392    $TODO ? _print(@mess) : _print_stderr(@mess);
393}
394
395sub diag {
396    _diag(@_);
397}
398
399sub skip_all {
400    if (@_) {
401        _print "1..0 # Skip @_\n";
402    } else {
403        _print "1..0\n";
404    }
405    exit(0);
406}
407
408sub _ok {
409    my ($pass, $where, $name, @mess) = @_;
410    # Do not try to microoptimize by factoring out the "not ".
411    # VMS will avenge.
412    my $out;
413    if ($name) {
414        # escape out '#' or it will interfere with '# skip' and such
415        $name =~ s/#/\\#/g;
416        $out = $pass ? "ok $test - $name" : "not ok $test - $name";
417    } else {
418        $out = $pass ? "ok $test" : "not ok $test";
419    }
420
421    $out .= " # TODO $TODO" if $TODO;
422    _print "$out\n";
423
424    unless ($pass) {
425        _diag "# Failed $where\n";
426    }
427
428    # Ensure that the message is properly escaped.
429    _diag @mess;
430
431    $test = $test + 1; # don't use ++
432
433    return $pass;
434}
435
436sub _where {
437    my @caller = caller($Level);
438    return "at $caller[1] line $caller[2]";
439}
440
441# DON'T use this for matches. Use like() instead.
442sub ok ($@) {
443    my ($pass, $name, @mess) = @_;
444    _ok($pass, _where(), $name, @mess);
445}
446
447sub _q {
448    my $x = shift;
449    return 'undef' unless defined $x;
450    my $q = $x;
451    $q =~ s/\\/\\\\/g;
452    $q =~ s/'/\\'/g;
453    return "'$q'";
454}
455
456sub _qq {
457    my $x = shift;
458    return defined $x ? '"' . display ($x) . '"' : 'undef';
459};
460
461# keys are the codes \n etc map to, values are 2 char strings such as \n
462my %backslash_escape;
463foreach my $x (split //, 'nrtfa\\\'"') {
464    $backslash_escape{ord eval "\"\\$x\""} = "\\$x";
465}
466# A way to display scalars containing control characters and Unicode.
467# Trying to avoid setting $_, or relying on local $_ to work.
468sub display {
469    my @result;
470    foreach my $x (@_) {
471        if (defined $x and not ref $x) {
472            my $y = '';
473            foreach my $c (unpack("U*", $x)) {
474                if ($c > 255) {
475                    $y .= sprintf "\\x{%x}", $c;
476                } elsif ($backslash_escape{$c}) {
477                    $y .= $backslash_escape{$c};
478                } else {
479                    my $z = chr $c; # Maybe we can get away with a literal...
480                    $z = sprintf "\\%03o", $c if $z =~ /[[:^print:]]/;
481                    $y .= $z;
482                }
483            }
484            $x = $y;
485        }
486        return $x unless wantarray;
487        push @result, $x;
488    }
489    return @result;
490}
491
492sub is ($$@) {
493    my ($got, $expected, $name, @mess) = @_;
494
495    my $pass;
496    if( !defined $got || !defined $expected ) {
497        # undef only matches undef
498        $pass = !defined $got && !defined $expected;
499    }
500    else {
501        $pass = $got eq $expected;
502    }
503
504    unless ($pass) {
505        unshift(@mess, "#      got "._q($got)."\n",
506                       "# expected "._q($expected)."\n");
507    }
508    _ok($pass, _where(), $name, @mess);
509}
510
511sub isnt ($$@) {
512    my ($got, $isnt, $name, @mess) = @_;
513
514    my $pass;
515    if( !defined $got || !defined $isnt ) {
516        # undef only matches undef
517        $pass = defined $got || defined $isnt;
518    }
519    else {
520        $pass = $got ne $isnt;
521    }
522
523    unless( $pass ) {
524        unshift(@mess, "# it should not be "._q($got)."\n",
525                       "# but it is.\n");
526    }
527    _ok($pass, _where(), $name, @mess);
528}
529
530sub cmp_ok ($$$@) {
531    my($got, $type, $expected, $name, @mess) = @_;
532
533    my $pass;
534    {
535        local $^W = 0;
536        local($@,$!);   # don't interfere with $@
537                        # eval() sometimes resets $!
538        $pass = eval "\$got $type \$expected";
539    }
540    unless ($pass) {
541        # It seems Irix long doubles can have 2147483648 and 2147483648
542        # that stringify to the same thing but are acutally numerically
543        # different. Display the numbers if $type isn't a string operator,
544        # and the numbers are stringwise the same.
545        # (all string operators have alphabetic names, so tr/a-z// is true)
546        # This will also show numbers for some uneeded cases, but will
547        # definately be helpful for things such as == and <= that fail
548        if ($got eq $expected and $type !~ tr/a-z//) {
549            unshift @mess, "# $got - $expected = " . ($got - $expected) . "\n";
550        }
551        unshift(@mess, "#      got "._q($got)."\n",
552                       "# expected $type "._q($expected)."\n");
553    }
554    _ok($pass, _where(), $name, @mess);
555}
556
557# Check that $got is within $range of $expected
558# if $range is 0, then check it's exact
559# else if $expected is 0, then $range is an absolute value
560# otherwise $range is a fractional error.
561# Here $range must be numeric, >= 0
562# Non numeric ranges might be a useful future extension. (eg %)
563sub within ($$$@) {
564    my ($got, $expected, $range, $name, @mess) = @_;
565    my $pass;
566    if (!defined $got or !defined $expected or !defined $range) {
567        # This is a fail, but doesn't need extra diagnostics
568    } elsif ($got !~ tr/0-9// or $expected !~ tr/0-9// or $range !~ tr/0-9//) {
569        # This is a fail
570        unshift @mess, "# got, expected and range must be numeric\n";
571    } elsif ($range < 0) {
572        # This is also a fail
573        unshift @mess, "# range must not be negative\n";
574    } elsif ($range == 0) {
575        # Within 0 is ==
576        $pass = $got == $expected;
577    } elsif ($expected == 0) {
578        # If expected is 0, treat range as absolute
579        $pass = ($got <= $range) && ($got >= - $range);
580    } else {
581        my $diff = $got - $expected;
582        $pass = abs ($diff / $expected) < $range;
583    }
584    unless ($pass) {
585        if ($got eq $expected) {
586            unshift @mess, "# $got - $expected = " . ($got - $expected) . "\n";
587        }
588        unshift@mess, "#      got "._q($got)."\n",
589                      "# expected "._q($expected)." (within "._q($range).")\n";
590    }
591    _ok($pass, _where(), $name, @mess);
592}
593
594# Note: this isn't quite as fancy as Test::More::like().
595
596sub like   ($$@) { like_yn (0,@_) }; # 0 for -
597sub unlike ($$@) { like_yn (1,@_) }; # 1 for un-
598
599sub like_yn ($$$@) {
600    my ($flip, $got, $expected, $name, @mess) = @_;
601    my $pass;
602    $pass = $got =~ /$expected/ if !$flip;
603    $pass = $got !~ /$expected/ if $flip;
604    unless ($pass) {
605        unshift(@mess, "#      got '$got'\n",
606                $flip
607                ? "# expected !~ /$expected/\n" : "# expected /$expected/\n");
608    }
609    local $Level = $Level + 1;
610    _ok($pass, _where(), $name, @mess);
611}
612
613sub pass {
614    _ok(1, '', @_);
615}
616
617sub fail {
618    _ok(0, _where(), @_);
619}
620
621sub curr_test {
622    $test = shift if @_;
623    return $test;
624}
625
626sub next_test {
627  my $retval = $test;
628  $test = $test + 1; # don't use ++
629  $retval;
630}
631
632# Note: can't pass multipart messages since we try to
633# be compatible with Test::More::skip().
634sub skip {
635    my $why = shift;
636    my $n    = @_ ? shift : 1;
637    for (1..$n) {
638        _print "ok $test # skip $why\n";
639        $test = $test + 1;
640    }
641    local $^W = 0;
642    last SKIP;
643}
644
645sub todo_skip {
646    my $why = shift;
647    my $n   = @_ ? shift : 1;
648
649    for (1..$n) {
650        _print "not ok $test # TODO & SKIP $why\n";
651        $test = $test + 1;
652    }
653    local $^W = 0;
654    last TODO;
655}
656
657sub eq_array {
658    my ($ra, $rb) = @_;
659    return 0 unless $#$ra == $#$rb;
660    for my $i (0..$#$ra) {
661        next     if !defined $ra->[$i] && !defined $rb->[$i];
662        return 0 if !defined $ra->[$i];
663        return 0 if !defined $rb->[$i];
664        return 0 unless $ra->[$i] eq $rb->[$i];
665    }
666    return 1;
667}
668
669sub eq_hash {
670  my ($orig, $suspect) = @_;
671  my $fail;
672  while (my ($key, $value) = each %$suspect) {
673    # Force a hash recompute if this perl's internals can cache the hash key.
674    $key = "" . $key;
675    if (exists $orig->{$key}) {
676      if ($orig->{$key} ne $value) {
677        _print "# key ", _qq($key), " was ", _qq($orig->{$key}),
678                     " now ", _qq($value), "\n";
679        $fail = 1;
680      }
681    } else {
682      _print "# key ", _qq($key), " is ", _qq($value),
683                   ", not in original.\n";
684      $fail = 1;
685    }
686  }
687  foreach (keys %$orig) {
688    # Force a hash recompute if this perl's internals can cache the hash key.
689    $_ = "" . $_;
690    next if (exists $suspect->{$_});
691    _print "# key ", _qq($_), " was ", _qq($orig->{$_}), " now missing.\n";
692    $fail = 1;
693  }
694  !$fail;
695}
696
697sub require_ok ($) {
698    my ($require) = @_;
699    eval <<REQUIRE_OK;
700require $require;
701REQUIRE_OK
702    _ok(!$@, _where(), "require $require");
703}
704
705sub use_ok ($) {
706    my ($use) = @_;
707    eval <<USE_OK;
708use $use;
709USE_OK
710    _ok(!$@, _where(), "use $use");
711}
712
713# runperl - Runs a separate perl interpreter.
714# Arguments :
715#   switches => [ command-line switches ]
716#   nolib    => 1 # don't use -I../lib (included by default)
717#   prog     => one-liner (avoid quotes)
718#   progs    => [ multi-liner (avoid quotes) ]
719#   progfile => perl script
720#   stdin    => string to feed the stdin
721#   stderr   => redirect stderr to stdout
722#   args     => [ command-line arguments to the perl program ]
723#   verbose  => print the command line
724
725my $is_mswin    = $^O eq 'MSWin32';
726my $is_netware  = $^O eq 'NetWare';
727my $is_macos    = $^O eq 'MacOS';
728my $is_vms      = $^O eq 'VMS';
729my $is_cygwin   = $^O eq 'cygwin';
730
731sub _quote_args {
732    my ($runperl, $args) = @_;
733
734    foreach (@$args) {
735        # In VMS protect with doublequotes because otherwise
736        # DCL will lowercase -- unless already doublequoted.
737       $_ = q(").$_.q(") if $is_vms && !/^\"/ && length($_) > 0;
738        $$runperl .= ' ' . $_;
739    }
740}
741
742sub _create_runperl { # Create the string to qx in runperl().
743    my %args = @_;
744    my $runperl = which_perl();
745    if ($runperl =~ m/\s/) {
746        $runperl = qq{"$runperl"};
747    }
748    #- this allows, for example, to set PERL_RUNPERL_DEBUG=/usr/bin/valgrind
749    if ($ENV{PERL_RUNPERL_DEBUG}) {
750        $runperl = "$ENV{PERL_RUNPERL_DEBUG} $runperl";
751    }
752    unless ($args{nolib}) {
753        if ($is_macos) {
754            $runperl .= ' -I::lib';
755            # Use UNIX style error messages instead of MPW style.
756            $runperl .= ' -MMac::err=unix' if $args{stderr};
757        }
758        else {
759            $runperl .= ' "-I../lib"'; # doublequotes because of VMS
760        }
761    }
762    if ($args{switches}) {
763        local $Level = 2;
764        die "test.pl:runperl(): 'switches' must be an ARRAYREF " . _where()
765            unless ref $args{switches} eq "ARRAY";
766        _quote_args(\$runperl, $args{switches});
767    }
768    if (defined $args{prog}) {
769        die "test.pl:runperl(): both 'prog' and 'progs' cannot be used " . _where()
770            if defined $args{progs};
771        $args{progs} = [$args{prog}]
772    }
773    if (defined $args{progs}) {
774        die "test.pl:runperl(): 'progs' must be an ARRAYREF " . _where()
775            unless ref $args{progs} eq "ARRAY";
776        foreach my $prog (@{$args{progs}}) {
777            if ($is_mswin || $is_netware || $is_vms) {
778                $runperl .= qq ( -e "$prog" );
779            }
780            else {
781                $runperl .= qq ( -e '$prog' );
782            }
783        }
784    } elsif (defined $args{progfile}) {
785        $runperl .= qq( "$args{progfile}");
786    } else {
787        # You probaby didn't want to be sucking in from the upstream stdin
788        die "test.pl:runperl(): none of prog, progs, progfile, args, "
789            . " switches or stdin specified"
790            unless defined $args{args} or defined $args{switches}
791                or defined $args{stdin};
792    }
793    if (defined $args{stdin}) {
794        # so we don't try to put literal newlines and crs onto the
795        # command line.
796        $args{stdin} =~ s/\n/\\n/g;
797        $args{stdin} =~ s/\r/\\r/g;
798
799        if ($is_mswin || $is_netware || $is_vms) {
800            $runperl = qq{$Perl -e "print qq(} .
801                $args{stdin} . q{)" | } . $runperl;
802        }
803        elsif ($is_macos) {
804            # MacOS can only do two processes under MPW at once;
805            # the test itself is one; we can't do two more, so
806            # write to temp file
807            my $stdin = qq{$Perl -e 'print qq(} . $args{stdin} . qq{)' > teststdin; };
808            if ($args{verbose}) {
809                my $stdindisplay = $stdin;
810                $stdindisplay =~ s/\n/\n\#/g;
811                _print_stderr "# $stdindisplay\n";
812            }
813            `$stdin`;
814            $runperl .= q{ < teststdin };
815        }
816        else {
817            $runperl = qq{$Perl -e 'print qq(} .
818                $args{stdin} . q{)' | } . $runperl;
819        }
820    }
821    if (defined $args{args}) {
822        _quote_args(\$runperl, $args{args});
823    }
824    $runperl .= ' 2>&1'          if  $args{stderr} && !$is_macos;
825    $runperl .= " \xB3 Dev:Null" if !$args{stderr} &&  $is_macos;
826    if ($args{verbose}) {
827        my $runperldisplay = $runperl;
828        $runperldisplay =~ s/\n/\n\#/g;
829        _print_stderr "# $runperldisplay\n";
830    }
831    return $runperl;
832}
833
834sub runperl {
835    die "test.pl:runperl() does not take a hashref"
836        if ref $_[0] and ref $_[0] eq 'HASH';
837    my $runperl = &_create_runperl;
838    my $result;
839
840    my $tainted = ${^TAINT};
841    my %args = @_;
842    exists $args{switches} && grep m/^-T$/, @{$args{switches}} and $tainted = $tainted + 1;
843
844    if ($tainted) {
845        # We will assume that if you're running under -T, you really mean to
846        # run a fresh perl, so we'll brute force launder everything for you
847        my $sep;
848
849        if (! eval 'require Config; 1') {
850            warn "test.pl had problems loading Config: $@";
851            $sep = ':';
852        } else {
853            $sep = $Config::Config{path_sep};
854        }
855
856        my @keys = grep {exists $ENV{$_}} qw(CDPATH IFS ENV BASH_ENV);
857        local @ENV{@keys} = ();
858        # Untaint, plus take out . and empty string:
859        local $ENV{'DCL$PATH'} = $1 if $is_vms && ($ENV{'DCL$PATH'} =~ /(.*)/s);
860        $ENV{PATH} =~ /(.*)/s;
861        local $ENV{PATH} =
862            join $sep, grep { $_ ne "" and $_ ne "." and -d $_ and
863                ($is_mswin or $is_vms or !(stat && (stat _)[2]&0022)) }
864                    split quotemeta ($sep), $1;
865        $ENV{PATH} .= "$sep/bin" if $is_cygwin;  # Must have /bin under Cygwin
866
867        $runperl =~ /(.*)/s;
868        $runperl = $1;
869
870        $result = `$runperl`;
871    } else {
872        $result = `$runperl`;
873    }
874    $result =~ s/\n\n/\n/ if $is_vms; # XXX pipes sometimes double these
875    return $result;
876}
877
878*run_perl = \&runperl; # Nice alias.
879
880sub DIE {
881    _print_stderr "# @_\n";
882    exit 1;
883}
884
885# A somewhat safer version of the sometimes wrong $^X.
886sub which_perl {
887    unless (defined $Perl) {
888        $Perl = $^X;
889
890        # VMS should have 'perl' aliased properly
891        return $Perl if $^O eq 'VMS';
892
893        my $exe;
894        if (! eval 'require Config; 1') {
895            warn "test.pl had problems loading Config: $@";
896            $exe = '';
897        } else {
898            $exe = $Config::Config{_exe};
899        }
900       $exe = '' unless defined $exe;
901
902        # This doesn't absolutize the path: beware of future chdirs().
903        # We could do File::Spec->abs2rel() but that does getcwd()s,
904        # which is a bit heavyweight to do here.
905
906        if ($Perl =~ /^perl\Q$exe\E$/i) {
907            my $perl = "perl$exe";
908            if (! eval 'require File::Spec; 1') {
909                warn "test.pl had problems loading File::Spec: $@";
910                $Perl = "./$perl";
911            } else {
912                $Perl = File::Spec->catfile(File::Spec->curdir(), $perl);
913            }
914        }
915
916        # Build up the name of the executable file from the name of
917        # the command.
918
919        if ($Perl !~ /\Q$exe\E$/i) {
920            $Perl .= $exe;
921        }
922
923        warn "which_perl: cannot find $Perl from $^X" unless -f $Perl;
924
925        # For subcommands to use.
926        $ENV{PERLEXE} = $Perl;
927    }
928    return $Perl;
929}
930
931sub unlink_all {
932    foreach my $file (@_) {
933        1 while unlink $file;
934        _print_stderr "# Couldn't unlink '$file': $!\n" if -f $file;
935    }
936}
937
938my %tmpfiles;
939END { unlink_all keys %tmpfiles }
940
941# A regexp that matches the tempfile names
942$::tempfile_regexp = 'tmp\d+[A-Z][A-Z]?';
943
944# Avoid ++, avoid ranges, avoid split //
945my @letters = qw(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z);
946sub tempfile {
947    my $count = 0;
948    do {
949        my $temp = $count;
950        my $try = "tmp$$";
951        do {
952            $try .= $letters[$temp % 26];
953            $temp = int ($temp / 26);
954        } while $temp;
955        # Need to note all the file names we allocated, as a second request may
956        # come before the first is created.
957        if (!-e $try && !$tmpfiles{$try}) {
958            # We have a winner
959            $tmpfiles{$try}++;
960            return $try;
961        }
962        $count = $count + 1;
963    } while $count < 26 * 26;
964    die "Can't find temporary file name starting 'tmp$$'";
965}
966
967# This is the temporary file for _fresh_perl
968my $tmpfile = tempfile();
969
970#
971# _fresh_perl
972#
973# The $resolve must be a subref that tests the first argument
974# for success, or returns the definition of success (e.g. the
975# expected scalar) if given no arguments.
976#
977
978sub _fresh_perl {
979    my($prog, $resolve, $runperl_args, $name) = @_;
980
981    $runperl_args ||= {};
982    $runperl_args->{progfile} = $tmpfile;
983    $runperl_args->{stderr} = 1;
984
985    open TEST, ">$tmpfile" or die "Cannot open $tmpfile: $!";
986
987    # VMS adjustments
988    if( $^O eq 'VMS' ) {
989        $prog =~ s#/dev/null#NL:#;
990
991        # VMS file locking
992        $prog =~ s{if \(-e _ and -f _ and -r _\)}
993                  {if (-e _ and -f _)}
994    }
995
996    print TEST $prog;
997    close TEST or die "Cannot close $tmpfile: $!";
998
999    my $results = runperl(%$runperl_args);
1000    my $status = $?;
1001
1002    # Clean up the results into something a bit more predictable.
1003    $results =~ s/\n+$//;
1004    $results =~ s/at\s+$::tempfile_regexp\s+line/at - line/g;
1005    $results =~ s/of\s+$::tempfile_regexp\s+aborted/of - aborted/g;
1006
1007    # bison says 'parse error' instead of 'syntax error',
1008    # various yaccs may or may not capitalize 'syntax'.
1009    $results =~ s/^(syntax|parse) error/syntax error/mig;
1010
1011    if ($^O eq 'VMS') {
1012        # some tests will trigger VMS messages that won't be expected
1013        $results =~ s/\n?%[A-Z]+-[SIWEF]-[A-Z]+,.*//;
1014
1015        # pipes double these sometimes
1016        $results =~ s/\n\n/\n/g;
1017    }
1018
1019    my $pass = $resolve->($results);
1020    unless ($pass) {
1021        _diag "# PROG: \n$prog\n";
1022        _diag "# EXPECTED:\n", $resolve->(), "\n";
1023        _diag "# GOT:\n$results\n";
1024        _diag "# STATUS: $status\n";
1025    }
1026
1027    # Use the first line of the program as a name if none was given
1028    unless( $name ) {
1029        ($first_line, $name) = $prog =~ /^((.{1,50}).*)/;
1030        $name .= '...' if length $first_line > length $name;
1031    }
1032
1033    _ok($pass, _where(), "fresh_perl - $name");
1034}
1035
1036#
1037# fresh_perl_is
1038#
1039# Combination of run_perl() and is().
1040#
1041
1042sub fresh_perl_is {
1043    my($prog, $expected, $runperl_args, $name) = @_;
1044    local $Level = 2;
1045    _fresh_perl($prog,
1046                sub { @_ ? $_[0] eq $expected : $expected },
1047                $runperl_args, $name);
1048}
1049
1050#
1051# fresh_perl_like
1052#
1053# Combination of run_perl() and like().
1054#
1055
1056sub fresh_perl_like {
1057    my($prog, $expected, $runperl_args, $name) = @_;
1058    local $Level = 2;
1059    _fresh_perl($prog,
1060                sub { @_ ?
1061                          $_[0] =~ (ref $expected ? $expected : /$expected/) :
1062                          $expected },
1063                $runperl_args, $name);
1064}
1065
1066sub can_ok ($@) {
1067    my($proto, @methods) = @_;
1068    my $class = ref $proto || $proto;
1069
1070    unless( @methods ) {
1071        return _ok( 0, _where(), "$class->can(...)" );
1072    }
1073
1074    my @nok = ();
1075    foreach my $method (@methods) {
1076        local($!, $@);  # don't interfere with caller's $@
1077                        # eval sometimes resets $!
1078        eval { $proto->can($method) } || push @nok, $method;
1079    }
1080
1081    my $name;
1082    $name = @methods == 1 ? "$class->can('$methods[0]')"
1083                          : "$class->can(...)";
1084
1085    _ok( !@nok, _where(), $name );
1086}
1087
1088sub isa_ok ($$;$) {
1089    my($object, $class, $obj_name) = @_;
1090
1091    my $diag;
1092    $obj_name = 'The object' unless defined $obj_name;
1093    my $name = "$obj_name isa $class";
1094    if( !defined $object ) {
1095        $diag = "$obj_name isn't defined";
1096    }
1097    elsif( !ref $object ) {
1098        $diag = "$obj_name isn't a reference";
1099    }
1100    else {
1101        # We can't use UNIVERSAL::isa because we want to honor isa() overrides
1102        local($@, $!);  # eval sometimes resets $!
1103        my $rslt = eval { $object->isa($class) };
1104        if( $@ ) {
1105            if( $@ =~ /^Can't call method "isa" on unblessed reference/ ) {
1106                if( !UNIVERSAL::isa($object, $class) ) {
1107                    my $ref = ref $object;
1108                    $diag = "$obj_name isn't a '$class' it's a '$ref'";
1109                }
1110            } else {
1111                die <<WHOA;
1112WHOA! I tried to call ->isa on your object and got some weird error.
1113This should never happen.  Please contact the author immediately.
1114Here's the error.
1115$@
1116WHOA
1117            }
1118        }
1119        elsif( !$rslt ) {
1120            my $ref = ref $object;
1121            $diag = "$obj_name isn't a '$class' it's a '$ref'";
1122        }
1123    }
1124
1125    _ok( !$diag, _where(), $name );
1126}
1127
1128# Set a watchdog to timeout the entire test file
1129# NOTE:  If the test file uses 'threads', then call the watchdog() function
1130#        _AFTER_ the 'threads' module is loaded.
1131sub watchdog ($)
1132{
1133    my $timeout = shift;
1134    my $timeout_msg = 'Test process timed out - terminating';
1135
1136    my $pid_to_kill = $$;   # PID for this process
1137
1138    # Don't use a watchdog process if 'threads' is loaded -
1139    #   use a watchdog thread instead
1140    if (! $threads::threads) {
1141
1142        # On Windows and VMS, try launching a watchdog process
1143        #   using system(1, ...) (see perlport.pod)
1144        if (($^O eq 'MSWin32') || ($^O eq 'VMS')) {
1145            # On Windows, try to get the 'real' PID
1146            if ($^O eq 'MSWin32') {
1147                eval { require Win32; };
1148                if (defined(&Win32::GetCurrentProcessId)) {
1149                    $pid_to_kill = Win32::GetCurrentProcessId();
1150                }
1151            }
1152
1153            # If we still have a fake PID, we can't use this method at all
1154            return if ($pid_to_kill <= 0);
1155
1156            # Launch watchdog process
1157            my $watchdog;
1158            eval {
1159                local $SIG{'__WARN__'} = sub {
1160                    _diag("Watchdog warning: $_[0]");
1161                };
1162                my $sig = $^O eq 'VMS' ? 'TERM' : 'KILL';
1163                $watchdog = system(1, which_perl(), '-e',
1164                                                    "sleep($timeout);" .
1165                                                    "warn('# $timeout_msg\n');" .
1166                                                    "kill($sig, $pid_to_kill);");
1167            };
1168            if ($@ || ($watchdog <= 0)) {
1169                _diag('Failed to start watchdog');
1170                _diag($@) if $@;
1171                undef($watchdog);
1172                return;
1173            }
1174
1175            # Add END block to parent to terminate and
1176            #   clean up watchdog process
1177            eval "END { local \$! = 0; local \$? = 0;
1178                        wait() if kill('KILL', $watchdog); };";
1179            return;
1180        }
1181
1182        # Try using fork() to generate a watchdog process
1183        my $watchdog;
1184        eval { $watchdog = fork() };
1185        if (defined($watchdog)) {
1186            if ($watchdog) {   # Parent process
1187                # Add END block to parent to terminate and
1188                #   clean up watchdog process
1189                eval "END { local \$! = 0; local \$? = 0;
1190                            wait() if kill('KILL', $watchdog); };";
1191                return;
1192            }
1193
1194            ### Watchdog process code
1195
1196            # Load POSIX if available
1197            eval { require POSIX; };
1198
1199            # Execute the timeout
1200            sleep($timeout - 2) if ($timeout > 2);   # Workaround for perlbug #49073
1201            sleep(2);
1202
1203            # Kill test process if still running
1204            if (kill(0, $pid_to_kill)) {
1205                _diag($timeout_msg);
1206                kill('KILL', $pid_to_kill);
1207            }
1208
1209            # Don't execute END block (added at beginning of this file)
1210            $NO_ENDING = 1;
1211
1212            # Terminate ourself (i.e., the watchdog)
1213            POSIX::_exit(1) if (defined(&POSIX::_exit));
1214            exit(1);
1215        }
1216
1217        # fork() failed - fall through and try using a thread
1218    }
1219
1220    # Use a watchdog thread because either 'threads' is loaded,
1221    #   or fork() failed
1222    if (eval 'require threads; 1') {
1223        threads->create(sub {
1224                # Load POSIX if available
1225                eval { require POSIX; };
1226
1227                # Execute the timeout
1228                my $time_left = $timeout;
1229                do {
1230                    $time_left -= sleep($time_left);
1231                } while ($time_left > 0);
1232
1233                # Kill the parent (and ourself)
1234                select(STDERR); $| = 1;
1235                _diag($timeout_msg);
1236                POSIX::_exit(1) if (defined(&POSIX::_exit));
1237                my $sig = $^O eq 'VMS' ? 'TERM' : 'KILL';
1238                kill($sig, $pid_to_kill);
1239            })->detach();
1240        return;
1241    }
1242
1243    # If everything above fails, then just use an alarm timeout
1244    if (eval { alarm($timeout); 1; }) {
1245        # Load POSIX if available
1246        eval { require POSIX; };
1247
1248        # Alarm handler will do the actual 'killing'
1249        $SIG{'ALRM'} = sub {
1250            select(STDERR); $| = 1;
1251            _diag($timeout_msg);
1252            POSIX::_exit(1) if (defined(&POSIX::_exit));
1253            my $sig = $^O eq 'VMS' ? 'TERM' : 'KILL';
1254            kill($sig, $pid_to_kill);
1255        };
1256    }
1257}
1258
12591;
1260-- threads.h --
1261#ifndef _THREADS_H_
1262#define _THREADS_H_
1263
1264/* Needed for 5.8.0 */
1265#ifndef CLONEf_JOIN_IN
1266#  define CLONEf_JOIN_IN        8
1267#endif
1268#ifndef SAVEBOOL
1269#  define SAVEBOOL(a)
1270#endif
1271
1272/* Added in 5.11.x */
1273#ifndef G_WANT
1274#  define G_WANT                (128|1)
1275#endif
1276
1277/* Added in 5.24.x */
1278#ifndef PERL_TSA_RELEASE
1279#  define PERL_TSA_RELEASE(x)
1280#endif
1281#ifndef PERL_TSA_EXCLUDES
1282#  define PERL_TSA_EXCLUDES(x)
1283#endif
1284#ifndef CLANG_DIAG_IGNORE
1285#  define CLANG_DIAG_IGNORE(x)
1286#endif
1287#ifndef CLANG_DIAG_RESTORE
1288#  define CLANG_DIAG_RESTORE
1289#endif
1290
1291/* Added in 5.38 */
1292#ifndef PERL_SRAND_OVERRIDE_NEXT_PARENT
1293#  define PERL_SRAND_OVERRIDE_NEXT_PARENT()
1294#endif
1295
1296#endif
1297-- shared.h --
1298#ifndef _SHARED_H_
1299#define _SHARED_H_
1300
1301#include "ppport.h"
1302
1303#ifndef HvNAME_get
1304#  define HvNAME_get(hv)        (0 + ((XPVHV*)SvANY(hv))->xhv_name)
1305#endif
1306
1307#endif
1308