The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl -w

use strict;
use warnings;

use Test::More tests => 21;

use Error qw/ :warndie /;

# Turn on full stack trace capture
$Error::Debug = 1;

# This file's name - for string matching. We need to quotemeta it, because on
# Win32, the filename is t\08warndie.t, and we don't want that accidentally
# matching an (invalid) \08 octal digit
my $file = qr/\Q$0\E/;

# Most of these tests are fatal, and print data on STDERR. We therefore use
# this testing function to run a CODEref in a child process and captures its
# STDERR and note whether the CODE block exited
my ( $s, $felloffcode );
my $linekid = __LINE__ + 15; # the $code->() is 15 lines below this one
sub run_kid(&)
{
    my ( $code ) = @_;

    # Win32's fork() emulation can't correctly handle the open("-|") case yet
    # So we'll implement this manually - inspired by 'perldoc perlfork'
    pipe my $childh, my $child or die "Cannot pipe() - $!";
    defined( my $kid = fork() ) or die "Cannot fork() - $!";

    if ( !$kid ) {
        close $childh;
        close STDERR;
        open(STDERR, ">&=" . fileno($child)) or die;

        $code->();

        print STDERR "FELL OUT OF CODEREF\n";
        exit(1);
    }

    close $child;

    $s = "";
    while( defined ( $_ = <$childh> ) ) {
        $s .= $_;
    }

    close( $childh );
    waitpid( $kid, 0 );

    $felloffcode = 0;
    $s =~ tr/\r//d; # Remove Win32 \r linefeeds to make RE tests easier
    if( $s =~ s/FELL OUT OF CODEREF\n$// ) {
        $felloffcode = 1;
    }
}

ok(1, "Loaded");

run_kid {
    print STDERR "Print to STDERR\n";
};

is( $s, "Print to STDERR\n", "Test framework STDERR" );
is( $felloffcode, 1, "Test framework felloffcode" );

my $line;

$line = __LINE__;
run_kid {
    warn "A warning\n";
};

my ( $linea, $lineb ) = ( $line + 2, $line + 3 );
like( $s, qr/^A warning at $file line $linea\.?:
\tmain::__ANON__\(\) called at $file line $linekid
\tmain::run_kid\('?CODE\(0x[0-9a-f]+\)'?\) called at $file line $lineb
$/, "warn \\n-terminated STDERR" );
is( $felloffcode, 1, "warn \\n-terminated felloffcode" );

$line = __LINE__;
run_kid {
    warn "A warning";
};

( $linea, $lineb ) = ( $line + 2, $line + 3 );
like( $s, qr/^A warning at $file line $linea\.?:
\tmain::__ANON__\(\) called at $file line $linekid
\tmain::run_kid\('?CODE\(0x[0-9a-f]+\)'?\) called at $file line $lineb
$/, "warn unterminated STDERR" );
is( $felloffcode, 1, "warn unterminated felloffcode" );

$line = __LINE__;
run_kid {
    die "An error\n";
};

( $linea, $lineb ) = ( $line + 2, $line + 3 );
like( $s, qr/^
Unhandled perl error caught at toplevel:

  An error

Thrown from: $file:$linea

Full stack trace:

\tmain::__ANON__\(\) called at $file line $linekid
\tmain::run_kid\('?CODE\(0x[0-9a-f]+\)'?\) called at $file line $lineb

$/, "die \\n-terminated STDERR" );
is( $felloffcode, 0, "die \\n-terminated felloffcode" );

$line = __LINE__;
run_kid {
    die "An error";
};

( $linea, $lineb ) = ( $line + 2, $line + 3 );
like( $s, qr/^
Unhandled perl error caught at toplevel:

  An error

Thrown from: $file:$linea

Full stack trace:

\tmain::__ANON__\(\) called at $file line $linekid
\tmain::run_kid\('?CODE\(0x[0-9a-f]+\)'?\) called at $file line $lineb

$/, "die unterminated STDERR" );
is( $felloffcode, 0, "die unterminated felloffcode" );

$line = __LINE__;
run_kid {
    throw Error( -text => "An exception" );
};

( $linea, $lineb ) = ( $line + 2, $line + 3 );
like( $s, qr/^
Unhandled exception of type Error caught at toplevel:

  An exception

Thrown from: $file:$linea

Full stack trace:

\tmain::__ANON__\(\) called at $file line $linekid
\tmain::run_kid\('?CODE\(0x[0-9a-f]+\)'?\) called at $file line $lineb

$/, "Error STDOUT" );
is( $felloffcode, 0, "Error felloffcode" );

# Now custom warn and die functions to ensure the :warndie handler respects them
$SIG{__WARN__} = sub { warn "My custom warning here: $_[0]" };
$SIG{__DIE__}  = sub { die  "My custom death here: $_[0]" };

# First test them
$line = __LINE__;
run_kid {
    warn "A warning";
};

$linea = $line + 2;
like( $s, qr/^My custom warning here: A warning at $file line $linea\.?
$/, "Custom warn test STDERR" );
is( $felloffcode, 1, "Custom warn test felloffcode" );

$line = __LINE__;
run_kid {
    die "An error";
};

$linea = $line + 2;
like( $s, qr/^My custom death here: An error at $file line $linea\.?
/, "Custom die test STDERR" );
is( $felloffcode, 0, "Custom die test felloffcode" );

# Re-install the :warndie handlers
import Error qw( :warndie );

$line = __LINE__;
run_kid {
    warn "A warning\n";
};

( $linea, $lineb ) = ( $line + 2, $line + 3 );
like( $s, qr/^My custom warning here: A warning at $file line $linea\.?:
\tmain::__ANON__\(\) called at $file line $linekid
\tmain::run_kid\('?CODE\(0x[0-9a-f]+\)'?\) called at $file line $lineb
$/, "Custom warn STDERR" );
is( $felloffcode, 1, "Custom warn felloffcode" );

$line = __LINE__;
run_kid {
    die "An error";
};

( $linea, $lineb ) = ( $line + 2, $line + 3 );
like( $s, qr/^My custom death here: 
Unhandled perl error caught at toplevel:

  An error

Thrown from: $file:$linea

Full stack trace:

\tmain::__ANON__\(\) called at $file line $linekid
\tmain::run_kid\('?CODE\(0x[0-9a-f]+\)'?\) called at $file line $lineb

$/, "Custom die STDERR" );
is( $felloffcode, 0, "Custom die felloffcode" );

# Done