This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
(was Re: [PATCH perl@13600] cat2type in tests for VMS)
[perl5.git] / t / test.pl
index 029d80f..b6f4258 100644 (file)
--- a/t/test.pl
+++ b/t/test.pl
@@ -5,6 +5,9 @@
 my $test = 1;
 my $planned;
 
+$TODO = 0;
+$NO_ENDING = 0;
+
 sub plan {
     my $n;
     if (@_ == 1) {
@@ -13,39 +16,53 @@ sub plan {
        my %plan = @_;
        $n = $plan{tests}; 
     }
-    print "1..$n\n";
+    print STDOUT "1..$n\n";
     $planned = $n;
 }
 
 END {
     my $ran = $test - 1;
-    if (defined $planned && $planned != $ran) {
-       print "# Looks like you planned $planned tests but ran $ran.\n";
+    if (!$NO_ENDING && defined $planned && $planned != $ran) {
+        print STDOUT "# Looks like you planned $planned tests but ran $ran.\n";
     }
 }
 
 sub skip_all {
     if (@_) {
-       print "1..0 - @_\n";
+       print STDOUT "1..0 - @_\n";
     } else {
-       print "1..0\n";
+       print STDOUT "1..0\n";
     }
     exit(0);
 }
 
 sub _ok {
-    my ($pass, $where, @mess) = @_;
+    my ($pass, $where, $name, @mess) = @_;
     # Do not try to microoptimize by factoring out the "not ".
     # VMS will avenge.
-    if (@mess) {
-       print $pass ? "ok $test - @mess\n" : "not ok $test - @mess\n";
+    my $out;
+    if ($name) {
+        # escape out '#' or it will interfere with '# skip' and such
+        $name =~ s/#/\\#/g;
+       $out = $pass ? "ok $test - $name" : "not ok $test - $name";
     } else {
-       print $pass ? "ok $test\n" : "not ok $test\n";
+       $out = $pass ? "ok $test" : "not ok $test";
     }
+
+    $out .= " # TODO $TODO" if $TODO;
+    print STDOUT "$out\n";
+
     unless ($pass) {
-       print "# Failed $where\n";
+       print STDOUT "# Failed $where\n";
     }
+
+    # Ensure that the message is properly escaped.
+    print STDOUT map { /^#/ ? "$_\n" : "# $_\n" } 
+                 map { split /\n/ } @mess if @mess;
+
     $test++;
+
+    return $pass;
 }
 
 sub _where {
@@ -54,49 +71,92 @@ sub _where {
 }
 
 sub ok {
-    my ($pass, @mess) = @_;
-    _ok($pass, _where(), @mess);
+    my ($pass, $name, @mess) = @_;
+    _ok($pass, _where(), $name, @mess);
 }
 
-sub _expect {
-    my ($got, $pass, @mess) = @_;
-    if ($pass) {
-       ok(1, @mess);
-    } else {
-       ok(0, @mess);
+sub _q {
+    my $x = shift;
+    return 'undef' unless defined $x;
+    my $q = $x;
+    $q =~ s/\\/\\\\/;
+    $q =~ s/'/\\'/;
+    return "'$q'";
+}
+
+sub _qq {
+    my $x = shift;
+    return defined $x ? '"' . display ($x) . '"' : 'undef';
+};
+
+# keys are the codes \n etc map to, values are 2 char strings such as \n
+my %backslash_escape;
+foreach my $x (split //, 'nrtfa\\\'"') {
+    $backslash_escape{ord eval "\"\\$x\""} = "\\$x";
+}
+# A way to display scalars containing control characters and Unicode.
+# Trying to avoid setting $_, or relying on local $_ to work.
+sub display {
+    my @result;
+    foreach my $x (@_) {
+        if (defined $x and not ref $x) {
+            my $y = '';
+            foreach my $c (unpack("U*", $x)) {
+                if ($c > 255) {
+                    $y .= sprintf "\\x{%x}", $c;
+                } elsif ($backslash_escape{$c}) {
+                    $y .= $backslash_escape{$c};
+                } else {
+                    my $z = chr $c; # Maybe we can get away with a literal...
+                    $z = sprintf "\\%03o", $c if $z =~ /[[:^print:]]/;
+                    $y .= $z;
+                }
+            }
+            $x = $y;
+        }
+        return $x unless wantarray;
+        push @result, $x;
     }
-} 
+    return @result;
+}
 
 sub is {
-    my ($got, $expected, @mess) = @_;
+    my ($got, $expected, $name, @mess) = @_;
     my $pass = $got eq $expected;
     unless ($pass) {
-       unshift(@mess, "\n",
-               "#      got '$got'\n",
-               "# expected '$expected'\n");
+       unshift(@mess, "#      got "._q($got)."\n",
+                      "# expected "._q($expected)."\n");
     }
-    _expect($pass, _where(), @mess);
+    _ok($pass, _where(), $name, @mess);
+}
+
+sub isnt {
+    my ($got, $isnt, $name, @mess) = @_;
+    my $pass = $got ne $isnt;
+    unless( $pass ) {
+        unshift(@mess, "# it should not be "._q($got)."\n",
+                       "# but it is.\n");
+    }
+    _ok($pass, _where(), $name, @mess);
 }
 
 # Note: this isn't quite as fancy as Test::More::like().
 sub like {
-    my ($got, $expected, @mess) = @_;
+    my ($got, $expected, $name, @mess) = @_;
     my $pass;
     if (ref $expected eq 'Regexp') {
        $pass = $got =~ $expected;
        unless ($pass) {
-           unshift(@mess, "\n",
-                   "#      got '$got'\n");
+           unshift(@mess, "#      got '$got'\n");
        }
     } else {
        $pass = $got =~ /$expected/;
        unless ($pass) {
-           unshift(@mess, "\n",
-                   "#      got '$got'\n",
-                   "# expected /$expected/\n");
+           unshift(@mess, "#      got '$got'\n",
+                          "# expected /$expected/\n");
        }
     }
-    _expect($pass, _where(), @mess);
+    _ok($pass, _where(), $name, @mess);
 }
 
 sub pass {
@@ -107,13 +167,22 @@ sub fail {
     _ok(0, _where(), @_);
 }
 
+sub curr_test {
+    return $test;
+}
+
+sub next_test {
+    $test++
+}
+
 # Note: can't pass multipart messages since we try to
 # be compatible with Test::More::skip().
 sub skip {
-    my $mess = shift;
+    my $why = shift;
     my $n    = @_ ? shift : 1;
     for (1..$n) {
-       ok(1, "# skip:", $mess);
+        print STDOUT "ok $test # skip: $why\n";
+        $test++;
     }
     local $^W = 0;
     last SKIP;
@@ -128,12 +197,39 @@ sub eq_array {
     return 1;
 }
 
+sub eq_hash {
+  my ($orig, $suspect) = @_;
+  my $fail;
+  while (my ($key, $value) = each %$suspect) {
+    # Force a hash recompute if this perl's internals can cache the hash key.
+    $key = "" . $key;
+    if (exists $orig->{$key}) {
+      if ($orig->{$key} ne $value) {
+        print "# key ", _qq($key), " was ", _qq($orig->{$key}),
+          " now ", _qq($value), "\n";
+        $fail = 1;
+      }
+    } else {
+      print "# key ", _qq($key), " is ", _qq($value), ", not in original.\n";
+      $fail = 1;
+    }
+  }
+  foreach (keys %$orig) {
+    # Force a hash recompute if this perl's internals can cache the hash key.
+    $_ = "" . $_;
+    next if (exists $suspect->{$_});
+    print "# key ", _qq($_), " was ", _qq($orig->{$_}), " now missing.\n";
+    $fail = 1;
+  }
+  !$fail;
+}
+
 sub require_ok {
     my ($require) = @_;
     eval <<REQUIRE_OK;
 require $require;
 REQUIRE_OK
-    ok(!$@, "require $require");
+    _ok(!$@, _where(), "require $require");
 }
 
 sub use_ok {
@@ -141,7 +237,140 @@ sub use_ok {
     eval <<USE_OK;
 use $use;
 USE_OK
-    ok(!$@, "use $use");
+    _ok(!$@, _where(), "use $use");
+}
+
+# runperl - Runs a separate perl interpreter.
+# Arguments :
+#   switches => [ command-line switches ]
+#   nolib    => 1 # don't use -I../lib (included by default)
+#   prog     => one-liner (avoid quotes)
+#   progfile => perl script
+#   stdin    => string to feed the stdin
+#   stderr   => redirect stderr to stdout
+#   args     => [ command-line arguments to the perl program ]
+#   verbose  => print the command line
+
+my $is_mswin    = $^O eq 'MSWin32';
+my $is_netware  = $^O eq 'NetWare';
+my $is_macos    = $^O eq 'MacOS';
+my $is_vms      = $^O eq 'VMS';
+
+sub _quote_args {
+    my ($runperl, $args) = @_;
+
+    foreach (@$args) {
+       # In VMS protect with doublequotes because otherwise
+       # DCL will lowercase -- unless already doublequoted.
+       $_ = q(").$_.q(") if $is_vms && !/^\"/;
+       $$runperl .= ' ' . $_;
+    }
+}
+
+sub runperl {
+    my %args = @_;
+    my $runperl = $^X;
+    if ($args{switches}) {
+       _quote_args(\$runperl, $args{switches});
+    }
+    unless ($args{nolib}) {
+       if ($is_macos) {
+           $runperl .= ' -I::lib';
+           # Use UNIX style error messages instead of MPW style.
+           $runperl .= ' -MMac::err=unix' if $args{stderr};
+       }
+       else {
+           $runperl .= ' "-I../lib"'; # doublequotes because of VMS
+       }
+    }
+    if (defined $args{prog}) {
+       if ($is_mswin || $is_netware || $is_vms) {
+           $runperl .= qq( -e ") . $args{prog} . qq(");
+       }
+       else {
+           $runperl .= qq( -e ') . $args{prog} . qq(');
+       }
+    } elsif (defined $args{progfile}) {
+       $runperl .= qq( "$args{progfile}");
+    }
+    if (defined $args{stdin}) {
+        # so we don't try to put literal newlines and crs onto the
+        # command line.
+        $args{stdin} =~ s/\n/\\n/g;
+        $args{stdin} =~ s/\r/\\r/g;
+
+       if ($is_mswin || $is_netware || $is_vms) {
+           $runperl = qq{$^X -e "print qq(} .
+               $args{stdin} . q{)" | } . $runperl;
+       }
+       else {
+           $runperl = qq{$^X -e 'print qq(} .
+               $args{stdin} . q{)' | } . $runperl;
+       }
+    }
+    if (defined $args{args}) {
+       _quote_args(\$runperl, $args{args});
+    }
+    $runperl .= ' 2>&1'          if  $args{stderr} && !$is_macos;
+    $runperl .= " \xB3 Dev:Null" if !$args{stderr} &&  $is_macos;
+    if ($args{verbose}) {
+       my $runperldisplay = $runperl;
+       $runperldisplay =~ s/\n/\n\#/g;
+       print STDOUT "# $runperldisplay\n";
+    }
+    my $result = `$runperl`;
+    $result =~ s/\n\n/\n/ if $is_vms; # XXX pipes sometimes double these
+    return $result;
+}
+
+
+sub BAILOUT {
+    print STDOUT "Bail out! @_\n";
+    exit;
+}
+
+# A somewhat safer version of the sometimes wrong $^X.
+my $Perl;
+sub which_perl {
+    unless (defined $Perl) {
+       $Perl = $^X;
+       
+       my $exe;
+       eval "require Config; Config->import";
+       if ($@) {
+           warn "test.pl had problems loading Config: $@";
+           $exe = '';
+       } else {
+           $exe = $Config{_exe};
+       }
+       $exe = '' unless defined $exe;
+       
+       # This doesn't absolutize the path: beware of future chdirs().
+       # We could do File::Spec->abs2rel() but that does getcwd()s,
+       # which is a bit heavyweight to do here.
+       
+       if ($Perl =~ /^perl\Q$exe\E$/i) {
+           my $perl = "perl$exe";
+           eval "require File::Spec";
+           if ($@) {
+               warn "test.pl had problems loading File::Spec: $@";
+               $Perl = "./$perl";
+           } else {
+               $Perl = File::Spec->catfile(File::Spec->curdir(), $perl);
+           }
+       }
+       
+        # Its like this.  stat on Cygwin treats 'perl' to mean 'perl.exe'
+        # but open does not.  This can get confusing, so to be safe we
+        # always put the .exe on the end on Cygwin.
+        $Perl .= $exe if $^O eq 'cygwin' && $Perl !~ /\Q$exe\E$/;
+
+       warn "which_perl: cannot find $Perl from $^X" unless -f $Perl;
+       
+       # For subcommands to use.
+       $ENV{PERLEXE} = $Perl;
+    }
+    return $Perl;
 }
 
 1;