| 1 | # $Id$ |
|---|
| 2 | |
|---|
| 3 | use strict; |
|---|
| 4 | use File::Spec; |
|---|
| 5 | use Carp qw(croak); |
|---|
| 6 | |
|---|
| 7 | sub run_tests { |
|---|
| 8 | my ($n, $code) = @_; |
|---|
| 9 | |
|---|
| 10 | run_tests_mysql($n, $code); |
|---|
| 11 | run_tests_pgsql($n, $code); |
|---|
| 12 | run_tests_sqlite($n, $code); |
|---|
| 13 | } |
|---|
| 14 | |
|---|
| 15 | sub run_tests_innodb { |
|---|
| 16 | my ($n, $code) = @_; |
|---|
| 17 | run_tests_mysql($n, $code, 1); |
|---|
| 18 | } |
|---|
| 19 | |
|---|
| 20 | sub run_tests_mysql { |
|---|
| 21 | my ($n, $code, $innodb) = @_; |
|---|
| 22 | SKIP: { |
|---|
| 23 | local $ENV{USE_MYSQL} = 1; |
|---|
| 24 | local $ENV{TS_DB_USER} ||= 'root'; |
|---|
| 25 | my $dbh = eval { mysql_dbh() }; |
|---|
| 26 | skip "MySQL not accessible as root on localhost", $n if $@; |
|---|
| 27 | skip "InnoDB not available on localhost's MySQL", $n if $innodb && ! has_innodb($dbh); |
|---|
| 28 | $code->(); |
|---|
| 29 | } |
|---|
| 30 | } |
|---|
| 31 | |
|---|
| 32 | sub run_tests_pgsql { |
|---|
| 33 | my ($n, $code) = @_; |
|---|
| 34 | SKIP: { |
|---|
| 35 | local $ENV{USE_PGSQL} = 1; |
|---|
| 36 | local $ENV{TS_DB_USER} ||= 'postgres'; |
|---|
| 37 | my $dbh = eval { pgsql_dbh() }; |
|---|
| 38 | skip "PgSQL not accessible as root on localhost", $n if $@; |
|---|
| 39 | $code->(); |
|---|
| 40 | } |
|---|
| 41 | } |
|---|
| 42 | |
|---|
| 43 | sub run_tests_sqlite { |
|---|
| 44 | my ($n, $code) = @_; |
|---|
| 45 | |
|---|
| 46 | # SQLite |
|---|
| 47 | SKIP: { |
|---|
| 48 | my $rv = eval "use DBD::SQLite; 1"; |
|---|
| 49 | $rv = 0 if $ENV{SKIP_SQLITE}; |
|---|
| 50 | skip "SQLite not installed", $n if !$rv; |
|---|
| 51 | $code->(); |
|---|
| 52 | } |
|---|
| 53 | } |
|---|
| 54 | |
|---|
| 55 | sub test_client { |
|---|
| 56 | my %opts = @_; |
|---|
| 57 | my $dbs = delete $opts{dbs}; |
|---|
| 58 | my $init = delete $opts{init}; |
|---|
| 59 | my $pfx = delete $opts{dbprefix}; |
|---|
| 60 | croak "'dbs' not an ARRAY" unless ref $dbs eq "ARRAY"; |
|---|
| 61 | croak "unknown opts" if %opts; |
|---|
| 62 | $init = 1 unless defined $init; |
|---|
| 63 | |
|---|
| 64 | if ($init) { |
|---|
| 65 | setup_dbs({ prefix => $pfx }, $dbs); |
|---|
| 66 | } |
|---|
| 67 | |
|---|
| 68 | if ($ENV{USE_DBH_FOR_TEST}) { |
|---|
| 69 | my @tmp; |
|---|
| 70 | for (@$dbs) { eval { |
|---|
| 71 | my $dsn = dsn_for($_); |
|---|
| 72 | my $dbh = DBI->connect( $dsn, "root", "", { |
|---|
| 73 | RaiseError => 1, |
|---|
| 74 | PrintError => 0, |
|---|
| 75 | AutoCommit => 1, |
|---|
| 76 | } ) or die $DBI::errstr; |
|---|
| 77 | my $driver = Data::ObjectDriver::Driver::DBI->new( dbh => $dbh); |
|---|
| 78 | push @tmp, { driver => $driver, prefix => $pfx }; |
|---|
| 79 | } } |
|---|
| 80 | return TheSchwartz->new(databases => [@tmp]); |
|---|
| 81 | } else { |
|---|
| 82 | return TheSchwartz->new(databases => [ |
|---|
| 83 | map { { |
|---|
| 84 | dsn => dsn_for($_), |
|---|
| 85 | user => $ENV{TS_DB_USER}, |
|---|
| 86 | pass => $ENV{TS_DB_PASS}, |
|---|
| 87 | prefix => $pfx, |
|---|
| 88 | } } @$dbs |
|---|
| 89 | ]); |
|---|
| 90 | } |
|---|
| 91 | } |
|---|
| 92 | |
|---|
| 93 | sub has_innodb { |
|---|
| 94 | my $dbh = shift; |
|---|
| 95 | my $tmpname = "test_to_see_if_innoavail"; |
|---|
| 96 | $dbh->do("CREATE TABLE IF NOT EXISTS $tmpname (i int) ENGINE=INNODB") |
|---|
| 97 | or return 0; |
|---|
| 98 | my @row = $dbh->selectrow_array("SHOW CREATE TABLE $tmpname"); |
|---|
| 99 | my $row = join(' ', @row); |
|---|
| 100 | my $has_it = ($row =~ /=InnoDB/i); |
|---|
| 101 | $dbh->do("DROP TABLE $tmpname"); |
|---|
| 102 | return $has_it; |
|---|
| 103 | } |
|---|
| 104 | |
|---|
| 105 | sub schema_file { |
|---|
| 106 | return "doc/schema.sql" if $ENV{USE_MYSQL}; |
|---|
| 107 | return "doc/schema-postgres.sql" if $ENV{USE_PGSQL}; |
|---|
| 108 | return "t/schema-sqlite.sql"; |
|---|
| 109 | } |
|---|
| 110 | |
|---|
| 111 | sub db_filename { |
|---|
| 112 | my($dbname) = @_; |
|---|
| 113 | return $dbname . '.db'; |
|---|
| 114 | } |
|---|
| 115 | |
|---|
| 116 | sub mysql_dbname { |
|---|
| 117 | my($dbname) = @_; |
|---|
| 118 | return 't_sch_' . $dbname; |
|---|
| 119 | } |
|---|
| 120 | |
|---|
| 121 | sub dsn_for { |
|---|
| 122 | my $dbname = shift; |
|---|
| 123 | if ($ENV{USE_MYSQL}) { |
|---|
| 124 | return 'dbi:mysql:' . mysql_dbname($dbname); |
|---|
| 125 | } |
|---|
| 126 | elsif ($ENV{USE_PGSQL}) { |
|---|
| 127 | return 'dbi:Pg:dbname=' . mysql_dbname($dbname); |
|---|
| 128 | } else { |
|---|
| 129 | return 'dbi:SQLite:dbname=' . db_filename($dbname); |
|---|
| 130 | } |
|---|
| 131 | } |
|---|
| 132 | |
|---|
| 133 | sub setup_dbs { |
|---|
| 134 | shift if $_[0] =~ /\.sql$/; # skip filenames (old) |
|---|
| 135 | |
|---|
| 136 | my $opts = ref $_[0] eq "HASH" ? shift : {}; |
|---|
| 137 | my $pfx = delete $opts->{prefix} || ""; |
|---|
| 138 | die "unknown opts" if %$opts; |
|---|
| 139 | |
|---|
| 140 | my(@dbs) = @_; |
|---|
| 141 | my $dbs = ref $dbs[0] ? $dbs[0] : \@dbs; # support array or arrayref (old) |
|---|
| 142 | |
|---|
| 143 | my $schema = schema_file(); |
|---|
| 144 | teardown_dbs(@$dbs); |
|---|
| 145 | for my $dbname (@$dbs) { |
|---|
| 146 | if ($ENV{USE_MYSQL}) { |
|---|
| 147 | create_mysql_db(mysql_dbname($dbname)); |
|---|
| 148 | } |
|---|
| 149 | elsif ($ENV{USE_PGSQL}) { |
|---|
| 150 | create_pgsql_db(mysql_dbname($dbname)); |
|---|
| 151 | } |
|---|
| 152 | my $dbh = DBI->connect(dsn_for($dbname), |
|---|
| 153 | $ENV{TS_DB_USER}, $ENV{TS_DB_PASS}, { RaiseError => 1, PrintError => 0 }) |
|---|
| 154 | or die "Couldn't connect: $!\n"; |
|---|
| 155 | my @sql = load_sql($schema); |
|---|
| 156 | for my $sql (@sql) { |
|---|
| 157 | $sql =~ s!^\s*create\s+table\s+(\w+)!CREATE TABLE ${pfx}$1!mi; |
|---|
| 158 | $sql =~ s!^\s*(create.*?index)\s+(\w+)\s+on\s+(\w+)!$1 $2 ON ${pfx}$3!i; |
|---|
| 159 | $sql .= " ENGINE=INNODB\n" if $ENV{USE_MYSQL}; |
|---|
| 160 | $dbh->do($sql); |
|---|
| 161 | } |
|---|
| 162 | $dbh->disconnect; |
|---|
| 163 | } |
|---|
| 164 | } |
|---|
| 165 | |
|---|
| 166 | sub mysql_dbh { |
|---|
| 167 | return DBI->connect("DBI:mysql:mysql", "root", "", { RaiseError => 1 }) |
|---|
| 168 | or die "Couldn't connect to database"; |
|---|
| 169 | } |
|---|
| 170 | |
|---|
| 171 | my $pg_dbh; |
|---|
| 172 | |
|---|
| 173 | sub pgsql_dbh { |
|---|
| 174 | return $pg_dbh if $pg_dbh; |
|---|
| 175 | $pg_dbh ||= |
|---|
| 176 | DBI->connect("DBI:Pg:dbname=postgres", "postgres", "", { RaiseError => 1 }) |
|---|
| 177 | or die "Couldn't connect to database"; |
|---|
| 178 | } |
|---|
| 179 | |
|---|
| 180 | sub create_mysql_db { |
|---|
| 181 | my $dbname = shift; |
|---|
| 182 | mysql_dbh()->do("CREATE DATABASE $dbname"); |
|---|
| 183 | } |
|---|
| 184 | |
|---|
| 185 | sub drop_mysql_db { |
|---|
| 186 | my $dbname = shift; |
|---|
| 187 | mysql_dbh()->do("DROP DATABASE IF EXISTS $dbname"); |
|---|
| 188 | } |
|---|
| 189 | |
|---|
| 190 | sub create_pgsql_db { |
|---|
| 191 | my $dbname = shift; |
|---|
| 192 | pgsql_dbh()->do("CREATE DATABASE $dbname"); |
|---|
| 193 | } |
|---|
| 194 | |
|---|
| 195 | sub drop_pgsql_db { |
|---|
| 196 | my $dbname = shift; |
|---|
| 197 | undef $pg_dbh; |
|---|
| 198 | eval { pgsql_dbh()->do("DROP DATABASE IF EXISTS $dbname") }; |
|---|
| 199 | } |
|---|
| 200 | |
|---|
| 201 | sub teardown_dbs { |
|---|
| 202 | my(@dbs) = @_; |
|---|
| 203 | for my $db (@dbs) { |
|---|
| 204 | if ($ENV{USE_MYSQL}) { |
|---|
| 205 | drop_mysql_db(mysql_dbname($db)); |
|---|
| 206 | } elsif ($ENV{USE_PGSQL}) { |
|---|
| 207 | drop_pgsql_db(mysql_dbname($db)); |
|---|
| 208 | } else { |
|---|
| 209 | my $file = db_filename($db); |
|---|
| 210 | next unless -e $file; |
|---|
| 211 | unlink $file or die "Can't teardown $db: $!"; |
|---|
| 212 | } |
|---|
| 213 | } |
|---|
| 214 | } |
|---|
| 215 | |
|---|
| 216 | sub load_sql { |
|---|
| 217 | my($file) = @_; |
|---|
| 218 | open my $fh, $file or die "Can't open $file: $!"; |
|---|
| 219 | my $sql = do { local $/; <$fh> }; |
|---|
| 220 | close $fh; |
|---|
| 221 | split /;\s*/, $sql; |
|---|
| 222 | } |
|---|
| 223 | |
|---|
| 224 | 1; |
|---|