Introduce GOVERNANCE document and empty RESOLUTIONS file.
[dbsrgits/DBIx-Class.git] / t / storage / txn_scope_guard.t
index c0cb347..00d81a4 100644 (file)
@@ -1,10 +1,19 @@
+# Test is sufficiently involved to *want* to run with "maximum paranoia"
+BEGIN { $ENV{DBICTEST_OLD_MRO_SANITY_CHECK_ASSERTIONS} = 1 }
+
+BEGIN { do "./t/lib/ANFANG.pm" or die ( $@ || $! ) }
+
 use strict;
 use warnings;
 
 use Test::More;
 use Test::Warn;
 use Test::Exception;
-use lib qw(t/lib);
+
+use List::Util 'shuffle';
+use DBIx::Class::_Util 'sigwarn_silencer';
+
+
 use DBICTest;
 
 # Test txn_scope_guard
@@ -90,10 +99,8 @@ use DBICTest;
 {
   my $schema = DBICTest->init_schema;
 
-  no strict 'refs';
   no warnings 'redefine';
-
-  local *{DBIx::Class::Storage::DBI::txn_rollback} = sub { die 'die die my darling' };
+  local *DBIx::Class::Storage::DBI::txn_rollback = sub { die 'die die my darling' };
   Class::C3->reinitialize() if DBIx::Class::_ENV_::OLD_MRO;
 
   throws_ok (sub {
@@ -106,7 +113,7 @@ use DBICTest;
     #$schema->storage->_dbh( $schema->storage->_dbh->clone );
 
     die 'Deliberate exception';
-  }, ($] >= 5.013008 )
+  }, ( "$]" >= 5.013008 )
     ? qr/Deliberate exception/s # temporary until we get the generic exception wrapper rolling
     : qr/Deliberate exception.+Rollback failed/s
   );
@@ -117,36 +124,9 @@ use DBICTest;
 
 # make sure it warns *big* on failed rollbacks
 # test with and without a poisoned $@
-for my $poison (0,1) {
-
-  my $schema = DBICTest->init_schema();
-
-  no strict 'refs';
-  no warnings 'redefine';
-  local *{DBIx::Class::Storage::DBI::txn_rollback} = sub { die 'die die my darling' };
-  Class::C3->reinitialize() if DBIx::Class::_ENV_::OLD_MRO;
-
-#The warn from within a DESTROY callback freaks out Test::Warn, do it old-school
-=begin
-  warnings_exist (
-    sub {
-      my $guard = $schema->txn_scope_guard;
-      $schema->resultset ('Artist')->create ({ name => 'bohhoo'});
-
-      # this should freak out the guard rollback
-      # but it won't work because DBD::SQLite is buggy
-      # instead just install a toxic rollback above
-      #$schema->storage->_dbh( $schema->storage->_dbh->clone );
-    },
-    [
-      qr/A DBIx::Class::Storage::TxnScopeGuard went out of scope without explicit commit or error. Rolling back./,
-      qr/\*+ ROLLBACK FAILED\!\!\! \*+/,
-    ],
-    'proper warnings generated on out-of-scope+rollback failure'
-  );
-=cut
-
-# delete this once the above works properly (same test)
+require DBICTest::AntiPattern::TrueZeroLen;
+require DBICTest::AntiPattern::NullObject;
+{
   my @want = (
     qr/A DBIx::Class::Storage::TxnScopeGuard went out of scope without explicit commit or error. Rolling back./,
     qr/\*+ ROLLBACK FAILED\!\!\! \*+/,
@@ -161,16 +141,105 @@ for my $poison (0,1) {
       warn $_[0];
     }
   };
-  {
-      eval { die 'GIFT!' if $poison };
-      my $guard = $schema->txn_scope_guard;
-      $schema->resultset ('Artist')->create ({ name => 'bohhoo'});
+
+
+  # we are driving manually here, do not allow interference
+  local $SIG{__DIE__} if $SIG{__DIE__};
+
+
+  no warnings 'redefine';
+  local *DBIx::Class::Storage::DBI::txn_rollback = sub { die 'die die my darling' };
+  Class::C3->reinitialize() if DBIx::Class::_ENV_::OLD_MRO;
+
+  my @poisons = shuffle (
+    undef,
+    DBICTest::AntiPattern::TrueZeroLen->new,
+    DBICTest::AntiPattern::NullObject->new,
+    'GIFT!',
+  );
+
+  for my $pre_poison (@poisons) {
+    for my $post_poison (@poisons) {
+
+      @w = ();
+
+      my $schema = DBICTest->init_schema(no_populate => 1);
+
+      # the actual scope where the guard is created/freed
+      {
+        # in this particular case these are not the warnings we are looking for
+        local $SIG{__WARN__} = sigwarn_silencer qr/implementing the so called null-object-pattern/;
+
+        # if is inside the eval, to clear $@ in the undef case
+        eval { die $pre_poison if defined $pre_poison };
+
+        my $guard = $schema->txn_scope_guard;
+
+        eval { die $post_poison if defined $post_poison };
+
+        $schema->resultset ('Artist')->create ({ name => "bohhoo, too bad we'll roll you back"});
+      }
+
+      local $TODO = 'Do not know how to deal with trapped exceptions occuring after guard instantiation...'
+        if ( defined $post_poison and (
+          # take no chances on installation
+          DBICTest::RunMode->is_plain
+            or
+          # I do not understand why but on <= 5.8.8 and on 5.10.0
+          # "$pre_poison == $post_poison == string" passes...
+          # so todoify 5.8.9 and 5.10.1+, and deal with the rest below
+          ( ( "$]" > 5.008008 and "$]" < 5.010000 ) or "$]" > 5.010000 )
+            or
+          ! defined $pre_poison
+            or
+          length ref $pre_poison
+            or
+          length ref $post_poison
+        ));
+
+      is (@w, 2, sprintf 'Both expected warnings found - $@ poisonstate:   pre-poison:%s   post-poison:%s',
+        map {
+          ! defined $_      ? 'UNDEF'
+        : ! length ref $_   ? $_
+                            : ref $_
+
+        } ($pre_poison, $post_poison)
+      );
+
+      # just to mask off warning since we could not disconnect above
+      $schema->storage->_dbh->disconnect;
+    }
   }
+}
 
-  is (@w, 2, 'Both expected warnings found' . ($poison ? ' (after $@ poisoning)' : '') );
+# add a TODO to catch when Text::Balanced is finally fixed
+# https://rt.cpan.org/Public/Bug/Display.html?id=74994
+#
+# while it doesn't matter much for DBIC itself, this particular bug
+# is a *BANE*, and DBIC is to bump its dep as soon as possible
+{
 
-  # just to mask off warning since we could not disconnect above
-  $schema->storage->_dbh->disconnect;
+  require Text::Balanced;
+
+  my @w;
+  local $SIG{__WARN__} = sub {
+    $_[0] =~ /External exception class .+? \Qimplements partial (broken) overloading/
+      ? push @w, @_
+      : warn @_
+  };
+
+  lives_ok {
+    # this is what poisons $@
+    Text::Balanced::extract_bracketed( '(foo', '()' );
+    DBIx::Class::_Util::is_exception($@);
+
+    my $s = DBICTest::Schema->connect('dbi:SQLite::memory:');
+    my $g = $s->txn_scope_guard;
+    $g->commit;
+  } 'Broken Text::Balanced is not screwing up txn_guard';
+
+  local $TODO = 'RT#74994 *STILL* not fixed';
+  is(scalar @w, 0, 'no warnings \o/');
 }
 
 done_testing;