#!perl # -*- mode: cperl ; compile-command: "cd .. ; ./Build ; prove -vb t/11-*.t" -*- use Test::More; use strict; use warnings; use File::Temp qw( tempfile ); use Test::Trap::Builder::SystemSafe; use Test::Trap qw( trap $T :flow:stderr(systemsafe):stdout(systemsafe):warn ); BEGIN { # silence some warnings that make coverage reports hard to get at if ($Storable::VERSION) { eval { eval { no warnings; Storable::retrieve('.') }; # silly, but hopefully safe ... my $_r = \&Storable::_retrieve; no warnings 'redefine'; *Storable::_retrieve = sub { no warnings; local $SIG{__WARN__} = sub {}; $_r->(@_); }; }; } if ($Devel::Cover::DB::Structure::VERSION) { eval { my $d = \&Devel::Cover::DB::Structure::digest; no warnings 'redefine'; *Devel::Cover::DB::Structure::digest = sub { no warnings; local $SIG{__WARN__} = sub {}; $d->(@_); }; }; } } # Protect against tainted PATH &c ... $ENV{PATH} = ''; $ENV{ENV} = ''; $ENV{BASH_ENV} = ''; my ($PERL) = $^X =~ /^([\w.\/:\\~-]+)$/; if ($PERL) { plan tests => 3 + 6*6 + 4; } else { plan skip_all => "Odd perl path: $^X"; } my $desc = "fdopen()ed file handle"; SKIP: { skip 'These tests are irrelevant on old perls', 3 if $] < 5.008; open my $fh, '>&=STDOUT' or die "Cannot fdopen STDOUT: '$!'"; exit diag "Got fileno " . fileno($fh) unless fileno($fh)==1; # Basic error situation: STDOUT cannot be reopened on fd-1: eval { trap { system $PERL, '-e', 'binmode STDOUT; binmode STDERR; warn qq(0123456789Warning\n); print qq(Printing\n)'; exit 1 } }; like( $@, qr/^\QCannot get the desired descriptor, '1' (could it be that it is fdopened and so still open?)/, "$desc: exception string" ); is( fileno STDOUT, undef, "$desc: STDOUT should be left closed by now") or exit diag "Got STDOUT with fd " . fileno(STDOUT); is( fileno STDERR, 2, "$desc: STDERR fileno should be unchanged"); unless (fileno(STDOUT) or open STDOUT, '>&=' . fileno $fh) { exit diag "Cannot fdopen fno ".fileno($fh).": '$!'"; } if (fileno $fh and !close $fh) { exit diag "Cannot close: '$!'"; } } $desc = "simple fork test"; trap { fork ? wait : do { warn "0123456789Warning\n"; print "Printing\n" }; exit 1; }; is( $T->exit, 1, "$desc: exit(1)" ); is( $T->stdout, "Printing\n", "$desc: system() STDOUT" ); is( $T->stderr, "0123456789Warning\n", "$desc: system() STDERR" ); is( join("\n", @{$T->warn}), '', "$desc: No warnings" ); # Have the file handles been re-opened on the right descriptors? is( fileno STDOUT, 1, "$desc: STDOUT fileno should be unchanged"); is( fileno STDERR, 2, "$desc: STDERR fileno should be unchanged"); # Basic messing-up -- protect the handles with an outer trap: trap { for (1..5) { my $desc = "Take $_"; my $OUTFNO = 1; my $EXPECT = "Printing\n"; if ($_ > 2) { close STDIN; $desc .= ' - STDIN closed'; } if ($_ > 3) { close STDOUT; undef $OUTFNO; $EXPECT = ''; $desc .= ' - STDOUT closed'; } # Output from forked-off processes? trap { my @args = ($PERL, '-e', 'binmode STDOUT; binmode STDERR; warn qq(0123456789Warning\n); print qq(Printing\n)'); system @args and die "system @args failed with $?"; exit 1; }; is( $T->exit, 1, "$desc: exit(1)" ) or $T->diag_all; is( $T->stdout, $EXPECT, "$desc: system() STDOUT" ); is( $T->stderr, "0123456789Warning\n", "$desc: system() STDERR" ); is( join("\n", @{$T->warn}), '', "$desc: No warnings" ); # Have the file handles been re-opened on the right descriptors? is( fileno STDOUT, $OUTFNO, "$desc: STDOUT fileno should be unchanged"); is( fileno STDERR, 2, "$desc: STDERR fileno should be unchanged"); } }; SKIP: { use Config; unless ($Config{d_fork}) { skip 'Need a real fork()', 4; } # For coverage: Output from forked-off processes? my $me; trap { trap { $me = fork ? 'parent' : 'child'; print "\u$me print\n"; warn "\u$me warning\n"; trap { 1 }; wait, exit $$ if $me eq 'parent'; }; # On windows, in the child pseudo-process, this dies on leaving # the trap (fd 2 is not availible, because it is open in another # thread). I don't think anything can be done about it. CORE::exit(0) if $me eq 'child'; is( $T->exit, $$, "Trapped the parent exit" ); like( $T->stdout, qr/^(Parent print\nChild print\n|Child print\nParent print\n)/, 'STDOUT from both processes!' ); like( $T->stderr, qr/^(Parent warning\nChild warning\n|Child warning\nParent warning\n)/, 'STDERR from both processes!' ); is_deeply( $T->warn, ["Parent warning\n"], 'Warnings from the parent only' ); }; } exit;