The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
Build.PL 03
Changes 014
META.json 2121
META.yml 88
Makefile.PL 11
lib/Test/Trap/Builder/PerlIO.pm 22
lib/Test/Trap/Builder/SystemSafe.pm 513
lib/Test/Trap/Builder/TempFile.pm 38
lib/Test/Trap/Builder.pm 34
lib/Test/Trap.pm 22
t/03-files.pl 128
t/04-exit.t 114
t/14-leaks.t 11
13 files changed (This is a version diff) 48119
@@ -10,6 +10,9 @@ my $builder = $class->new
     create_makefile_pl  => 'traditional',
     dist_author         => 'Eirik Berg Hanssen <ebhanssen@cpan.org>',
     dist_version_from   => 'lib/Test/Trap.pm',
+    configure_requires  =>
+    { 'Module::Build'     => 0,
+    },
     requires =>
     { 'base'              => 0, # core ...
       'constant'          => 0, # core ...
@@ -1,5 +1,19 @@
 Revision history for Test-Trap
 
+0.2.5	Sun Nov 16 18:31:42 CET 2014
+	This release localizes $! (ERRNO) for internal operations that
+	change it, as suggested by Felipe Gasper.  For the same
+	operations, it also localizes $^E (extended OS error).
+        Library files:
+        - Localize $! and $^E for internal operations that change them.
+        Tests:
+        - Check that bare test blocks leave $!, %!, and $^E unchanged,
+          but that blocks that modify $! and $^E still do so.
+        Also:
+        - Added Module::Build to the configure_requires.
+        - Fix emacs mode line on t/14-leaks.t.
+        - Better comments in lib/Test/Trap/Builder.pm.
+
 0.2.4	Sun Mar 30 10:02:16 CEST 2014
 	This release fixes a memory leak with the default tempfile
 	backend layer implementation for trapping output.  The
@@ -4,7 +4,7 @@
       "Eirik Berg Hanssen <ebhanssen@cpan.org>"
    ],
    "dynamic_config" : 1,
-   "generated_by" : "Module::Build version 0.38, CPAN::Meta::Converter version 2.110440",
+   "generated_by" : "Module::Build version 0.4003, CPAN::Meta::Converter version 2.133380",
    "license" : [
       "perl_5"
    ],
@@ -16,49 +16,49 @@
    "prereqs" : {
       "configure" : {
          "requires" : {
-            "Module::Build" : "0.38"
+            "Module::Build" : "0"
          }
       },
       "runtime" : {
          "requires" : {
-            "Carp" : 0,
-            "Data::Dump" : 0,
-            "Exporter" : 0,
-            "File::Temp" : 0,
-            "IO::Handle" : 0,
-            "Test::Builder" : 0,
-            "Test::More" : 0,
+            "Carp" : "0",
+            "Data::Dump" : "0",
+            "Exporter" : "0",
+            "File::Temp" : "0",
+            "IO::Handle" : "0",
+            "Test::Builder" : "0",
+            "Test::More" : "0",
             "Test::Tester" : "0.107",
-            "base" : 0,
-            "constant" : 0,
-            "lib" : 0,
+            "base" : "0",
+            "constant" : "0",
+            "lib" : "0",
             "perl" : "v5.6.2",
-            "strict" : 0,
-            "version" : 0,
-            "warnings" : 0
+            "strict" : "0",
+            "version" : "0",
+            "warnings" : "0"
          }
       }
    },
    "provides" : {
       "Test::Trap" : {
          "file" : "lib/Test/Trap.pm",
-         "version" : "v0.2.4"
+         "version" : "v0.2.5"
       },
       "Test::Trap::Builder" : {
          "file" : "lib/Test/Trap/Builder.pm",
-         "version" : "v0.2.4"
+         "version" : "v0.2.5"
       },
       "Test::Trap::Builder::PerlIO" : {
          "file" : "lib/Test/Trap/Builder/PerlIO.pm",
-         "version" : "v0.2.4"
+         "version" : "v0.2.5"
       },
       "Test::Trap::Builder::SystemSafe" : {
          "file" : "lib/Test/Trap/Builder/SystemSafe.pm",
-         "version" : "v0.2.4"
+         "version" : "v0.2.5"
       },
       "Test::Trap::Builder::TempFile" : {
          "file" : "lib/Test/Trap/Builder/TempFile.pm",
-         "version" : "v0.2.4"
+         "version" : "v0.2.5"
       }
    },
    "release_status" : "stable",
@@ -67,5 +67,5 @@
          "http://dev.perl.org/licenses/"
       ]
    },
-   "version" : "v0.2.4"
+   "version" : "v0.2.5"
 }
@@ -4,9 +4,9 @@ author:
   - 'Eirik Berg Hanssen <ebhanssen@cpan.org>'
 build_requires: {}
 configure_requires:
-  Module::Build: 0.38
+  Module::Build: 0
 dynamic_config: 1
-generated_by: 'Module::Build version 0.38, CPAN::Meta::Converter version 2.110440'
+generated_by: 'Module::Build version 0.4003, CPAN::Meta::Converter version 2.133380'
 license: perl
 meta-spec:
   url: http://module-build.sourceforge.net/META-spec-v1.4.html
@@ -15,19 +15,19 @@ name: Test-Trap
 provides:
   Test::Trap:
     file: lib/Test/Trap.pm
-    version: v0.2.4
+    version: v0.2.5
   Test::Trap::Builder:
     file: lib/Test/Trap/Builder.pm
-    version: v0.2.4
+    version: v0.2.5
   Test::Trap::Builder::PerlIO:
     file: lib/Test/Trap/Builder/PerlIO.pm
-    version: v0.2.4
+    version: v0.2.5
   Test::Trap::Builder::SystemSafe:
     file: lib/Test/Trap/Builder/SystemSafe.pm
-    version: v0.2.4
+    version: v0.2.5
   Test::Trap::Builder::TempFile:
     file: lib/Test/Trap/Builder/TempFile.pm
-    version: v0.2.4
+    version: v0.2.5
 requires:
   Carp: 0
   Data::Dump: 0
@@ -46,4 +46,4 @@ requires:
   warnings: 0
 resources:
   license: http://dev.perl.org/licenses/
-version: v0.2.4
+version: v0.2.5
@@ -1,4 +1,4 @@
-# Note: this file was auto-generated by Module::Build::Compat version 0.3800
+# Note: this file was auto-generated by Module::Build::Compat version 0.4003
 require 5.006002;
 use ExtUtils::MakeMaker;
 WriteMakefile
@@ -1,6 +1,6 @@
 package Test::Trap::Builder::PerlIO;
 
-use version; $VERSION = qv('0.2.4');
+use version; $VERSION = qv('0.2.5');
 
 use strict;
 use warnings;
@@ -30,7 +30,7 @@ Test::Trap::Builder::PerlIO - Output layer backend using PerlIO::scalar
 
 =head1 VERSION
 
-Version 0.2.4
+Version 0.2.5
 
 =head1 DESCRIPTION
 
@@ -1,6 +1,6 @@
 package Test::Trap::Builder::SystemSafe;
 
-use version; $VERSION = qv('0.2.4');
+use version; $VERSION = qv('0.2.5');
 
 use strict;
 use warnings;
@@ -16,11 +16,15 @@ sub import {
     if (tied *$globref or $fileno < 0) {
       $self->Exception("SystemSafe only works with real file descriptors; aborting");
     }
-    my ($fh, $file) = tempfile( UNLINK => 1 ); # XXX: Test?
+    my ($fh, $file) = do {
+      local ($!, $^E);
+      tempfile( UNLINK => 1 ); # XXX: Test?
+    };
     my ($fh_keeper, $autoflush_keeper);
     my $Die = $self->ExceptionFunction;
     for my $buffer ($self->{$name}) {
       $self->Teardown($_) for sub {
+        local ($!, $^E);
         if ($pid == $$) {
           # this process opened it, so it gets to collect the contents:
           local $/;
@@ -44,8 +48,11 @@ sub import {
       };
     }
     binmode $fh; # superfluous?
-    open $fh_keeper, ">&$fileno"
-      or $self->Exception("Cannot dup '$fileno' for $name: '$!'");
+    {
+      local ($!, $^E);
+      open $fh_keeper, ">&$fileno"
+        or $self->Exception("Cannot dup '$fileno' for $name: '$!'");
+    }
     $autoflush_keeper = $globref->autoflush;
     _close_reopen( $self->ExceptionFunction, $globref, $fileno, ">>$file",
                    sub {
@@ -61,6 +68,7 @@ sub import {
 
 sub _close_reopen {
   my ($Die, $glob, $fno_want, $what, $err) = @_;
+  local ($!, $^E);
   close *$glob;
   my @fh;
   while (1) {
@@ -94,7 +102,7 @@ Test::Trap::Builder::SystemSafe - "Safe" output layer backend using File::Temp
 
 =head1 VERSION
 
-Version 0.2.4
+Version 0.2.5
 
 =head1 DESCRIPTION
 
@@ -1,6 +1,6 @@
 package Test::Trap::Builder::TempFile;
 
-use version; $VERSION = qv('0.2.4');
+use version; $VERSION = qv('0.2.5');
 
 use strict;
 use warnings;
@@ -13,13 +13,17 @@ sub import {
     my $self = shift;
     my ($name, $fileno, $globref) = @_;
     my $pid = $$;
-    my ($fh, $file) = tempfile( UNLINK => 1 ); # XXX: Test?
+    my ($fh, $file) = do {
+      local ($!, $^E);
+      tempfile( UNLINK => 1 ); # XXX: Test?
+    };
     # make an alias to $self->{$name}, so that the closure does not hold $self:
     for my $buffer ($self->{$name}) {
       $self->Teardown($_) for sub {
         # if the file is opened by some other process, that one should deal with it:
         return unless $pid == $$;
         local $/;
+        local ($!, $^E);
         $buffer .= <$fh>;
         close $fh;
         unlink $file;
@@ -29,6 +33,7 @@ sub import {
     local *$globref;
     {
       no warnings 'io';
+      local ($!, $^E);
       open *$globref, '>>', $file;
     }
     binmode *$globref; # must write as we read.
@@ -47,7 +52,7 @@ Test::Trap::Builder::TempFile - Output layer backend using File::Temp
 
 =head1 VERSION
 
-Version 0.2.4
+Version 0.2.5
 
 =head1 DESCRIPTION
 
@@ -1,6 +1,6 @@
 package Test::Trap::Builder;
 
-use version; $VERSION = qv('0.2.4');
+use version; $VERSION = qv('0.2.5');
 
 use strict;
 use warnings;
@@ -142,6 +142,7 @@ BEGIN { # Test callback registration and test method generation:
     );
   # backwards compatibility -- don't use these:
   @argspec{ qw( object all indexed ) } = @argspec{ qw( trap entirety element ) };
+  # stringifying the CODE refs, that we may easily check if we have a specific one:
   my %isname    = ( $argspec{name}      => 1 );
   my %iselement = ( $argspec{element}   => 1 );
   my %takesarg  = ( $argspec{predicate} => 1 );
@@ -352,7 +353,7 @@ sub layer_implementation {
       next;
     }
     my ($name, $arg) =
-      /^ ( [^\(]+ )      # meth: anything but '('
+      /^ ( [^\(]+ )      # layer name: anything but '('
          (?:             # begin optional group
              \(          # literal '('
              ( [^\)]* )  # arg: anything but ')'
@@ -376,7 +377,7 @@ Test::Trap::Builder - Backend for building test traps
 
 =head1 VERSION
 
-Version 0.2.4
+Version 0.2.5
 
 =head1 SYNOPSIS
 
@@ -1,6 +1,6 @@
 package Test::Trap;
 
-use version; $VERSION = qv('0.2.4');
+use version; $VERSION = qv('0.2.5');
 
 use strict;
 use warnings;
@@ -301,7 +301,7 @@ Test::Trap - Trap exit codes, exceptions, output, etc.
 
 =head1 VERSION
 
-Version 0.2.4
+Version 0.2.5
 
 =head1 SYNOPSIS
 
@@ -14,7 +14,7 @@ BEGIN {
   local $@;
   eval qq{ use $pkg };
   if (exists &{"$pkg\::import"}) {
-    plan tests => 1 + 6*10 + 5*3 + 1; # 10 runtests; 3 inner_tests
+    plan tests => 1 + 6*10 + 5*3 + 13; # 10 runtests; 3 inner_tests; another bunch ...
   }
   else {
     plan skip_all => "$backend backend not supported; skipping";
@@ -165,4 +165,31 @@ trap {
 };
 unlike $T->stderr, qr/, \S+ line 1\./, 'No "<$f> line ..." stuff, please';
 
+# test the $! handling:
+my $errnum = 11; # "Resource temporarily unavailable" locally -- sounds good :-P
+my $errstring = do { local $! = $errnum; "$!" };
+my $erros = do { local $! = $errnum; $^E };
+my ($errsym) = do { local $! = $errnum; grep { $!{$_} } keys %! };
+for my $case ([Bare => sub { return 42 }], [Dying => sub { die 42 }], [Exiting => sub { exit 42 }]) {
+  my ($type, $code);
+  local $! = $errnum;
+  trap { $code->() };
+  my ($sym) = grep { $!{$_} } keys %!;
+  is $!+0, $errnum, "$type trap doesn't change errno (remains $errnum/$errstring)";
+  is $^E, $erros,  "$type trap doesn't change extended OS error (remains $erros)";
+  is $sym, $errsym, "$type trap doesn't change the error symbol (remains $errsym)";
+}
+
+{
+  local $! = $errnum;
+  trap {
+    $! = 0;
+    $^E = '';
+  };
+  my ($sym) = grep { $!{$_} } keys %!;
+  is $!+0, 0, "Errno-unsetting trap unsets errno (it's not localized)";
+  is $^E, '',  "Errno-unsetting trap unsets extended OS error (it's not localized)";
+  is $sym, undef, "Errno-unsetting trap unsets the error symbol (it's not localized)";
+}
+
 1;
@@ -2,7 +2,7 @@
 # -*- mode: cperl ; compile-command: "cd .. ; ./Build ; prove -vb t/04-*.t" -*-
 
 BEGIN { $_ = defined && /(.*)/ && $1 for @ENV{qw/ TMPDIR TEMP TMP /} } # taint vs tempfile
-use Test::More tests => 6;
+use Test::More tests => 9;
 use strict;
 use warnings;
 
@@ -22,6 +22,14 @@ BEGIN {
   use_ok( 'Test::Trap' );
 }
 
+# test the $! handling:
+my $errnum = 11; # "Resource temporarily unavailable" locally -- sounds good :-P
+my $errstring = do { local $! = $errnum; "$!" };
+my $erros = do { local $! = $errnum; $^E };
+my ($errsym) = do { local $! = $errnum; grep { $!{$_} } keys %! };
+
+$! = $errnum;
+
 trap { exit };
 is( $trap->exit, 0, "Trapped the first exit");
 trap {
@@ -34,6 +42,11 @@ trap {
 };
 like( $trap->stderr, qr/^Subroutine (?:CORE::GLOBAL::)?exit redefined at \Q${\__FILE__} line/, 'Override warning' );
 
+my ($sym) = grep { $!{$_} } keys %!;
+is $!+0, $errnum, "These traps don't change errno (remains $errnum/$errstring)";
+is $^E, $erros,  "These traps don't change extended OS error (remains $erros)";
+is $sym, $errsym, "These traps don't change the error symbol (remains $errsym)";
+
 $ready_for_exit++;
 
 exit;
@@ -1,5 +1,5 @@
 #!perl -T
-# -*- mode: cperl ; compile-command: "cd .. ; ./Build ; prove -vb t/13-*.t" -*-
+# -*- mode: cperl ; compile-command: "cd .. ; ./Build ; prove -vb t/14-*.t" -*-
 
 BEGIN {
   $_ = defined && /(.*)/ && $1 for @ENV{qw/ TMPDIR TEMP TMP /}; # taint vs tempfile