From 7c48e67cf07ee41bfde7139a62bb232bd23a4a48 Mon Sep 17 00:00:00 2001 From: Lorry Tar Creator Date: Wed, 6 Jun 2012 16:41:29 +0000 Subject: Imported from /srv/lorry/lorry-area/perl-dbi-tarball/DBI-1.622.tar.gz. --- t/70callbacks.t | 207 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+) create mode 100644 t/70callbacks.t (limited to 't/70callbacks.t') diff --git a/t/70callbacks.t b/t/70callbacks.t new file mode 100644 index 0000000..4acb9c3 --- /dev/null +++ b/t/70callbacks.t @@ -0,0 +1,207 @@ +#!perl -w +# vim:ts=8:sw=4 + +use strict; + +use Test::More; +use DBI; + +BEGIN { + plan skip_all => '$h->{Callbacks} attribute not supported for DBI::PurePerl' + if $DBI::PurePerl && $DBI::PurePerl; # doubled to avoid typo warning + plan tests => 63; +} + +$| = 1; +my $dsn = "dbi:ExampleP:"; +my %called; + +ok my $dbh = DBI->connect($dsn, '', ''), "Create dbh"; + +is $dbh->{Callbacks}, undef, "Callbacks initially undef"; +ok $dbh->{Callbacks} = my $cb = { }; +is ref $dbh->{Callbacks}, 'HASH', "Callbacks can be set to a hash ref"; +is $dbh->{Callbacks}, $cb, "Callbacks set to same hash ref"; + +$dbh->{Callbacks} = undef; +is $dbh->{Callbacks}, undef, "Callbacks set to undef again"; + +ok $dbh->{Callbacks} = { + ping => sub { + is $_, 'ping', '$_ holds method name'; + is @_, 1, '@_ holds 1 values'; + is ref $_[0], 'DBI::db', 'first is $dbh'; + $called{$_}++; + return; + }, + quote_identifier => sub { + is @_, 4, '@_ holds 4 values'; + my $dbh = shift; + is ref $dbh, 'DBI::db', 'first is $dbh'; + is $_[0], 'foo'; + is $_[1], 'bar'; + is $_[2], undef; + $_[2] = { baz => 1 }; + $called{$_}++; + return (1,2,3); # return something - which is not allowed + }, + disconnect => sub { # test die from within a callback + die "You can't disconnect that easily!\n"; + }, + "*" => sub { + $called{$_}++; + return; + } +}; +is keys %{ $dbh->{Callbacks} }, 4; + +is ref $dbh->{Callbacks}->{ping}, 'CODE'; + +$_ = 42; +ok $dbh->ping; +is $called{ping}, 1; +is $_, 42, '$_ not altered by callback'; + +ok $dbh->ping; +is $called{ping}, 2; + +ok $dbh->type_info_all; +is $called{type_info_all}, 1, 'fallback callback'; + +my $attr; +eval { $dbh->quote_identifier('foo','bar', $attr) }; +is $called{quote_identifier}, 1; +ok $@, 'quote_identifier callback caused fatal error'; +is ref $attr, 'HASH', 'param modified by callback - not recommended!'; + +ok !eval { $dbh->disconnect }; +ok $@, "You can't disconnect that easily!\n"; + +$dbh->{Callbacks} = undef; +ok $dbh->ping; +is $called{ping}, 2; # no change + + +# --- test skipping dispatch and fallback callbacks + +$dbh->{Callbacks} = { + ping => sub { + undef $_; # tell dispatch to not call the method + return "42 bells"; + }, + data_sources => sub { + my ($h, $values_to_return) = @_; + undef $_; # tell dispatch to not call the method + my @ret = 11..10+($values_to_return||0); + return @ret; + }, + commit => sub { # test using set_err within a callback + my $h = shift; + undef $_; # tell dispatch to not call the method + return $h->set_err(42, "faked commit failure"); + }, +}; + +# these tests are slightly convoluted because messing with the stack is bad for +# your mental health +my $rv = $dbh->ping; +is $rv, "42 bells"; +my @rv = $dbh->ping; +is scalar @rv, 1, 'should return a single value in list context'; +is "@rv", "42 bells"; +# test returning lists with different number of args to test +# the stack handling in the dispatch code +is join(":", $dbh->data_sources()), ""; +is join(":", $dbh->data_sources(0)), ""; +is join(":", $dbh->data_sources(1)), "11"; +is join(":", $dbh->data_sources(2)), "11:12"; + +{ +local $dbh->{RaiseError} = 1; +local $dbh->{PrintError} = 0; +is eval { $dbh->commit }, undef, 'intercepted commit should return undef'; +like $@, '/DBD::\w+::db commit failed: faked commit failure/'; +is $DBI::err, 42; +is $DBI::errstr, "faked commit failure"; +} + +# --- test connect_cached.* + +=for comment XXX + +The big problem here is that conceptually the Callbacks attribute +is applied to the $dbh _during_ the $drh->connect() call, so you can't +set a callback on "connect" on the $dbh because connect isn't called +on the dbh, but on the $drh. + +So a "connect" callback would have to be defined on the $drh, but that's +cumbersome for the user and then it would apply to all future connects +using that driver. + +The best thing to do is probably to special-case "connect", "connect_cached" +and (the already special-case) "connect_cached.reused". + +=cut + +my @args = ( + $dsn, '', '', { + Callbacks => { + "connect_cached.new" => sub { $called{new}++; return; }, + "connect_cached.reused" => sub { $called{cached}++; return; }, + } + } +); + +%called = (); + +ok $dbh = DBI->connect(@args), "Create handle with callbacks"; +is keys %called, 0, 'no callback for plain connect'; + +ok $dbh = DBI->connect_cached(@args), "Create handle with callbacks"; +is $called{new}, 1, "connect_cached.new called"; +is $called{cached}, undef, "connect_cached.reused not yet called"; + +ok $dbh = DBI->connect_cached(@args), "Create handle with callbacks"; +is $called{cached}, 1, "connect_cached.reused called"; +is $called{new}, 1, "connect_cached.new not called again"; + + +# --- test ChildCallbacks. +%called = (); +$args[-1] = { + Callbacks => my $dbh_callbacks = { + ping => sub { $called{ping}++; return; }, + ChildCallbacks => my $sth_callbacks = { + execute => sub { $called{execute}++; return; }, + fetch => sub { $called{fetch}++; return; }, + } + } +}; + +ok $dbh = DBI->connect(@args), "Create handle with ChildCallbacks"; +ok $dbh->ping, 'Ping'; +is $called{ping}, 1, 'Ping callback should have been called'; +ok my $sth = $dbh->prepare('SELECT name from t'), 'Prepare a statement handle (child)'; +ok $sth->{Callbacks}, 'child should have Callbacks'; +is $sth->{Callbacks}, $sth_callbacks, "child Callbacks should be ChildCallbacks of parent" + or diag "(dbh Callbacks is $dbh_callbacks)"; +ok $sth->execute, 'Execute'; +is $called{execute}, 1, 'Execute callback should have been called'; +ok $sth->fetch, 'Fetch'; +is $called{fetch}, 1, 'Fetch callback should have been called'; + +__END__ + +A generic 'transparent' callback looks like this: +(this assumes only scalar context will be used) + + sub { + my $h = shift; + return if our $avoid_deep_recursion->{"$h $_"}++; + my $this = $h->$_(@_); + undef $_; # tell DBI not to call original method + return $this; # tell DBI to return this instead + }; + +XXX should add a test for this +XXX even better would be to run chunks of the test suite with that as a '*' callback. In theory everything should pass (except this test file, naturally).. -- cgit v1.2.1