1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
|
#!perl -w # -*- perl -*-
# vim:sw=4:ts=8
$|=1;
use strict;
use warnings;
use Cwd;
use Config;
use Data::Dumper;
use Test::More;
use Getopt::Long;
use DBI qw(dbi_time);
if (my $ap = $ENV{DBI_AUTOPROXY}) { # limit the insanity
plan skip_all => "transport+policy tests skipped with non-gofer DBI_AUTOPROXY"
if $ap !~ /^dbi:Gofer/i;
plan skip_all => "transport+policy tests skipped with non-pedantic policy in DBI_AUTOPROXY"
if $ap !~ /policy=pedantic\b/i;
}
# 0=SQL::Statement if avail, 1=DBI::SQL::Nano
# next line forces use of Nano rather than default behaviour
$ENV{DBI_SQL_NANO}=1;
GetOptions(
'c|count=i' => \(my $opt_count = (-t STDOUT ? 100 : 0)),
'dbm=s' => \my $opt_dbm,
'v|verbose!' => \my $opt_verbose,
't|transport=s' => \my $opt_transport,
'p|policy=s' => \my $opt_policy,
) or exit 1;
# so users can try others from the command line
if (!$opt_dbm) {
# pick first available, starting with SDBM_File
for (qw( SDBM_File GDBM_File DB_File BerkeleyDB )) {
if (eval { local $^W; require "$_.pm" }) {
$opt_dbm = ($_);
last;
}
}
plan skip_all => 'No DBM modules available' if !$opt_dbm;
}
my $remote_driver_dsn = "dbm_type=$opt_dbm;lockfile=0";
my $remote_dsn = "dbi:DBM:$remote_driver_dsn";
my $timeout = 60; # for slow/overloaded systems (incl virtual machines with low priority)
plan 'no_plan';
if ($ENV{DBI_AUTOPROXY}) {
# this means we have DBD::Gofer => DBD::Gofer => DBD::DBM!
# rather than disable it we let it run because we're twisted
# and because it helps find more bugs (though debugging can be painful)
warn "\n$0 is running with DBI_AUTOPROXY enabled ($ENV{DBI_AUTOPROXY})\n"
unless $0 =~ /\bzv/; # don't warn for t/zvg_85gofer.t
}
# ensure subprocess (for pipeone and stream transport) will use the same modules as us, ie ./blib
local $ENV{PERL5LIB} = join $Config{path_sep}, @INC;
my %durations;
my $getcwd = getcwd();
my $username = eval { getpwuid($>) } || ''; # fails on windows
my $can_ssh = ($username && $username eq 'timbo' && -d '.svn');
my $perl = "$^X -Mblib=$getcwd/blib"; # ensure sameperl and our blib (note two spaces)
my %trials = (
null => {},
pipeone => { perl=>$perl, timeout=>$timeout },
stream => { perl=>$perl, timeout=>$timeout },
stream_ssh => ($can_ssh)
? { perl=>$perl, timeout=>$timeout, url => "ssh:$username\@localhost" }
: undef,
#http => { url => "http://localhost:8001/gofer" },
);
# too dependant on local config to make a standard test
delete $trials{http} unless $username eq 'timbo' && -d '.svn';
my @transports = ($opt_transport) ? ($opt_transport) : (sort keys %trials);
print "Transports: @transports\n";
my @policies = ($opt_policy) ? ($opt_policy) : qw(pedantic classic rush);
print "Policies: @policies\n";
print "Count: $opt_count\n";
for my $trial (@transports) {
(my $transport = $trial) =~ s/_.*//;
my $trans_attr = $trials{$trial}
or next;
# XXX temporary restrictions, hopefully
if ( ($^O eq 'MSWin32') || ($^O eq 'VMS') ) {
# stream needs Fcntl macro F_GETFL for non-blocking
# and pipe seems to hang on some windows systems
next if $transport eq 'stream' or $transport eq 'pipeone';
}
for my $policy_name (@policies) {
eval { run_tests($transport, $trans_attr, $policy_name) };
($@) ? fail("$trial: $@") : pass();
}
}
# to get baseline for comparisons if doing performance testing
run_tests('no', {}, 'pedantic') if $opt_count;
while ( my ($activity, $stats_hash) = each %durations ) {
print "\n";
$stats_hash->{'~baseline~'} = delete $stats_hash->{"no+pedantic"};
for my $perf_tag (reverse sort keys %$stats_hash) {
my $dur = $stats_hash->{$perf_tag} || 0.0000001;
printf " %6s %-16s: %.6fsec (%5d/sec)",
$activity, $perf_tag, $dur/$opt_count, $opt_count/$dur;
my $baseline_dur = $stats_hash->{'~baseline~'};
printf " %+5.1fms", (($dur-$baseline_dur)/$opt_count)*1000
unless $perf_tag eq '~baseline~';
print "\n";
}
}
sub run_tests {
my ($transport, $trans_attr, $policy_name) = @_;
my $policy = get_policy($policy_name);
my $skip_gofer_checks = ($transport eq 'no');
my $test_run_tag = "Testing $transport transport with $policy_name policy";
print "\n$test_run_tag\n";
my $driver_dsn = "transport=$transport;policy=$policy_name";
$driver_dsn .= join ";", '', map { "$_=$trans_attr->{$_}" } keys %$trans_attr
if %$trans_attr;
my $dsn = "dbi:Gofer:$driver_dsn;dsn=$remote_dsn";
$dsn = $remote_dsn if $transport eq 'no';
print " $dsn\n";
my $dbh = DBI->connect($dsn, undef, undef, { RaiseError => 1, PrintError => 0, ShowErrorStatement => 1 } );
die "$test_run_tag aborted: $DBI::errstr\n" unless $dbh; # no point continuing
ok $dbh, sprintf "should connect to %s", $dsn;
is $dbh->{Name}, ($policy->skip_connect_check)
? $driver_dsn
: $remote_driver_dsn;
ok $dbh->do("DROP TABLE IF EXISTS fruit");
ok $dbh->do("CREATE TABLE fruit (dKey INT, dVal VARCHAR(10))");
die "$test_run_tag aborted\n" if $DBI::err;
my $sth = do {
local $dbh->{RaiseError} = 0;
$dbh->prepare("complete non-sql gibberish");
};
($policy->skip_prepare_check)
? isa_ok $sth, 'DBI::st'
: is $sth, undef, 'should detect prepare failure';
ok my $ins_sth = $dbh->prepare("INSERT INTO fruit VALUES (?,?)");
ok $ins_sth->execute(1, 'oranges');
ok $ins_sth->execute(2, 'oranges');
my $rowset;
ok $rowset = $dbh->selectall_arrayref("SELECT dKey, dVal FROM fruit ORDER BY dKey");
is_deeply($rowset, [ [ '1', 'oranges' ], [ '2', 'oranges' ] ]);
ok $dbh->do("UPDATE fruit SET dVal='apples' WHERE dVal='oranges'");
ok $dbh->{go_response}->executed_flag_set, 'go_response executed flag should be true'
unless $skip_gofer_checks && pass();
ok $sth = $dbh->prepare("SELECT dKey, dVal FROM fruit");
ok $sth->execute;
ok $rowset = $sth->fetchall_hashref('dKey');
is_deeply($rowset, { '1' => { dKey=>1, dVal=>'apples' }, 2 => { dKey=>2, dVal=>'apples' } });
if ($opt_count and $transport ne 'pipeone') {
print "performance check - $opt_count selects and inserts\n";
my $start = dbi_time();
$dbh->selectall_arrayref("SELECT dKey, dVal FROM fruit")
for (1000..1000+$opt_count);
$durations{select}{"$transport+$policy_name"} = dbi_time() - $start;
# some rows in to get a (*very* rough) idea of overheads
$start = dbi_time();
$ins_sth->execute($_, 'speed')
for (1000..1000+$opt_count);
$durations{insert}{"$transport+$policy_name"} = dbi_time() - $start;
}
print "Testing go_request_count and caching of simple values\n";
my $go_request_count = $dbh->{go_request_count};
ok $go_request_count
unless $skip_gofer_checks && pass();
ok $dbh->do("DROP TABLE fruit");
is ++$go_request_count, $dbh->{go_request_count}
unless $skip_gofer_checks && pass();
# tests go_request_count, caching, and skip_default_methods policy
my $use_remote = ($policy->skip_default_methods) ? 0 : 1;
printf "use_remote=%s (policy=%s, transport=%s) %s\n",
$use_remote, $policy_name, $transport, $dbh->{dbi_default_methods}||'';
SKIP: {
skip "skip_default_methods checking doesn't work with Gofer over Gofer", 3
if $ENV{DBI_AUTOPROXY} or $skip_gofer_checks;
$dbh->data_sources({ foo_bar => $go_request_count });
is $dbh->{go_request_count}, $go_request_count + 1*$use_remote;
$dbh->data_sources({ foo_bar => $go_request_count }); # should use cache
is $dbh->{go_request_count}, $go_request_count + 1*$use_remote;
@_=$dbh->data_sources({ foo_bar => $go_request_count }); # no cached yet due to wantarray
is $dbh->{go_request_count}, $go_request_count + 2*$use_remote;
}
SKIP: {
skip "caching of metadata methods returning sth not yet implemented", 2;
print "Testing go_request_count and caching of sth\n";
$go_request_count = $dbh->{go_request_count};
my $sth_ti1 = $dbh->table_info("%", "%", "%", "TABLE", { foo_bar => $go_request_count });
is $go_request_count + 1, $dbh->{go_request_count};
my $sth_ti2 = $dbh->table_info("%", "%", "%", "TABLE", { foo_bar => $go_request_count }); # should use cache
is $go_request_count + 1, $dbh->{go_request_count};
}
ok $dbh->disconnect;
}
sub get_policy {
my ($policy_class) = @_;
$policy_class = "DBD::Gofer::Policy::$policy_class" unless $policy_class =~ /::/;
_load_class($policy_class) or die $@;
return $policy_class->new();
}
sub _load_class { # return true or false+$@
my $class = shift;
(my $pm = $class) =~ s{::}{/}g;
$pm .= ".pm";
return 1 if eval { require $pm };
delete $INC{$pm}; # shouldn't be needed (perl bug?) and assigning undef isn't enough
undef; # error in $@
}
1;
|