X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=t%2Fstorage%2Ftxn.t;h=09260f0076466b72358e1d2423ca7f5743551e94;hb=9f30030205481d5b2c8d0f7d637259c5ae6dd52d;hp=24d40f12e4bd89b74d4f87395f81c716a8f44e8b;hpb=471a5fddd105be3f526ab3be978dc74f6b990609;p=dbsrgits%2FDBIx-Class.git diff --git a/t/storage/txn.t b/t/storage/txn.t index 24d40f1..09260f0 100644 --- a/t/storage/txn.t +++ b/t/storage/txn.t @@ -15,7 +15,7 @@ my $code = sub { year => 2006, }) foreach (@cd_titles); - return $artist->cds; + return $artist->cds->all; }; # Test checking of parameters @@ -112,7 +112,7 @@ for my $want (0,1) { die "$$ starts in txn!" if $s->storage->transaction_depth != 0; $s->txn_do ( sub { die "$$ not in txn!" if $s->storage->transaction_depth == 0; - $s->storage->dbh->do('SELECT 1') } + $s->storage->dbh->do('SELECT 1') } ); die "$$ did not finish txn!" if $s->storage->transaction_depth != 0; }, @@ -127,11 +127,12 @@ for my $want (0,1) { $guard->commit }, ) { - push @pids, fork(); + my $pid = fork(); die "Unable to fork: $!\n" - if ! defined $pids[-1]; + if ! defined $pid; - if ($pids[-1]) { + if ($pid) { + push @pids, $pid; next; } @@ -159,16 +160,13 @@ for my $want (0,1) { my $guard = $schema->txn_scope_guard; $schema->txn_do( sub { die } ); }; + is( $schema->storage->transaction_depth, 0, 'Transaction successfully aborted' ); $schema->txn_do( sub { ok ($schema->storage->_dbh->do ('SELECT 1'), "Query after exceptions ok ($_)"); }); } - for my $pid ( $schema->txn_do ( sub { _forking_action ($schema) } ) ) { - waitpid ($pid, 0); - ok (! $?, "Child $pid exit ok (pass $pass)"); - isa_ok ($schema->resultset ('Artist')->find ({ name => "forking action $pid" }), 'DBIx::Class::Row'); - } + $schema->txn_do ( sub { _test_forking_action ($schema, $pass) } ); } } @@ -184,39 +182,38 @@ for my $want (0,1) { my $guard = $schema->txn_scope_guard; $schema->txn_do( sub { die } ); }; + is( $schema->storage->transaction_depth, 0, 'Transaction successfully aborted' ); $schema->txn_do( sub { ok ($schema->storage->_dbh->do ('SELECT 1'), "Query after exceptions ok ($_)"); }); } - my @pids; my $guard = $schema->txn_scope_guard; - _forking_action ($schema); + my @pids = _test_forking_action ($schema, $pass); $guard->commit; - - for my $pid (@pids) { - waitpid ($pid, 0); - ok (! $?, "Child $pid exit ok (pass $pass)"); - isa_ok ($schema->resultset ('Artist')->find ({ name => "forking action $pid" }), 'DBIx::Class::Row'); - } } } -sub _forking_action { - my $schema = shift; +sub _test_forking_action { + my ($schema, $pass) = @_; my @pids; - while (@pids < 5) { - push @pids, fork(); + SKIP: for my $count (1 .. 5) { + + skip 'Weird DBI General Protection Faults, skip forking tests (RT#63104)', 5 + if $^O eq 'MSWin32'; + + my $pid = fork(); die "Unable to fork: $!\n" - if ! defined $pids[-1]; + if ! defined $pid; - if ($pids[-1]) { + if ($pid) { + push @pids, $pid; next; } - if (@pids % 2) { + if ($count % 2) { $schema->txn_do (sub { my $depth = $schema->storage->transaction_depth; die "$$(txn_do)unexpected txn depth $depth!" if $depth != 1; @@ -234,7 +231,17 @@ sub _forking_action { exit 0; } - return @pids; + for my $pid (@pids) { + waitpid ($pid, 0); + ok (! $?, "Child $pid exit ok (pass $pass)"); + } + + # it is important to reap all children before checking the final db-state + # otherwise a deadlock may occur between the transactions running in the + # children and the query of the parent + for my $pid (@pids) { + isa_ok ($schema->resultset ('Artist')->find ({ name => "forking action $pid" }), 'DBIx::Class::Row'); + } } my $fail_code = sub { @@ -277,14 +284,14 @@ my $fail_code = sub { my $artist = $schema->resultset('Artist')->find(3); # Force txn_rollback() to throw an exception - no warnings 'redefine'; - no strict 'refs'; + no warnings qw/once redefine/; - # die in rollback - local *{"DBIx::Class::Storage::DBI::SQLite::txn_rollback"} = sub{ - my $storage = shift; - die 'FAILED'; - }; + # this should logically work just fine - but it does not, + # only direct override of the existing method dtrt + #local *DBIx::Class::Storage::DBI::SQLite::txn_rollback = sub { die 'FAILED' }; + + local *DBIx::Class::Storage::DBI::txn_rollback = sub { die 'FAILED' }; + Class::C3->reinitialize() if DBIx::Class::_ENV_::OLD_MRO; throws_ok ( sub { @@ -342,7 +349,9 @@ my $fail_code = sub { ok(!defined($cd), q{failed txn_do didn't add failed txn's cd}); } + # Grab a new schema to test txn before connect +# also test nested txn exception { my $schema = DBICTest->init_schema(no_deploy => 1); lives_ok (sub { @@ -350,16 +359,15 @@ my $fail_code = sub { $schema->txn_begin(); }, 'Pre-connection nested transactions.'); - # although not connected DBI would still warn about rolling back at disconnect - $schema->txn_rollback; - $schema->txn_rollback; + throws_ok( sub { $schema->txn_rollback }, 'DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION', 'got proper nested rollback exception' ); } # make sure AutoCommit => 0 on external handles behaves correctly with scope_guard warnings_are { - my $factory = DBICTest->init_schema (AutoCommit => 0); + my $factory = DBICTest->init_schema; cmp_ok ($factory->resultset('CD')->count, '>', 0, 'Something to delete'); my $dbh = $factory->storage->dbh; + $dbh->{AutoCommit} = 0; ok (!$dbh->{AutoCommit}, 'AutoCommit is off on $dbh'); my $schema = DBICTest::Schema->connect (sub { $dbh }); @@ -379,14 +387,14 @@ warnings_are { # make sure AutoCommit => 0 on external handles behaves correctly with txn_do warnings_are { - my $factory = DBICTest->init_schema (AutoCommit => 0); + my $factory = DBICTest->init_schema; cmp_ok ($factory->resultset('CD')->count, '>', 0, 'Something to delete'); my $dbh = $factory->storage->dbh; + $dbh->{AutoCommit} = 0; ok (!$dbh->{AutoCommit}, 'AutoCommit is off on $dbh'); my $schema = DBICTest::Schema->connect (sub { $dbh }); - lives_ok ( sub { $schema->txn_do (sub { $schema->resultset ('CD')->delete }); }, 'No attempt to start a atransaction with txn_do');