This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
test.pl: Add skip_without_dynamic_extension
[perl5.git] / t / test.pl
index 0c4c3a7..aee1d24 100644 (file)
--- a/t/test.pl
+++ b/t/test.pl
@@ -24,6 +24,10 @@ my $planned;
 my $noplan;
 my $Perl;       # Safer version of $^X set by which_perl()
 
+# This defines ASCII/UTF-8 vs EBCDIC/UTF-EBCDIC
+$::IS_ASCII  = ord 'A' ==  65;
+$::IS_EBCDIC = ord 'A' == 193;
+
 $TODO = 0;
 $NO_ENDING = 0;
 $Tests_Are_Passing = 1;
@@ -105,6 +109,16 @@ sub _comment {
            map { split /\n/ } @_;
 }
 
+sub _have_dynamic_extension {
+    my $extension = shift;
+    unless (eval {require Config; 1}) {
+       warn "test.pl had problems loading Config: $@";
+       return 1;
+    }
+    $extension =~ s!::!/!g;
+    return 1 if ($Config::Config{extensions} =~ /\b$extension\b/);
+}
+
 sub skip_all {
     if (@_) {
         _print "1..0 # Skip @_\n";
@@ -118,6 +132,74 @@ sub skip_all_if_miniperl {
     skip_all(@_) if is_miniperl();
 }
 
+sub skip_all_without_dynamic_extension {
+    my ($extension) = @_;
+    skip_all("no dynamic loading on miniperl, no $extension") if is_miniperl();
+    return if &_have_dynamic_extension;
+    skip_all("$extension was not built");
+}
+
+sub skip_all_without_perlio {
+    skip_all('no PerlIO') unless PerlIO::Layer->find('perlio');
+}
+
+sub skip_all_without_config {
+    unless (eval {require Config; 1}) {
+       warn "test.pl had problems loading Config: $@";
+       return;
+    }
+    foreach (@_) {
+       next if $Config::Config{$_};
+       my $key = $_; # Need to copy, before trying to modify.
+       $key =~ s/^use//;
+       $key =~ s/^d_//;
+       skip_all("no $key");
+    }
+}
+
+sub find_git_or_skip {
+    my ($source_dir, $reason);
+    if (-d '.git') {
+       $source_dir = '.';
+    } elsif (-l 'MANIFEST' && -l 'AUTHORS') {
+       my $where = readlink 'MANIFEST';
+       die "Can't readling MANIFEST: $!" unless defined $where;
+       die "Confusing symlink target for MANIFEST, '$where'"
+           unless $where =~ s!/MANIFEST\z!!;
+       if (-d "$where/.git") {
+           # Looks like we are in a symlink tree
+           if (exists $ENV{GIT_DIR}) {
+               diag("Found source tree at $where, but \$ENV{GIT_DIR} is $ENV{GIT_DIR}. Not changing it");
+           } else {
+               note("Found source tree at $where, setting \$ENV{GIT_DIR}");
+               $ENV{GIT_DIR} = "$where/.git";
+           }
+           $source_dir = $where;
+       }
+    }
+    if ($source_dir) {
+       my $version_string = `git --version`;
+       if (defined $version_string
+             && $version_string =~ /\Agit version (\d+\.\d+\.\d+)(.*)/) {
+           return $source_dir if eval "v$1 ge v1.5.0";
+           # If you have earlier than 1.5.0 and it works, change this test
+           $reason = "in git checkout, but git version '$1$2' too old";
+       } else {
+           $reason = "in git checkout, but cannot run git";
+       }
+    } else {
+       $reason = 'not being run from a git checkout';
+    }
+    skip_all($reason) if $_[0] && $_[0] eq 'all';
+    skip($reason, @_);
+}
+
+sub BAIL_OUT {
+    my ($reason) = @_;
+    _print("Bail out!  $reason\n");
+    exit 255;
+}
+
 sub _ok {
     my ($pass, $where, $name, @mess) = @_;
     # Do not try to microoptimize by factoring out the "not ".
@@ -143,7 +225,10 @@ sub _ok {
        note @mess; # Ensure that the message is properly escaped.
     }
     else {
-       _diag "# Failed $where\n";
+       my $msg = "# Failed test $test - ";
+       $msg.= "$name " if $name;
+       $msg .= "$where\n";
+       _diag $msg;
        _diag @mess;
     }
 
@@ -374,6 +459,13 @@ sub skip_if_miniperl {
     skip(@_) if is_miniperl();
 }
 
+sub skip_without_dynamic_extension {
+    my ($extension) = @_;
+    skip("no dynamic loading on miniperl, no $extension") if is_miniperl();
+    return if &_have_dynamic_extension;
+    skip_all("$extension was not built");
+}
+
 sub todo_skip {
     my $why = shift;
     my $n   = @_ ? shift : 1;
@@ -570,7 +662,7 @@ sub runperl {
        # run a fresh perl, so we'll brute force launder everything for you
        my $sep;
 
-       if (! eval 'require Config; 1') {
+       if (! eval {require Config; 1}) {
            warn "test.pl had problems loading Config: $@";
            $sep = ':';
        } else {
@@ -620,7 +712,7 @@ sub which_perl {
        return $Perl if $is_vms;
 
        my $exe;
-       if (! eval 'require Config; 1') {
+       if (! eval {require Config; 1}) {
            warn "test.pl had problems loading Config: $@";
            $exe = '';
        } else {
@@ -634,7 +726,7 @@ sub which_perl {
 
        if ($Perl =~ /^perl\Q$exe\E$/i) {
            my $perl = "perl$exe";
-           if (! eval 'require File::Spec; 1') {
+           if (! eval {require File::Spec; 1}) {
                warn "test.pl had problems loading File::Spec: $@";
                $Perl = "./$perl";
            } else {
@@ -713,8 +805,8 @@ sub _fresh_perl {
     # it feels like the least-worse thing is to assume that auto-vivification
     # works. At least, this is only going to be a run-time failure, so won't
     # affect tests using this file but not this function.
-    $runperl_args->{progfile} = $tmpfile;
-    $runperl_args->{stderr} = 1;
+    $runperl_args->{progfile} ||= $tmpfile;
+    $runperl_args->{stderr}     = 1 unless exists $runperl_args->{stderr};
 
     open TEST, ">$tmpfile" or die "Cannot open $tmpfile: $!";
 
@@ -809,6 +901,33 @@ sub fresh_perl_like {
 # Many tests use the same format in __DATA__ or external files to specify a
 # sequence of (fresh) tests to run, extra files they may temporarily need, and
 # what the expected output is. So have excatly one copy of the code to run that
+#
+# Each program is source code to run followed by an "EXPECT" line, followed
+# by the expected output.
+#
+# The code to run may contain (note the '# ' on each):
+#   # TODO reason for todo
+#   # SKIP reason for skip
+#   # SKIP ?code to test if this should be skipped
+#   # NAME name of the test (as with ok($ok, $name))
+#
+# The expected output may contain:
+#   OPTION list of options
+#   OPTIONS list of options
+#   PREFIX
+#     indicates that the supplied output is only a prefix to the
+#     expected output
+#
+# The possible options for OPTION may be:
+#   regex - the expected output is a regular expression
+#   random - all lines match but in any order
+#   fatal - the code will fail fatally (croak, die)
+#
+# If the actual output contains a line "SKIPPED" the test will be
+# skipped.
+#
+# If the global variable $FATAL is true then OPTION fatal is the
+# default.
 
 sub run_multiple_progs {
     my $up = shift;
@@ -852,6 +971,10 @@ sub run_multiple_progs {
                $reason{$what} = $temp;
            }
        }
+       my $name = '';
+       if ($prog =~ s/^#\s*NAME\s+(.+)\n//m) {
+           $name = $1;
+       }
 
        if ($prog =~ /--FILE--/) {
            my @files = split(/\n--FILE--\s*([^\s\n]*)\s*\n/, $prog) ;
@@ -912,6 +1035,7 @@ sub run_multiple_progs {
        # any special options? (OPTIONS foo bar zap)
        my $option_regex = 0;
        my $option_random = 0;
+       my $fatal = $FATAL;
        if ($expected =~ s/^OPTIONS? (.+)\n//) {
            foreach my $option (split(' ', $1)) {
                if ($option eq 'regex') { # allow regular expressions
@@ -920,6 +1044,9 @@ sub run_multiple_progs {
                elsif ($option eq 'random') { # all lines match, but in any order
                    $option_random = 1;
                }
+               elsif ($option eq 'fatal') { # perl should fail
+                   $fatal = 1;
+               }
                else {
                    die "$0: Unknown OPTION '$option'\n";
                }
@@ -932,28 +1059,36 @@ sub run_multiple_progs {
            print "$results\n" ;
            $ok = 1;
        }
-       elsif ($option_random) {
-           my @got = sort split "\n", $results;
-           my @expected = sort split "\n", $expected;
-
-           $ok = "@got" eq "@expected";
-       }
-       elsif ($option_regex) {
-           $ok = $results =~ /^$expected/;
-       }
-       elsif ($prefix) {
-           $ok = $results =~ /^\Q$expected/;
-       }
        else {
-           $ok = $results eq $expected;
+           if ($option_random) {
+               my @got = sort split "\n", $results;
+               my @expected = sort split "\n", $expected;
+
+               $ok = "@got" eq "@expected";
+           }
+           elsif ($option_regex) {
+               $ok = $results =~ /^$expected/;
+           }
+           elsif ($prefix) {
+               $ok = $results =~ /^\Q$expected/;
+           }
+           else {
+               $ok = $results eq $expected;
+           }
+
+           if ($ok && $fatal && !($status >> 8)) {
+               $ok = 0;
+           }
        }
 
        local $::TODO = $reason{todo};
 
        unless ($ok) {
            my $err_line = "PROG: $switch\n$prog\n" .
-                          "EXPECTED:\n$expected\n" .
-                          "GOT:\n$results\n";
+                          "EXPECTED:\n$expected\n";
+           $err_line   .= "EXIT STATUS: != 0\n" if $fatal;
+           $err_line   .= "GOT:\n$results\n";
+           $err_line   .= "EXIT STATUS: " . ($status >> 8) . "\n" if $fatal;
            if ($::TODO) {
                $err_line =~ s/^/# /mg;
                print $err_line;  # Harness can't filter it out from STDERR.
@@ -963,7 +1098,7 @@ sub run_multiple_progs {
            }
        }
 
-       ok($ok);
+       ok($ok, $name);
 
        foreach (@temps) {
            unlink $_ if $_;
@@ -997,7 +1132,7 @@ sub can_ok ($@) {
 }
 
 
-# Call $class->new( @$args ); and run the result through isa_ok.
+# Call $class->new( @$args ); and run the result through object_ok.
 # See Test::More::new_ok
 sub new_ok {
     my($class, $args, $obj_name) = @_;
@@ -1011,7 +1146,7 @@ sub new_ok {
     my $error = $@;
 
     if($ok) {
-        isa_ok($obj, $class, $object_name);
+        object_ok($obj, $class, $object_name);
     }
     else {
         ok( 0, "new() died" );
@@ -1032,20 +1167,29 @@ sub isa_ok ($$;$) {
     if( !defined $object ) {
         $diag = "$obj_name isn't defined";
     }
-    elsif( !ref $object ) {
-        $diag = "$obj_name isn't a reference";
-    }
     else {
+        my $whatami = ref $object ? 'object' : 'class';
+
         # We can't use UNIVERSAL::isa because we want to honor isa() overrides
         local($@, $!);  # eval sometimes resets $!
         my $rslt = eval { $object->isa($class) };
-        if( $@ ) {
-            if( $@ =~ /^Can't call method "isa" on unblessed reference/ ) {
+        my $error = $@;  # in case something else blows away $@
+
+        if( $error ) {
+            if( $error =~ /^Can't call method "isa" on unblessed reference/ ) {
+                # It's an unblessed reference
+                $obj_name = 'The reference' unless defined $obj_name;
                 if( !UNIVERSAL::isa($object, $class) ) {
                     my $ref = ref $object;
                     $diag = "$obj_name isn't a '$class' it's a '$ref'";
                 }
-            } else {
+            }
+            elsif( $error =~ /Can't call method "isa" without a package/ ) {
+                # It's something that can't even be a class
+                $obj_name = 'The thing' unless defined $obj_name;
+                $diag = "$obj_name isn't a class or reference";
+            }
+            else {
                 die <<WHOA;
 WHOA! I tried to call ->isa on your object and got some weird error.
 This should never happen.  Please contact the author immediately.
@@ -1055,6 +1199,7 @@ WHOA
             }
         }
         elsif( !$rslt ) {
+            $obj_name = "The $whatami" unless defined $obj_name;
             my $ref = ref $object;
             $diag = "$obj_name isn't a '$class' it's a '$ref'";
         }
@@ -1063,26 +1208,77 @@ WHOA
     _ok( !$diag, _where(), $name );
 }
 
-# This will generate a variable number of tests if passed an array of 2 or more
-# tests. Use done_testing() instead of a fixed plan.
+
+sub class_ok {
+    my($class, $isa, $class_name) = @_;
+
+    # Written so as to count as one test
+    local $Level = $Level + 1;
+    if( ref $class ) {
+        ok( 0, "$class is a refrence, not a class name" );
+    }
+    else {
+        isa_ok($class, $isa, $class_name);
+    }
+}
+
+
+sub object_ok {
+    my($obj, $isa, $obj_name) = @_;
+
+    local $Level = $Level + 1;
+    if( !ref $obj ) {
+        ok( 0, "$obj is not a reference" );
+    }
+    else {
+        isa_ok($obj, $isa, $obj_name);
+    }
+}
+
+
+# Purposefully avoiding a closure.
+sub __capture {
+    push @::__capture, join "", @_;
+}
+    
+sub capture_warnings {
+    my $code = shift;
+
+    local @::__capture;
+    local $SIG {__WARN__} = \&__capture;
+    &$code;
+    return @::__capture;
+}
+
+# This will generate a variable number of tests.
+# Use done_testing() instead of a fixed plan.
 sub warnings_like {
     my ($code, $expect, $name) = @_;
-    my @w;
-    local $SIG {__WARN__} = sub {push @w, join "", @_};
-    {
-       use warnings 'all';
-       &$code;
-    }
     local $Level = $Level + 1;
 
-    cmp_ok(scalar @w, '==', scalar @$expect, $name) if @$expect != 1;
-    while (my ($i, $e) = each @$expect) {
+    my @w = capture_warnings($code);
+
+    cmp_ok(scalar @w, '==', scalar @$expect, $name);
+    foreach my $e (@$expect) {
        if (ref $e) {
-           like($w[$i], $e, $name);
+           like(shift @w, $e, $name);
        } else {
-           is($w[$i], $e, $name);
+           is(shift @w, $e, $name);
        }
     }
+    if (@w) {
+       diag("Saw these additional warnings:");
+       diag($_) foreach @w;
+    }
+}
+
+sub _fail_excess_warnings {
+    my($expect, $got, $name) = @_;
+    local $Level = $Level + 1;
+    # This will fail, and produce diagnostics
+    is($expect, scalar @$got, $name);
+    diag("Saw these warnings:");
+    diag($_) foreach @$got;
 }
 
 sub warning_is {
@@ -1090,7 +1286,12 @@ sub warning_is {
     die sprintf "Expect must be a string or undef, not a %s reference", ref $expect
        if ref $expect;
     local $Level = $Level + 1;
-    warnings_like($code, defined $expect? [$expect] : [], $name);
+    my @w = capture_warnings($code);
+    if (@w > 1) {
+       _fail_excess_warnings(0 + defined $expect, \@w, $name);
+    } else {
+       is($w[0], $expect, $name);
+    }
 }
 
 sub warning_like {
@@ -1098,7 +1299,12 @@ sub warning_like {
     die sprintf "Expect must be a regexp object"
        unless ref $expect eq 'Regexp';
     local $Level = $Level + 1;
-    warnings_like($code, [$expect], $name);
+    my @w = capture_warnings($code);
+    if (@w > 1) {
+       _fail_excess_warnings(0 + defined $expect, \@w, $name);
+    } else {
+       like($w[0], $expect, $name);
+    }
 }
 
 # Set a watchdog to timeout the entire test file
@@ -1124,7 +1330,7 @@ sub watchdog ($;$)
 
     # Don't use a watchdog process if 'threads' is loaded -
     #   use a watchdog thread instead
-    if (!$threads_on) {
+    if (!$threads_on || $method eq "process") {
 
         # On Windows and VMS, try launching a watchdog process
         #   using system(1, ...) (see perlport.pod)
@@ -1191,6 +1397,11 @@ sub watchdog ($;$)
             if (kill(0, $pid_to_kill)) {
                 _diag($timeout_msg);
                 kill('KILL', $pid_to_kill);
+               if ($is_cygwin) {
+                   # sometimes the above isn't enough on cygwin
+                   sleep 1; # wait a little, it might have worked after all
+                   system("/bin/kill -f $pid_to_kill");
+               }
             }
 
             # Don't execute END block (added at beginning of this file)
@@ -1206,7 +1417,7 @@ sub watchdog ($;$)
 
     # Use a watchdog thread because either 'threads' is loaded,
     #   or fork() failed
-    if (eval 'require threads; 1') {
+    if (eval {require threads; 1}) {
         'threads'->create(sub {
                 # Load POSIX if available
                 eval { require POSIX; };