1# 2# t/test.pl - most of Test::More functionality without the fuss 3 4 5# NOTE: 6# 7# Increment ($x++) has a certain amount of cleverness for things like 8# 9# $x = 'zz'; 10# $x++; # $x eq 'aaa'; 11# 12# stands more chance of breaking than just a simple 13# 14# $x = $x + 1 15# 16# In this file, we use the latter "Baby Perl" approach, and increment 17# will be worked over by t/op/inc.t 18 19$Level = 1; 20my $test = 1; 21my $planned; 22my $noplan; 23my $Perl; # Safer version of $^X set by which_perl() 24 25$TODO = 0; 26$NO_ENDING = 0; 27 28# Use this instead of print to avoid interference while testing globals. 29sub _print { 30 local($\, $", $,) = (undef, ' ', ''); 31 print STDOUT @_; 32} 33 34sub _print_stderr { 35 local($\, $", $,) = (undef, ' ', ''); 36 print STDERR @_; 37} 38 39sub plan { 40 my $n; 41 if (@_ == 1) { 42 $n = shift; 43 if ($n eq 'no_plan') { 44 undef $n; 45 $noplan = 1; 46 } 47 } else { 48 my %plan = @_; 49 $n = $plan{tests}; 50 } 51 _print "1..$n\n" unless $noplan; 52 $planned = $n; 53} 54 55END { 56 my $ran = $test - 1; 57 if (!$NO_ENDING) { 58 if (defined $planned && $planned != $ran) { 59 _print_stderr 60 "# Looks like you planned $planned tests but ran $ran.\n"; 61 } elsif ($noplan) { 62 _print "1..$ran\n"; 63 } 64 } 65} 66 67# Use this instead of "print STDERR" when outputing failure diagnostic 68# messages 69sub _diag { 70 return unless @_; 71 my @mess = map { /^#/ ? "$_\n" : "# $_\n" } 72 map { split /\n/ } @_; 73 $TODO ? _print(@mess) : _print_stderr(@mess); 74} 75 76sub diag { 77 _diag(@_); 78} 79 80sub skip_all { 81 if (@_) { 82 _print "1..0 # Skip @_\n"; 83 } else { 84 _print "1..0\n"; 85 } 86 exit(0); 87} 88 89sub _ok { 90 my ($pass, $where, $name, @mess) = @_; 91 # Do not try to microoptimize by factoring out the "not ". 92 # VMS will avenge. 93 my $out; 94 if ($name) { 95 # escape out '#' or it will interfere with '# skip' and such 96 $name =~ s/#/\\#/g; 97 $out = $pass ? "ok $test - $name" : "not ok $test - $name"; 98 } else { 99 $out = $pass ? "ok $test" : "not ok $test"; 100 } 101 102 $out .= " # TODO $TODO" if $TODO; 103 _print "$out\n"; 104 105 unless ($pass) { 106 _diag "# Failed $where\n"; 107 } 108 109 # Ensure that the message is properly escaped. 110 _diag @mess; 111 112 $test = $test + 1; # don't use ++ 113 114 return $pass; 115} 116 117sub _where { 118 my @caller = caller($Level); 119 return "at $caller[1] line $caller[2]"; 120} 121 122# DON'T use this for matches. Use like() instead. 123sub ok ($@) { 124 my ($pass, $name, @mess) = @_; 125 _ok($pass, _where(), $name, @mess); 126} 127 128sub _q { 129 my $x = shift; 130 return 'undef' unless defined $x; 131 my $q = $x; 132 $q =~ s/\\/\\\\/g; 133 $q =~ s/'/\\'/g; 134 return "'$q'"; 135} 136 137sub _qq { 138 my $x = shift; 139 return defined $x ? '"' . display ($x) . '"' : 'undef'; 140}; 141 142# keys are the codes \n etc map to, values are 2 char strings such as \n 143my %backslash_escape; 144foreach my $x (split //, 'nrtfa\\\'"') { 145 $backslash_escape{ord eval "\"\\$x\""} = "\\$x"; 146} 147# A way to display scalars containing control characters and Unicode. 148# Trying to avoid setting $_, or relying on local $_ to work. 149sub display { 150 my @result; 151 foreach my $x (@_) { 152 if (defined $x and not ref $x) { 153 my $y = ''; 154 foreach my $c (unpack("U*", $x)) { 155 if ($c > 255) { 156 $y .= sprintf "\\x{%x}", $c; 157 } elsif ($backslash_escape{$c}) { 158 $y .= $backslash_escape{$c}; 159 } else { 160 my $z = chr $c; # Maybe we can get away with a literal... 161 $z = sprintf "\\%03o", $c if $z =~ /[[:^print:]]/; 162 $y .= $z; 163 } 164 } 165 $x = $y; 166 } 167 return $x unless wantarray; 168 push @result, $x; 169 } 170 return @result; 171} 172 173sub is ($$@) { 174 my ($got, $expected, $name, @mess) = @_; 175 176 my $pass; 177 if( !defined $got || !defined $expected ) { 178 # undef only matches undef 179 $pass = !defined $got && !defined $expected; 180 } 181 else { 182 $pass = $got eq $expected; 183 } 184 185 unless ($pass) { 186 unshift(@mess, "# got "._q($got)."\n", 187 "# expected "._q($expected)."\n"); 188 } 189 _ok($pass, _where(), $name, @mess); 190} 191 192sub isnt ($$@) { 193 my ($got, $isnt, $name, @mess) = @_; 194 195 my $pass; 196 if( !defined $got || !defined $isnt ) { 197 # undef only matches undef 198 $pass = defined $got || defined $isnt; 199 } 200 else { 201 $pass = $got ne $isnt; 202 } 203 204 unless( $pass ) { 205 unshift(@mess, "# it should not be "._q($got)."\n", 206 "# but it is.\n"); 207 } 208 _ok($pass, _where(), $name, @mess); 209} 210 211sub cmp_ok ($$$@) { 212 my($got, $type, $expected, $name, @mess) = @_; 213 214 my $pass; 215 { 216 local $^W = 0; 217 local($@,$!); # don't interfere with $@ 218 # eval() sometimes resets $! 219 $pass = eval "\$got $type \$expected"; 220 } 221 unless ($pass) { 222 # It seems Irix long doubles can have 2147483648 and 2147483648 223 # that stringify to the same thing but are acutally numerically 224 # different. Display the numbers if $type isn't a string operator, 225 # and the numbers are stringwise the same. 226 # (all string operators have alphabetic names, so tr/a-z// is true) 227 # This will also show numbers for some uneeded cases, but will 228 # definately be helpful for things such as == and <= that fail 229 if ($got eq $expected and $type !~ tr/a-z//) { 230 unshift @mess, "# $got - $expected = " . ($got - $expected) . "\n"; 231 } 232 unshift(@mess, "# got "._q($got)."\n", 233 "# expected $type "._q($expected)."\n"); 234 } 235 _ok($pass, _where(), $name, @mess); 236} 237 238# Check that $got is within $range of $expected 239# if $range is 0, then check it's exact 240# else if $expected is 0, then $range is an absolute value 241# otherwise $range is a fractional error. 242# Here $range must be numeric, >= 0 243# Non numeric ranges might be a useful future extension. (eg %) 244sub within ($$$@) { 245 my ($got, $expected, $range, $name, @mess) = @_; 246 my $pass; 247 if (!defined $got or !defined $expected or !defined $range) { 248 # This is a fail, but doesn't need extra diagnostics 249 } elsif ($got !~ tr/0-9// or $expected !~ tr/0-9// or $range !~ tr/0-9//) { 250 # This is a fail 251 unshift @mess, "# got, expected and range must be numeric\n"; 252 } elsif ($range < 0) { 253 # This is also a fail 254 unshift @mess, "# range must not be negative\n"; 255 } elsif ($range == 0) { 256 # Within 0 is == 257 $pass = $got == $expected; 258 } elsif ($expected == 0) { 259 # If expected is 0, treat range as absolute 260 $pass = ($got <= $range) && ($got >= - $range); 261 } else { 262 my $diff = $got - $expected; 263 $pass = abs ($diff / $expected) < $range; 264 } 265 unless ($pass) { 266 if ($got eq $expected) { 267 unshift @mess, "# $got - $expected = " . ($got - $expected) . "\n"; 268 } 269 unshift@mess, "# got "._q($got)."\n", 270 "# expected "._q($expected)." (within "._q($range).")\n"; 271 } 272 _ok($pass, _where(), $name, @mess); 273} 274 275# Note: this isn't quite as fancy as Test::More::like(). 276 277sub like ($$@) { like_yn (0,@_) }; # 0 for - 278sub unlike ($$@) { like_yn (1,@_) }; # 1 for un- 279 280sub like_yn ($$$@) { 281 my ($flip, $got, $expected, $name, @mess) = @_; 282 my $pass; 283 $pass = $got =~ /$expected/ if !$flip; 284 $pass = $got !~ /$expected/ if $flip; 285 unless ($pass) { 286 unshift(@mess, "# got '$got'\n", 287 $flip 288 ? "# expected !~ /$expected/\n" : "# expected /$expected/\n"); 289 } 290 local $Level = $Level + 1; 291 _ok($pass, _where(), $name, @mess); 292} 293 294sub pass { 295 _ok(1, '', @_); 296} 297 298sub fail { 299 _ok(0, _where(), @_); 300} 301 302sub curr_test { 303 $test = shift if @_; 304 return $test; 305} 306 307sub next_test { 308 my $retval = $test; 309 $test = $test + 1; # don't use ++ 310 $retval; 311} 312 313# Note: can't pass multipart messages since we try to 314# be compatible with Test::More::skip(). 315sub skip { 316 my $why = shift; 317 my $n = @_ ? shift : 1; 318 for (1..$n) { 319 _print "ok $test # skip $why\n"; 320 $test = $test + 1; 321 } 322 local $^W = 0; 323 last SKIP; 324} 325 326sub todo_skip { 327 my $why = shift; 328 my $n = @_ ? shift : 1; 329 330 for (1..$n) { 331 _print "not ok $test # TODO & SKIP $why\n"; 332 $test = $test + 1; 333 } 334 local $^W = 0; 335 last TODO; 336} 337 338sub eq_array { 339 my ($ra, $rb) = @_; 340 return 0 unless $#$ra == $#$rb; 341 for my $i (0..$#$ra) { 342 next if !defined $ra->[$i] && !defined $rb->[$i]; 343 return 0 if !defined $ra->[$i]; 344 return 0 if !defined $rb->[$i]; 345 return 0 unless $ra->[$i] eq $rb->[$i]; 346 } 347 return 1; 348} 349 350sub eq_hash { 351 my ($orig, $suspect) = @_; 352 my $fail; 353 while (my ($key, $value) = each %$suspect) { 354 # Force a hash recompute if this perl's internals can cache the hash key. 355 $key = "" . $key; 356 if (exists $orig->{$key}) { 357 if ($orig->{$key} ne $value) { 358 _print "# key ", _qq($key), " was ", _qq($orig->{$key}), 359 " now ", _qq($value), "\n"; 360 $fail = 1; 361 } 362 } else { 363 _print "# key ", _qq($key), " is ", _qq($value), 364 ", not in original.\n"; 365 $fail = 1; 366 } 367 } 368 foreach (keys %$orig) { 369 # Force a hash recompute if this perl's internals can cache the hash key. 370 $_ = "" . $_; 371 next if (exists $suspect->{$_}); 372 _print "# key ", _qq($_), " was ", _qq($orig->{$_}), " now missing.\n"; 373 $fail = 1; 374 } 375 !$fail; 376} 377 378sub require_ok ($) { 379 my ($require) = @_; 380 eval <<REQUIRE_OK; 381require $require; 382REQUIRE_OK 383 _ok(!$@, _where(), "require $require"); 384} 385 386sub use_ok ($) { 387 my ($use) = @_; 388 eval <<USE_OK; 389use $use; 390USE_OK 391 _ok(!$@, _where(), "use $use"); 392} 393 394# runperl - Runs a separate perl interpreter. 395# Arguments : 396# switches => [ command-line switches ] 397# nolib => 1 # don't use -I../lib (included by default) 398# prog => one-liner (avoid quotes) 399# progs => [ multi-liner (avoid quotes) ] 400# progfile => perl script 401# stdin => string to feed the stdin 402# stderr => redirect stderr to stdout 403# args => [ command-line arguments to the perl program ] 404# verbose => print the command line 405 406my $is_mswin = $^O eq 'MSWin32'; 407my $is_netware = $^O eq 'NetWare'; 408my $is_macos = $^O eq 'MacOS'; 409my $is_vms = $^O eq 'VMS'; 410my $is_cygwin = $^O eq 'cygwin'; 411 412sub _quote_args { 413 my ($runperl, $args) = @_; 414 415 foreach (@$args) { 416 # In VMS protect with doublequotes because otherwise 417 # DCL will lowercase -- unless already doublequoted. 418 $_ = q(").$_.q(") if $is_vms && !/^\"/ && length($_) > 0; 419 $$runperl .= ' ' . $_; 420 } 421} 422 423sub _create_runperl { # Create the string to qx in runperl(). 424 my %args = @_; 425 my $runperl = which_perl(); 426 if ($runperl =~ m/\s/) { 427 $runperl = qq{"$runperl"}; 428 } 429 #- this allows, for example, to set PERL_RUNPERL_DEBUG=/usr/bin/valgrind 430 if ($ENV{PERL_RUNPERL_DEBUG}) { 431 $runperl = "$ENV{PERL_RUNPERL_DEBUG} $runperl"; 432 } 433 unless ($args{nolib}) { 434 if ($is_macos) { 435 $runperl .= ' -I::lib'; 436 # Use UNIX style error messages instead of MPW style. 437 $runperl .= ' -MMac::err=unix' if $args{stderr}; 438 } 439 else { 440 $runperl .= ' "-I../lib"'; # doublequotes because of VMS 441 } 442 } 443 if ($args{switches}) { 444 local $Level = 2; 445 die "test.pl:runperl(): 'switches' must be an ARRAYREF " . _where() 446 unless ref $args{switches} eq "ARRAY"; 447 _quote_args(\$runperl, $args{switches}); 448 } 449 if (defined $args{prog}) { 450 die "test.pl:runperl(): both 'prog' and 'progs' cannot be used " . _where() 451 if defined $args{progs}; 452 $args{progs} = [$args{prog}] 453 } 454 if (defined $args{progs}) { 455 die "test.pl:runperl(): 'progs' must be an ARRAYREF " . _where() 456 unless ref $args{progs} eq "ARRAY"; 457 foreach my $prog (@{$args{progs}}) { 458 if ($is_mswin || $is_netware || $is_vms) { 459 $runperl .= qq ( -e "$prog" ); 460 } 461 else { 462 $runperl .= qq ( -e '$prog' ); 463 } 464 } 465 } elsif (defined $args{progfile}) { 466 $runperl .= qq( "$args{progfile}"); 467 } else { 468 # You probaby didn't want to be sucking in from the upstream stdin 469 die "test.pl:runperl(): none of prog, progs, progfile, args, " 470 . " switches or stdin specified" 471 unless defined $args{args} or defined $args{switches} 472 or defined $args{stdin}; 473 } 474 if (defined $args{stdin}) { 475 # so we don't try to put literal newlines and crs onto the 476 # command line. 477 $args{stdin} =~ s/\n/\\n/g; 478 $args{stdin} =~ s/\r/\\r/g; 479 480 if ($is_mswin || $is_netware || $is_vms) { 481 $runperl = qq{$Perl -e "print qq(} . 482 $args{stdin} . q{)" | } . $runperl; 483 } 484 elsif ($is_macos) { 485 # MacOS can only do two processes under MPW at once; 486 # the test itself is one; we can't do two more, so 487 # write to temp file 488 my $stdin = qq{$Perl -e 'print qq(} . $args{stdin} . qq{)' > teststdin; }; 489 if ($args{verbose}) { 490 my $stdindisplay = $stdin; 491 $stdindisplay =~ s/\n/\n\#/g; 492 _print_stderr "# $stdindisplay\n"; 493 } 494 `$stdin`; 495 $runperl .= q{ < teststdin }; 496 } 497 else { 498 $runperl = qq{$Perl -e 'print qq(} . 499 $args{stdin} . q{)' | } . $runperl; 500 } 501 } 502 if (defined $args{args}) { 503 _quote_args(\$runperl, $args{args}); 504 } 505 $runperl .= ' 2>&1' if $args{stderr} && !$is_macos; 506 $runperl .= " \xB3 Dev:Null" if !$args{stderr} && $is_macos; 507 if ($args{verbose}) { 508 my $runperldisplay = $runperl; 509 $runperldisplay =~ s/\n/\n\#/g; 510 _print_stderr "# $runperldisplay\n"; 511 } 512 return $runperl; 513} 514 515sub runperl { 516 die "test.pl:runperl() does not take a hashref" 517 if ref $_[0] and ref $_[0] eq 'HASH'; 518 my $runperl = &_create_runperl; 519 my $result; 520 521 my $tainted = ${^TAINT}; 522 my %args = @_; 523 exists $args{switches} && grep m/^-T$/, @{$args{switches}} and $tainted = $tainted + 1; 524 525 if ($tainted) { 526 # We will assume that if you're running under -T, you really mean to 527 # run a fresh perl, so we'll brute force launder everything for you 528 my $sep; 529 530 if (! eval 'require Config; 1') { 531 warn "test.pl had problems loading Config: $@"; 532 $sep = ':'; 533 } else { 534 $sep = $Config::Config{path_sep}; 535 } 536 537 my @keys = grep {exists $ENV{$_}} qw(CDPATH IFS ENV BASH_ENV); 538 local @ENV{@keys} = (); 539 # Untaint, plus take out . and empty string: 540 local $ENV{'DCL$PATH'} = $1 if $is_vms && ($ENV{'DCL$PATH'} =~ /(.*)/s); 541 $ENV{PATH} =~ /(.*)/s; 542 local $ENV{PATH} = 543 join $sep, grep { $_ ne "" and $_ ne "." and -d $_ and 544 ($is_mswin or $is_vms or !(stat && (stat _)[2]&0022)) } 545 split quotemeta ($sep), $1; 546 $ENV{PATH} .= "$sep/bin" if $is_cygwin; # Must have /bin under Cygwin 547 548 $runperl =~ /(.*)/s; 549 $runperl = $1; 550 551 $result = `$runperl`; 552 } else { 553 $result = `$runperl`; 554 } 555 $result =~ s/\n\n/\n/ if $is_vms; # XXX pipes sometimes double these 556 return $result; 557} 558 559*run_perl = \&runperl; # Nice alias. 560 561sub DIE { 562 _print_stderr "# @_\n"; 563 exit 1; 564} 565 566# A somewhat safer version of the sometimes wrong $^X. 567sub which_perl { 568 unless (defined $Perl) { 569 $Perl = $^X; 570 571 # VMS should have 'perl' aliased properly 572 return $Perl if $^O eq 'VMS'; 573 574 my $exe; 575 if (! eval 'require Config; 1') { 576 warn "test.pl had problems loading Config: $@"; 577 $exe = ''; 578 } else { 579 $exe = $Config::Config{_exe}; 580 } 581 $exe = '' unless defined $exe; 582 583 # This doesn't absolutize the path: beware of future chdirs(). 584 # We could do File::Spec->abs2rel() but that does getcwd()s, 585 # which is a bit heavyweight to do here. 586 587 if ($Perl =~ /^perl\Q$exe\E$/i) { 588 my $perl = "perl$exe"; 589 if (! eval 'require File::Spec; 1') { 590 warn "test.pl had problems loading File::Spec: $@"; 591 $Perl = "./$perl"; 592 } else { 593 $Perl = File::Spec->catfile(File::Spec->curdir(), $perl); 594 } 595 } 596 597 # Build up the name of the executable file from the name of 598 # the command. 599 600 if ($Perl !~ /\Q$exe\E$/i) { 601 $Perl .= $exe; 602 } 603 604 warn "which_perl: cannot find $Perl from $^X" unless -f $Perl; 605 606 # For subcommands to use. 607 $ENV{PERLEXE} = $Perl; 608 } 609 return $Perl; 610} 611 612sub unlink_all { 613 foreach my $file (@_) { 614 1 while unlink $file; 615 _print_stderr "# Couldn't unlink '$file': $!\n" if -f $file; 616 } 617} 618 619my %tmpfiles; 620END { unlink_all keys %tmpfiles } 621 622# A regexp that matches the tempfile names 623$::tempfile_regexp = 'tmp\d+[A-Z][A-Z]?'; 624 625# Avoid ++, avoid ranges, avoid split // 626my @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); 627sub tempfile { 628 my $count = 0; 629 do { 630 my $temp = $count; 631 my $try = "tmp$$"; 632 do { 633 $try .= $letters[$temp % 26]; 634 $temp = int ($temp / 26); 635 } while $temp; 636 # Need to note all the file names we allocated, as a second request may 637 # come before the first is created. 638 if (!-e $try && !$tmpfiles{$try}) { 639 # We have a winner 640 $tmpfiles{$try}++; 641 return $try; 642 } 643 $count = $count + 1; 644 } while $count < 26 * 26; 645 die "Can't find temporary file name starting 'tmp$$'"; 646} 647 648# This is the temporary file for _fresh_perl 649my $tmpfile = tempfile(); 650 651# 652# _fresh_perl 653# 654# The $resolve must be a subref that tests the first argument 655# for success, or returns the definition of success (e.g. the 656# expected scalar) if given no arguments. 657# 658 659sub _fresh_perl { 660 my($prog, $resolve, $runperl_args, $name) = @_; 661 662 $runperl_args ||= {}; 663 $runperl_args->{progfile} = $tmpfile; 664 $runperl_args->{stderr} = 1; 665 666 open TEST, ">$tmpfile" or die "Cannot open $tmpfile: $!"; 667 668 # VMS adjustments 669 if( $^O eq 'VMS' ) { 670 $prog =~ s#/dev/null#NL:#; 671 672 # VMS file locking 673 $prog =~ s{if \(-e _ and -f _ and -r _\)} 674 {if (-e _ and -f _)} 675 } 676 677 print TEST $prog; 678 close TEST or die "Cannot close $tmpfile: $!"; 679 680 my $results = runperl(%$runperl_args); 681 my $status = $?; 682 683 # Clean up the results into something a bit more predictable. 684 $results =~ s/\n+$//; 685 $results =~ s/at\s+$::tempfile_regexp\s+line/at - line/g; 686 $results =~ s/of\s+$::tempfile_regexp\s+aborted/of - aborted/g; 687 688 # bison says 'parse error' instead of 'syntax error', 689 # various yaccs may or may not capitalize 'syntax'. 690 $results =~ s/^(syntax|parse) error/syntax error/mig; 691 692 if ($^O eq 'VMS') { 693 # some tests will trigger VMS messages that won't be expected 694 $results =~ s/\n?%[A-Z]+-[SIWEF]-[A-Z]+,.*//; 695 696 # pipes double these sometimes 697 $results =~ s/\n\n/\n/g; 698 } 699 700 my $pass = $resolve->($results); 701 unless ($pass) { 702 _diag "# PROG: \n$prog\n"; 703 _diag "# EXPECTED:\n", $resolve->(), "\n"; 704 _diag "# GOT:\n$results\n"; 705 _diag "# STATUS: $status\n"; 706 } 707 708 # Use the first line of the program as a name if none was given 709 unless( $name ) { 710 ($first_line, $name) = $prog =~ /^((.{1,50}).*)/; 711 $name .= '...' if length $first_line > length $name; 712 } 713 714 _ok($pass, _where(), "fresh_perl - $name"); 715} 716 717# 718# fresh_perl_is 719# 720# Combination of run_perl() and is(). 721# 722 723sub fresh_perl_is { 724 my($prog, $expected, $runperl_args, $name) = @_; 725 local $Level = 2; 726 _fresh_perl($prog, 727 sub { @_ ? $_[0] eq $expected : $expected }, 728 $runperl_args, $name); 729} 730 731# 732# fresh_perl_like 733# 734# Combination of run_perl() and like(). 735# 736 737sub fresh_perl_like { 738 my($prog, $expected, $runperl_args, $name) = @_; 739 local $Level = 2; 740 _fresh_perl($prog, 741 sub { @_ ? 742 $_[0] =~ (ref $expected ? $expected : /$expected/) : 743 $expected }, 744 $runperl_args, $name); 745} 746 747sub can_ok ($@) { 748 my($proto, @methods) = @_; 749 my $class = ref $proto || $proto; 750 751 unless( @methods ) { 752 return _ok( 0, _where(), "$class->can(...)" ); 753 } 754 755 my @nok = (); 756 foreach my $method (@methods) { 757 local($!, $@); # don't interfere with caller's $@ 758 # eval sometimes resets $! 759 eval { $proto->can($method) } || push @nok, $method; 760 } 761 762 my $name; 763 $name = @methods == 1 ? "$class->can('$methods[0]')" 764 : "$class->can(...)"; 765 766 _ok( !@nok, _where(), $name ); 767} 768 769sub isa_ok ($$;$) { 770 my($object, $class, $obj_name) = @_; 771 772 my $diag; 773 $obj_name = 'The object' unless defined $obj_name; 774 my $name = "$obj_name isa $class"; 775 if( !defined $object ) { 776 $diag = "$obj_name isn't defined"; 777 } 778 elsif( !ref $object ) { 779 $diag = "$obj_name isn't a reference"; 780 } 781 else { 782 # We can't use UNIVERSAL::isa because we want to honor isa() overrides 783 local($@, $!); # eval sometimes resets $! 784 my $rslt = eval { $object->isa($class) }; 785 if( $@ ) { 786 if( $@ =~ /^Can't call method "isa" on unblessed reference/ ) { 787 if( !UNIVERSAL::isa($object, $class) ) { 788 my $ref = ref $object; 789 $diag = "$obj_name isn't a '$class' it's a '$ref'"; 790 } 791 } else { 792 die <<WHOA; 793WHOA! I tried to call ->isa on your object and got some weird error. 794This should never happen. Please contact the author immediately. 795Here's the error. 796$@ 797WHOA 798 } 799 } 800 elsif( !$rslt ) { 801 my $ref = ref $object; 802 $diag = "$obj_name isn't a '$class' it's a '$ref'"; 803 } 804 } 805 806 _ok( !$diag, _where(), $name ); 807} 808 809# Set a watchdog to timeout the entire test file 810# NOTE: If the test file uses 'threads', then call the watchdog() function 811# _AFTER_ the 'threads' module is loaded. 812sub watchdog ($) 813{ 814 my $timeout = shift; 815 my $timeout_msg = 'Test process timed out - terminating'; 816 817 my $pid_to_kill = $$; # PID for this process 818 819 # Don't use a watchdog process if 'threads' is loaded - 820 # use a watchdog thread instead 821 if (! $threads::threads) { 822 823 # On Windows and VMS, try launching a watchdog process 824 # using system(1, ...) (see perlport.pod) 825 if (($^O eq 'MSWin32') || ($^O eq 'VMS')) { 826 # On Windows, try to get the 'real' PID 827 if ($^O eq 'MSWin32') { 828 eval { require Win32; }; 829 if (defined(&Win32::GetCurrentProcessId)) { 830 $pid_to_kill = Win32::GetCurrentProcessId(); 831 } 832 } 833 834 # If we still have a fake PID, we can't use this method at all 835 return if ($pid_to_kill <= 0); 836 837 # Launch watchdog process 838 my $watchdog; 839 eval { 840 local $SIG{'__WARN__'} = sub { 841 _diag("Watchdog warning: $_[0]"); 842 }; 843 my $sig = $^O eq 'VMS' ? 'TERM' : 'KILL'; 844 $watchdog = system(1, which_perl(), '-e', 845 "sleep($timeout);" . 846 "warn('# $timeout_msg\n');" . 847 "kill($sig, $pid_to_kill);"); 848 }; 849 if ($@ || ($watchdog <= 0)) { 850 _diag('Failed to start watchdog'); 851 _diag($@) if $@; 852 undef($watchdog); 853 return; 854 } 855 856 # Add END block to parent to terminate and 857 # clean up watchdog process 858 eval "END { local \$! = 0; local \$? = 0; 859 wait() if kill('KILL', $watchdog); };"; 860 return; 861 } 862 863 # Try using fork() to generate a watchdog process 864 my $watchdog; 865 eval { $watchdog = fork() }; 866 if (defined($watchdog)) { 867 if ($watchdog) { # Parent process 868 # Add END block to parent to terminate and 869 # clean up watchdog process 870 eval "END { local \$! = 0; local \$? = 0; 871 wait() if kill('KILL', $watchdog); };"; 872 return; 873 } 874 875 ### Watchdog process code 876 877 # Load POSIX if available 878 eval { require POSIX; }; 879 880 # Execute the timeout 881 sleep($timeout - 2) if ($timeout > 2); # Workaround for perlbug #49073 882 sleep(2); 883 884 # Kill test process if still running 885 if (kill(0, $pid_to_kill)) { 886 _diag($timeout_msg); 887 kill('KILL', $pid_to_kill); 888 } 889 890 # Don't execute END block (added at beginning of this file) 891 $NO_ENDING = 1; 892 893 # Terminate ourself (i.e., the watchdog) 894 POSIX::_exit(1) if (defined(&POSIX::_exit)); 895 exit(1); 896 } 897 898 # fork() failed - fall through and try using a thread 899 } 900 901 # Use a watchdog thread because either 'threads' is loaded, 902 # or fork() failed 903 if (eval 'require threads; 1') { 904 threads->create(sub { 905 # Load POSIX if available 906 eval { require POSIX; }; 907 908 # Execute the timeout 909 my $time_left = $timeout; 910 do { 911 $time_left -= sleep($time_left); 912 } while ($time_left > 0); 913 914 # Kill the parent (and ourself) 915 select(STDERR); $| = 1; 916 _diag($timeout_msg); 917 POSIX::_exit(1) if (defined(&POSIX::_exit)); 918 my $sig = $^O eq 'VMS' ? 'TERM' : 'KILL'; 919 kill($sig, $pid_to_kill); 920 })->detach(); 921 return; 922 } 923 924 # If everything above fails, then just use an alarm timeout 925 if (eval { alarm($timeout); 1; }) { 926 # Load POSIX if available 927 eval { require POSIX; }; 928 929 # Alarm handler will do the actual 'killing' 930 $SIG{'ALRM'} = sub { 931 select(STDERR); $| = 1; 932 _diag($timeout_msg); 933 POSIX::_exit(1) if (defined(&POSIX::_exit)); 934 my $sig = $^O eq 'VMS' ? 'TERM' : 'KILL'; 935 kill($sig, $pid_to_kill); 936 }; 937 } 938} 939 9401; 941

