diff options
author | Greg Sabino Mullane | 2008-04-16 23:11:59 +0000 |
---|---|---|
committer | Greg Sabino Mullane | 2008-04-16 23:11:59 +0000 |
commit | 1c48e7bcc0bcd90cfce79c71a3ab840b2bfc3229 (patch) | |
tree | 094ee9f81d89bd19e8a79fcec7c4f3a0ddf1b1d2 | |
parent | 7efab2903035858f7c042623fdabacd2c72663a5 (diff) |
First outline of a test suite.
-rw-r--r-- | t/00basic.t | 19 | ||||
-rw-r--r-- | t/01connect.t | 97 | ||||
-rw-r--r-- | t/99_perlcritic.t | 154 | ||||
-rw-r--r-- | t/99_pod.t | 44 | ||||
-rw-r--r-- | t/99cleanup.t | 23 | ||||
-rw-r--r-- | t/check_postgres_setup.pl | 184 |
6 files changed, 521 insertions, 0 deletions
diff --git a/t/00basic.t b/t/00basic.t new file mode 100644 index 000000000..45d6aaa03 --- /dev/null +++ b/t/00basic.t @@ -0,0 +1,19 @@ +#!perl + +## Simply test that the script compiles and gives a good version + +use strict; +use warnings; +use Test::More tests => 2; +select(($|=1,select(STDERR),$|=1)[1]); + +eval { + require 'check_postgres.pl'; +}; +like($@, qr{\-\-help}, 'check_postgres.pl compiles'); + +$@ =~ /help/ or BAIL_OUT "Script did not compile, cancelling rest of tests.\n"; + +like( $check_postgres::VERSION, qr/^v?\d+\.\d+\.\d+(?:_\d+)?$/, + qq{Found check_postgres version as "$check_postgres::VERSION"}); + diff --git a/t/01connect.t b/t/01connect.t new file mode 100644 index 000000000..bf803e34b --- /dev/null +++ b/t/01connect.t @@ -0,0 +1,97 @@ +#!perl + +## Make sure we can connect and disconnect cleanly +## All tests are stopped if we cannot make the first connect + +use strict; +use warnings; +use DBI; +use DBD::Pg; +use Test::More; +use lib 't','.'; +require 'check_postgres_setup.pl'; +select(($|=1,select(STDERR),$|=1)[1]); + +## Define this here in case we get to the END block before a connection is made. +BEGIN { + use vars qw/$pgversion $pglibversion $pgvstring $pgdefport $helpconnect $dbh $connerror %set/; + ($pgversion,$pglibversion,$pgvstring,$pgdefport) = ('?','?','?','?'); +} + +($helpconnect,$connerror,$dbh) = connect_database(); + +if (! defined $dbh) { + plan skip_all => 'Connection to database failed, cannot continue testing'; +} +plan tests => 1; + +# Trapping a connection error can be tricky, but we only have to do it +# this thoroughly one time. We are trapping two classes of errors: +# the first is when we truly do not connect, usually a bad DBI_DSN; +# the second is an invalid login, usually a bad DBI_USER or DBI_PASS + +my ($t); + +pass('Established a connection to the database'); + +$pgversion = $dbh->{pg_server_version}; +$pglibversion = $dbh->{pg_lib_version}; +$pgdefport = $dbh->{pg_default_port}; +$pgvstring = $dbh->selectall_arrayref('SELECT VERSION()')->[0][0]; + +END { + my $pv = sprintf('%vd', $^V); + my $schema = 'check_postgres_schema'; + my $dsn = exists $ENV{DBI_DSN} ? $ENV{DBI_DSN} : '?'; + my $ver = defined $DBD::Pg::VERSION ? $DBD::Pg::VERSION : '?'; + my $user = exists $ENV{DBI_USER} ? $ENV{DBI_USER} : '<not set>'; + + my $extra = ''; + for (sort qw/HOST HOSTADDR PORT DATABASE USER PASSWORD PASSFILE OPTIONS REALM + REQUIRESSL KRBSRVNAME CONNECT_TIMEOUT SERVICE SSLMODE SYSCONFDIR + CLIENTENCODING/) { + my $name = "PG$_"; + if (exists $ENV{$name} and defined $ENV{$name}) { + $extra .= sprintf "\n%-21s $ENV{$name}", $name; + } + } + for my $name (qw/DBI_DRIVER DBI_AUTOPROXY/) { + if (exists $ENV{$name} and defined $ENV{$name}) { + $extra .= sprintf "\n%-21s $ENV{$name}", $name; + } + } + + ## More helpful stuff + for (sort keys %set) { + $extra .= sprintf "\n%-21s %s", $_, $set{$_}; + } + + if ($helpconnect) { + $extra .= "\nAdjusted: "; + if ($helpconnect & 1) { + $extra .= 'DBI_DSN '; + } + if ($helpconnect & 4) { + $extra .= 'DBI_USER'; + } + } + + if (defined $connerror) { + $connerror =~ s/.+?failed: //; + $connerror =~ s{\n at t/check_postgres.*}{}m; + $extra .= "\nError was: $connerror"; + } + + diag + "\nDBI Version $DBI::VERSION\n". + "DBD::Pg Version $ver\n". + "Perl Version $pv\n". + "OS $^O\n". + "PostgreSQL (compiled) $pglibversion\n". + "PostgreSQL (target) $pgversion\n". + "PostgreSQL (reported) $pgvstring\n". + "Default port $pgdefport\n". + "DBI_DSN $dsn\n". + "DBI_USER $user\n". + "Test schema $schema$extra\n"; +} diff --git a/t/99_perlcritic.t b/t/99_perlcritic.t new file mode 100644 index 000000000..65efab0fb --- /dev/null +++ b/t/99_perlcritic.t @@ -0,0 +1,154 @@ +#!perl + +## Run Perl::Critic against the source code and the tests +## This is highly customized, so take with a grain of salt +## Requires TEST_CRITIC to be set + +use strict; +use warnings; +use Test::More; +use Data::Dumper; +select(($|=1,select(STDERR),$|=1)[1]); + +my @testfiles; + +if (!$ENV{TEST_CRITIC}) { + plan skip_all => 'Set the environment variable TEST_CRITIC to enable this test'; +} +elsif (!eval { require Perl::Critic; 1 }) { + plan skip_all => 'Could not find Perl::Critic'; +} +elsif ($Perl::Critic::VERSION < 0.23) { + plan skip_all => 'Perl::Critic must be version 0.23 or higher'; +} +else { + opendir my $dir, 't' or die qq{Could not open directory 't': $!\n}; + @testfiles = map { "t/$_" } grep { /^.+\.(t|pl)$/ } readdir $dir; + closedir $dir; + plan tests => 1+@testfiles; +} +ok(@testfiles, 'Found files in test directory'); + +## Check some non-test files +my $critic = Perl::Critic->new(-severity => 1); + +for my $filename (qw{check_postgres.pl }) { + + if ($ENV{TEST_CRITIC_SKIPNONTEST}) { + pass qq{Skipping non-test file "$filename"}; + next; + } + + -e $filename or die qq{Could not find "$filename"!}; + open my $oldstderr, '>&', \*STDERR or die 'Could not dupe STDERR'; + close STDERR or die qq{Could not close STDERR: $!}; + my @vio = $critic->critique($filename); + open STDERR, '>&', $oldstderr or die 'Could not recreate STDERR'; ## no critic + close $oldstderr or die qq{Could not close STDERR copy: $!}; + my $vios = 0; + VIO: for my $v (@vio) { + my $d = $v->description(); + (my $policy = $v->policy()) =~ s/Perl::Critic::Policy:://; + my $source = $v->source(); + + next if $policy =~ /ProhibitInterpolationOfLiterals/; ## For now + + ## Allow skipping of items: + ## next if $d =~ /Subroutine "looks_like_number" not exported/; + ## next if $policy =~ /ProhibitCallsToUndeclaredSubs/; + ##next if $policy =~ /ProhibitHardTabs/ and ($source =~ /sql = qq/i or $source =~ /qw[\(\/]/); + + $vios++; + my $f = $v->filename(); + my $l = $v->location(); + my $line = $l->[0]; + diag "\nFile: $f (line $line)\n"; + diag "Vio: $d\n"; + diag "Policy: $policy\n"; + diag "Source: $source\n\n"; + } + if ($vios) { + fail qq{ Failed Perl::Critic tests for file "$filename": $vios}; + } + else { + pass qq{ File "$filename" passed all Perl::Critic tests}; + } + +} + +## Specific exclusions for test scripts: +my %ok = + (yaml => { + sub => 'meta_spec_ok', + }, + pod => { + sub => 'pod_file_ok pod_coverage_ok', + }, + signature => { + sub => 'verify SIGNATURE_OK', + }, +); +for my $f (keys %ok) { + for my $ex (keys %{$ok{$f}}) { + if ($ex eq 'sub') { + for my $foo (split /\s+/ => $ok{$f}{sub}) { + push @{$ok{$f}{OK}} => qr{Subroutine "$foo" (?:is neither|not exported)}; + } + } + else { + die "Unknown exception '$ex'\n"; + } + } +} + +## Allow Test::More subroutines +my $tm = join '|' => (qw/skip plan pass fail is ok diag BAIL_OUT/); +my $testmoreok = qr{Subroutine "$tm" is neither}; + +## Create a new critic for the tests +$critic = Perl::Critic->new(-severity => 1); + +my $count = 1; +for my $filename (sort @testfiles) { + -e $filename or die qq{Could not find "$filename"!}; + my @vio = $critic->critique($filename); + my $vios = 0; + VIO: for my $v (@vio) { + my $d = $v->description(); + (my $policy = $v->policy()) =~ s/Perl::Critic::Policy:://; + my $source = $v->source(); + my $f = $v->filename(); + + ## Skip common Test::More subroutines: + next if $d =~ $testmoreok; + + ## Skip other specific items: + for my $k (sort keys %ok) { + next unless $f =~ /$k/; + for (@{$ok{$k}{OK}}) { + next VIO if $d =~ $_; + } + } + + ## Skip included file package warning + next if $policy =~ /RequireExplicitPackage/ and $filename =~ /setup/; + + $vios++; + my $l = $v->location(); + my $line = $l->[0]; + diag "\nFile: $f (line $line)\n"; + diag "Vio: $d\n"; + diag "Policy: $policy\n"; + diag "Source: $source\n\n"; + } + my $SPACE = ++$count < 9 ? ' ' : ''; + if ($vios) { + fail qq{${SPACE}Failed Perl::Critic tests for file "$filename": $vios}; + } + else { + pass qq{${SPACE}File "$filename" passed all Perl::Critic tests}; + } +} + +pass('Finished Perl::Critic testing'); + diff --git a/t/99_pod.t b/t/99_pod.t new file mode 100644 index 000000000..47b6fb90c --- /dev/null +++ b/t/99_pod.t @@ -0,0 +1,44 @@ +#!perl + +## Check our Pod, requires Test::Pod + +use strict; +use warnings; +use Test::More; +select(($|=1,select(STDERR),$|=1)[1]); + +plan tests => 2; + +my $PODVERSION = '0.95'; +eval { + require Test::Pod; + Test::Pod->import; +}; + +SKIP: { + if ($@ or $Test::Pod::VERSION < $PODVERSION) { + skip "Test::Pod $PODVERSION is required", 1; + } + pod_file_ok('check_postgres.pl'); +} + +## We won't require everyone to have this, so silently move on if not found +my $PODCOVERVERSION = '1.04'; +eval { + require Test::Pod::Coverage; + Test::Pod::Coverage->import; +}; +SKIP: { + + if ($@ or $Test::Pod::Coverage::VERSION < $PODCOVERVERSION) { + skip "Test::Pod::Coverage $PODCOVERVERSION is required", 1; + } + + my $trusted_names = + [ + qr{^CLONE$}, + qr{^driver$}, + qr{^constant$}, + ]; + pod_coverage_ok('check_postgres', {trustme => $trusted_names}, 'check_postgres.pl pod coverage okay'); +} diff --git a/t/99cleanup.t b/t/99cleanup.t new file mode 100644 index 000000000..f920c66c4 --- /dev/null +++ b/t/99cleanup.t @@ -0,0 +1,23 @@ +#!perl + +## Cleanup all database objects we may have created + +use strict; +use warnings; +use Test::More; +use lib 't','.'; +require 'check_postgres_setup.pl'; +select(($|=1,select(STDERR),$|=1)[1]); + +my $dbh = connect_database({nosetup => 1}); + +if (! defined $dbh) { + plan skip_all => 'Connection to database failed, cannot continue testing'; +} +plan tests => 1; + +isnt( $dbh, undef, 'Connect to database for cleanup'); + +cleanup_database($dbh); +$dbh->disconnect() if defined $dbh and ref $dbh; + diff --git a/t/check_postgres_setup.pl b/t/check_postgres_setup.pl new file mode 100644 index 000000000..2f61eef3b --- /dev/null +++ b/t/check_postgres_setup.pl @@ -0,0 +1,184 @@ + +## Helper file for the check_postgres.pl tests + +use strict; +use warnings; +use Data::Dumper; +use DBI; +select(($|=1,select(STDERR),$|=1)[1]); ## no critic + +my @schemas = + ( + 'check_postgres_testschema', + 'check_postgres_testschema2', + ); + +my @tables = + ( + 'check_postgres_test', + 'check_postgres_test2', + 'check_postgres_test3', + ); + +my @sequences = + ( + 'check_postgres_testsequence', + ); + +my $S = 'check_postgres_testschema'; + +sub connect_database { + + ## Connect to the database (unless 'dbh' is passed in) + ## Setup all the tables (unless 'nosetup' is passed in) + ## Returns three values: + ## 1. helpconnect for use by ?? + ## 2. Any error generated + ## 3. The database handle, or undef + ## The returned handle has AutoCommit=0 (unless AutoCommit is passed in) + + my $arg = shift || {}; + ref $arg and ref $arg eq 'HASH' or die qq{Need a hashref!\n}; + + my $dbh = $arg->{dbh} || ''; + + my $helpconnect = 0; + if (!defined $ENV{DBI_DSN}) { + $helpconnect = 1; + $ENV{DBI_DSN} = 'dbi:Pg:'; + } + + if (!$dbh) { + eval { + $dbh = DBI->connect($ENV{DBI_DSN}, $ENV{DBI_USER}, $ENV{DBI_PASS}, + {RaiseError => 1, PrintError => 0, AutoCommit => 1}); + }; + if ($@) { + return $helpconnect, $@, undef if $@ !~ /FATAL/ or defined $ENV{DBI_USER}; + ## Try one more time as postgres user (and possibly database) + if ($helpconnect) { + $ENV{DBI_DSN} .= 'dbname=postgres'; + $helpconnect += 2; + } + $helpconnect += 4; + $ENV{DBI_USER} = $^O =~ + /openbsd/ ? '_postgresql' + : $^O =~ /bsd/i ? 'pgsql' + : 'postgres'; + eval { + $dbh = DBI->connect($ENV{DBI_DSN}, $ENV{DBI_USER}, $ENV{DBI_PASS}, + {RaiseError => 1, PrintError => 0, AutoCommit => 1}); + }; + if ($@) { + ## Try one final time for Beastie + if ($ENV{DBI_USER} ne 'postgres') { + $helpconnect += 8; + $ENV{DBI_USER} = 'postgres'; + eval { + $dbh = DBI->connect($ENV{DBI_DSN}, $ENV{DBI_USER}, $ENV{DBI_PASS}, + {RaiseError => 1, PrintError => 0, AutoCommit => 1}); + }; + } + return $helpconnect, $@, undef if $@; + } + } + } + if ($arg->{nosetup}) { + return $helpconnect, undef, $dbh unless schema_exists($dbh, $S); + $dbh->do("SET search_path TO $S"); + } + else { + cleanup_database($dbh); + + eval { + $dbh->do("CREATE SCHEMA $S"); + }; + $@ and return $helpconnect, $@, undef; + $dbh->do("SET search_path TO $S"); + $dbh->do('CREATE SEQUENCE check_postgres_testsequence'); + # If you add columns to this, please do not use reserved words! + my $SQL = q{ +CREATE TABLE check_postgres_test ( + id integer not null primary key, + val text +) +}; + + $dbh->{Warn} = 0; + $dbh->do($SQL); + $dbh->{Warn} = 1; + +} ## end setup + +$dbh->commit() unless $dbh->{AutoCommit}; + +if ($arg->{disconnect}) { + $dbh->disconnect(); + return $helpconnect, undef, undef; +} + +$dbh->{AutoCommit} = 0 unless $arg->{AutoCommit}; +return $helpconnect, undef, $dbh; + +} ## end of connect_database + + +sub schema_exists { + + my ($dbh,$schema) = @_; + my $SQL = 'SELECT 1 FROM pg_catalog.pg_namespace WHERE nspname = ?'; + my $sth = $dbh->prepare_cached($SQL); + my $count = $sth->execute($schema); + $sth->finish(); + return $count < 1 ? 0 : 1; + +} + + +sub relation_exists { + + my ($dbh,$schema,$name) = @_; + my $SQL = 'SELECT 1 FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n '. + 'WHERE n.oid=c.relnamespace AND n.nspname = ? AND c.relname = ?'; + my $sth = $dbh->prepare_cached($SQL); + my $count = $sth->execute($schema,$name); + $sth->finish(); + return $count < 1 ? 0 : 1; + +} + + +sub cleanup_database { + + my $dbh = shift; + my $type = shift || 0; + + return unless defined $dbh and ref $dbh and $dbh->ping(); + + ## For now, we always run and disregard the type + + $dbh->rollback() if ! $dbh->{AutoCommit}; + + for my $name (@tables) { + my $schema = ($name =~ s/(.+)\.(.+)/$2/) ? $1 : $S; + next if ! relation_exists($dbh,$schema,$name); + $dbh->do("DROP TABLE $schema.$name"); + } + + for my $name (@sequences) { + my $schema = ($name =~ s/(.+)\.(.+)/$2/) ? $1 : $S; + next if ! relation_exists($dbh,$schema,$name); + $dbh->do("DROP SEQUENCE $schema.$name"); + } + + for my $schema (@schemas) { + next if ! schema_exists($dbh,$schema); + $dbh->do("DROP SCHEMA $schema CASCADE"); + } + $dbh->commit() if ! $dbh->{AutoCommit}; + + return; + +} + +1; |