#!perl -w
#
# check that the inner-method lookup cache works
# (or rather, check that it doesn't cache things when it shouldn't)

BEGIN { eval "use threads;" }	# Must be first
my $use_threads_err = $@;
use Config qw(%Config);
# With this test code and threads, 5.8.1 has issues with freeing freed
# scalars, while 5.8.9 doesn't; I don't know about in-between - DAPM
my $has_threads = $Config{useithreads};
die $use_threads_err if $has_threads && $use_threads_err;


use strict;

$|=1;
$^W=1;



use Test::More tests => 49;

BEGIN {
    use_ok( 'DBI' );
}

sub new_handle {
    my $dbh = DBI->connect("dbi:Sponge:foo","","", {
	PrintError => 0,
	RaiseError => 1,
    });

    my $sth = $dbh->prepare("foo",
	# data for DBD::Sponge to return via fetch
	{ rows =>
	    [
		[ "row0" ],
		[ "row1" ],
		[ "row2" ],
		[ "row3" ],
		[ "row4" ],
		[ "row5" ],
		[ "row6" ],
	    ],
	}
    );

    return ($dbh, $sth);
}


sub Foo::local1 { [ "local1" ] };
sub Foo::local2 { [ "local2" ] };


my $fetch_hook;
{
    package Bar;
    @Bar::ISA = qw(DBD::_::st);
    sub fetch { &$fetch_hook };
}

sub run_tests {
    my ($desc, $dbh, $sth) = @_;
    my $row = $sth->fetch;
    is($row->[0], "row0", "$desc row0");

    {
	# replace CV slot
	no warnings 'redefine';
	local *DBD::Sponge::st::fetch = sub { [ "local0" ] };
	$row = $sth->fetch;
	is($row->[0], "local0", "$desc local0");
    }
    $row = $sth->fetch;
    is($row->[0], "row1", "$desc row1");

    {
	# replace GP
	local *DBD::Sponge::st::fetch = *Foo::local1;
	$row = $sth->fetch;
	is($row->[0], "local1", "$desc local1");
    }
    $row = $sth->fetch;
    is($row->[0], "row2", "$desc row2");

    {
	# replace GV
	local $DBD::Sponge::st::{fetch} = *Foo::local2;
	$row = $sth->fetch;
	is($row->[0], "local2", "$desc local2");
    }
    $row = $sth->fetch;
    is($row->[0], "row3", "$desc row3");

    {
	# @ISA = NoSuchPackage
	local $DBD::Sponge::st::{fetch};
	local @DBD::Sponge::st::ISA = qw(NoSuchPackage);
	eval { local $SIG{__WARN__} = sub {}; $row = $sth->fetch };
	like($@, qr/Can't locate DBI object method/, "$desc locate DBI object");
    }
    $row = $sth->fetch;
    is($row->[0], "row4", "$desc row4");

    {
	# @ISA = Bar
	$fetch_hook = \&DBD::Sponge::st::fetch;
	local $DBD::Sponge::st::{fetch};
	local @DBD::Sponge::st::ISA = qw(Bar);
	$row = $sth->fetch;
	is($row->[0], "row5", "$desc row5");
	$fetch_hook = sub { [ "local3" ] };
	$row = $sth->fetch;
	is($row->[0], "local3", "$desc local3");
    }
    $row = $sth->fetch;
    is($row->[0], "row6", "$desc row6");
}

run_tests("plain", new_handle());


SKIP: {
    skip "no threads / perl < 5.8.9", 12 unless $has_threads;
    # only enable this when handles are allowed to be shared across threads
    #{
    #    my @h = new_handle();
    #    threads->new(sub { run_tests("threads", @h) })->join; 
    #}
    threads->new(sub { run_tests("threads-h", new_handle()) })->join; 
};

# using weaken attaches magic to the CV; see whether this interferes
# with the cache magic

use Scalar::Util qw(weaken);
my $fetch_ref = \&DBI::st::fetch;
weaken $fetch_ref;
run_tests("magic", new_handle());

SKIP: {
    skip "no threads / perl < 5.8.9", 12 unless $has_threads;

    skip "weaken itself is buggy on 5.8.1 (magic killbackrefs panic "
        ."triggered by threads, fixed in 5.8.2)"
    , 12 unless $] > 5.008001;
    # only enable this when handles are allowed to be shared across threads
    #{
    #    my @h = new_handle();
    #    threads->new(sub { run_tests("threads", @h) })->join; 
    #}
    threads->new(sub { run_tests("magic threads-h", new_handle()) })->join; 
};

1;
